Паттерн позволяет «перещелкнуть прерыватель», если в течение определенного периода времени произошло заданное число ошибок, и таким образом обойти медленный сервис, пока проблема не разрешится, и продолжить отвечать на запросы пользователей с максимальной скоростью.
```
import asyncio
from datetime import datetime, timedelta
class CircuitOpenException(Exception):
pass
class CircuitBreaker:
def __init__(self, callback, timeout: float, time_window: float,
max_failures: int, reset_interval: float):
self.callback = callback
self.timeout = timeout
self.time_window = time_window
self.max_failures = max_failures
self.reset_interval = reset_interval
self.last_request_time = None
self.last_failure_time = None
self.current_failures = 0
async def request(self, *args, **kwargs):
if self.current_failures >= self.max_failures:
if datetime.now() > self.last_request_time + timedelta(
seconds=self.reset_interval):
self._reset('Цепь переходит из разомкнутого состояния '
'в замкнутое, сброс!')
return await self._do_request(*args, **kwargs)
else:
print('Цепь разомкнута, быстрый отказ!')
raise CircuitOpenException()
else:
if self.last_failure_time and datetime.now() > self.last_failure_time + timedelta(
seconds=self.time_window):
self._reset('Interval since first failure elapsed, resetting!')
print('Цепь замкнута, отправляю запрос!')
return await self._do_request(*args, **kwargs)
async def _do_request(self, *args, **kwargs):
try:
print('Отправляется запрос!')
self.last_request_time = datetime.now()
return await asyncio.wait_for(self.callback(*args, **kwargs),
timeout=self.timeout)
except Exception as e:
self.current_failures = self.current_failures + 1
if self.last_failure_time is None:
self.last_failure_time = datetime.now()
raise
def _reset(self, msg: str):
print(msg)
self.last_failure_time = None
self.current_failures = 0
```
Конструктор нашего класса прерывателя принимает пять параметров. Первые два – обратный вызов, который должен выполнять прерыватель, и величина тайм-аута, по истечении которого обратный вызов признается неудачным. Следующие три параметра связаны с обработкой ошибок и сбросом в исходное состояние. Параметр max_ failure – максимальное число ошибок, допустимое в течение промежутка времени time_window, прежде чем прерыватель разомкнется. Параметр reset_interval определяет, сколько секунд ждать перед сбросом прерывателя из разомкнутого состояния в замкнутое, после того как произошло max_failure ошибок.
Далее определен метод-сопрограмма request, который выполняет обратный
вызов, следит за количеством возникших ошибок и возвращает полученный результат, если ошибок не было. При возникновении ошибки мы увеличиваем счетчик failure_count. Если в течение заданного промежутка времени произошло больше max_failure ошибок, то последующие обращения к request будут возбуждать исключение CircuitOpenException. По прошествии времени reset_interval счетчик failure_count сбрасывается в нуль и подсчет ошибочных запросов начинается заново (если прерыватель замкнут, что, вообще говоря, необязательно).