Паттерн позволяет «перещелкнуть прерыватель», если в течение определенного периода времени произошло заданное число ошибок, и таким образом обойти медленный сервис, пока проблема не разрешится, и продолжить отвечать на запросы пользователей с максимальной скоростью. ``` 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 сбрасывается в нуль и подсчет ошибочных запросов начинается заново (если прерыватель замкнут, что, вообще говоря, необязательно).