Active-Active Redis и географическое переключение на стороне клиента
Active-Active Redis распределяет данные по нескольким регионам, позволяя каждому региональному экземпляру базы данных обслуживать как чтение, так и запись. Какая магия позволяет это делать? Redis использует бесконфликтные реплицируемые типы данных (CRDT) для разрешения одновременных обновлений и обеспечения того, чтобы экземпляры в конечном итоге приходили к согласованному состоянию.
Стандартный клиент redis-py подключается к одной настроенной конечной точке. Чтобы иметь возможность быстро переключаться между экземплярами в случае сбоя, приложение могло бы создавать отдельные клиенты для нескольких региональных экземпляров баз данных, но тогда ему пришлось бы реализовывать (и поддерживать!) мониторинг состояния, выбор конечной точки, переключение при сбое и возврат к исходному состоянию. Наличие бесчисленного множества приложений по всему миру, реализующих одну и ту же логику, причем некоторые из них делают это успешнее других, не имеет особого инженерного смысла, поэтому мы решили создать единую «каноническую» реализацию, которая предоставляет клиентский API для управления этими задачами, тем самым обеспечивая географическое переключение на стороне клиента.
Этот API предоставляется через MultiDBClient — обертку над обычными экземплярами одиночного клиента и клиента кластера. MultiDBClient направляет трафик на одну выбранную конечную точку (активную базу данных), одновременно отслеживая состояние всех настроенных конечных точек. Если активная база данных считается неработоспособной, клиент выбирает другую работоспособную конечную точку в соответствии с настроенными весами и перенаправляет на нее трафик. Когда включен автоматический возврат к исходному состоянию (failback), клиент периодически оценивает недоступные конечные точки и может вернуться к наиболее приоритетной работоспособной конечной точке. MultiDBClient не транслирует команды и не реплицирует данные между конечными точками. Репликация данных остается обязанностью уровня Active-Active базы данных, позволяя CRDT проявить себя в полной мере.
Внутри MultiDBClient
Каждый DatabaseConfig определяет одну конечную точку и ее вес. Создайте такой объект для каждого регионального экземпляра базы данных. MultiDbConfig собирает эти конфигурации конечных точек и определяет поведение, применимое ко всей настройке MultiDBClient, включая проверки работоспособности, повторные попытки, переключение при сбое и возврат к исходному состоянию. Как мы увидим позже, существует множество настроек для различных сценариев и конфигураций.
MultiDBClient делегирует взаимодействие с конечными точками и управление соединениями базовому клиенту Redis или RedisCluster. Вы можете выбрать один из них глобально с помощью MultiDbConfig.client_class: используйте Redis для стандартных конечных точек и RedisCluster для конечных точек, использующих OSS Cluster API. Передавайте специфичные для клиента параметры через DatabaseConfig.client_kwargs.
От обнаружения сбоя до восстановления
MultiDBClient использует шаблон «предохранитель» (circuit breaker) для управления трафиком к каждой конечной точке. Когда активная конечная точка считается неработоспособной, ее цепь размыкается, и клиент переключается на наиболее приоритетную работоспособную конечную точку. Концепция заимствована из электрических цепей, где предохранители защищают цепь от повреждений из-за превышения нагрузки, которую оборудование может выдержать.
Предохранитель — это лишь один из двух взаимодополняющих механизмов обнаружения сбоев. Проактивная фоновая проверка работоспособности периодически оценивает каждую конечную точку с использованием настроенной политики проверки. Реактивный FailureDetector, описанный выше, отслеживает успешные и неудачные выполнения команд в скользящем окне и размыкает цепь при достижении настроенных порогов сбоев. Вместе они позволяют клиенту быстрее обнаруживать сбои и реагировать на них.
Когда цепь активной конечной точки размыкается, стратегия переключения выбирает наиболее приоритетную работоспособную конечную точку и направляет на нее последующие команды. Стоит отметить, что команды, которые уже находятся в процессе выполнения для предыдущей конечной точки, не могут быть перенаправлены. Если запись в процессе выполнения там завершится успешно, ее результат может быть не сразу виден через новую конечную точку, пока репликация Active-Active не синхронизируется.
Сбои команд, подлежащие обработке, обрабатываются глобальной политикой повторных попыток. Перед каждой повторной попыткой MultiDBClient проверяет текущую активную конечную точку, позволяя повторить команду для вновь выбранной базы данных.
Автоматический возврат к исходному состоянию периодически проверяет, восстановилась ли более приоритетная конечная точка. Как только конечная точка становится работоспособной, MultiDBClient может переключить трафик обратно на нее. Веса можно настроить так, чтобы отдавать приоритет конечной точке, ближайшей к приложению.
Эта система позволит вам всегда отдавать предпочтение конечным точкам, ближайшим к приложению, оставаясь при этом готовыми к сбоям.
Для большего контроля вы можете отключить автоматический возврат к исходному состоянию, установив auto_fallback_interval в -1, и динамически выбирать работоспособную конечную точку явно с помощью set_active_database().
Настройка высокодоступного клиента Python
Политики проверки работоспособности
Проверка работоспособности может выполнять несколько зондов перед принятием решения о том, работоспособна ли конечная точка. Политика определяет, как объединяются их результаты, балансируя между быстрым переключением при сбое и устойчивостью к кратковременным сбоям:
- HEALTHY_ALL — строгая: конечная точка считается работоспособной только тогда, когда каждый зонд успешен. Используйте ее, когда продолжение отправки трафика на нестабильную конечную точку более рискованно, чем случайное ненужное переключение. Ее недостаток — низкая устойчивость к кратковременным сетевым ошибкам и более длительное время оценки.
- HEALTHY_ANY — разрешительная: конечная точка остается работоспособной, если успешен хотя бы один зонд, а зондирование прекращается после первого успеха. Используйте ее, когда ожидаются кратковременные сбои соединения или переключение при сбое обходится дорого. Это минимизирует ложноположительные переключения, но может оставить трафик на деградировавшей конечной точке и требует выполнения всех настроенных зондов для подтверждения полного сбоя.
- HEALTHY_MAJORITY — сбалансированная: более половины зондов должны быть успешными. Она терпима к случайным сбоям, но при этом реагирует на постоянные проблемы. Это лучшая отправная точка для большинства производственных развертываний.
Короче говоря: выбирайте HEALTHY_ALL, чтобы отдать приоритет качеству конечной точки, HEALTHY_ANY — для обеспечения стабильности и предотвращения ненужных переключений, или HEALTHY_MAJORITY, когда вам нужен баланс между ними.
Например, следующая конфигурация выполняет пять зондов и считает конечную точку работоспособной, если успешны как минимум три из них:
Как настройки влияют на производительность
Интервал проверки работоспособности: частые проверки улучшают обнаружение сбоев при отсутствии трафика приложения, но каждый экземпляр приложения запускает их, что иногда может быть слишком расточительно.
Для систем с высоким трафиком позвольте реактивному детектору лидировать, а проверки работоспособности используйте как более медленную страховку. Для систем с низким трафиком используйте более частые проверки работоспособности, так как реактивный детектор может не собрать достаточно команд.
Окно обнаружения сбоев: короткое окно позволяет быстро реагировать и быстрее забывать старые сбои, что подходит для высокого трафика. Приложениям с низким трафиком нужно более длинное окно; в противном случае они могут никогда не собрать достаточно выборок для достижения min_num_failures.
Попытки переключения: failover_attempts × failover_delay определяет примерное окно восстановления, когда каждая конечная точка временно недоступна. API, чувствительный к задержкам, должен поддерживать это окно коротким и возвращать управление приложению. Фоновый рабочий процесс может дольше ждать восстановления конечной точки.
Сценарий с высокой пропускной способностью
Этот пресет ориентирован на пропускную способность и быстрое обнаружение сбоев:
- Короткие тайм-ауты предотвращают слишком долгое занятие соединений запросами.
- Одна попытка повтора ограничивает усиление трафика во время сбоя.
- Случайный разброс (джиттер) предотвращает одновременные повторные попытки всех экземпляров приложения.
- Короткое окно обнаружения сбоев быстро реагирует на концентрированные сбои.
- Более длительный интервал проверки работоспособности ограничивает фоновый трафик.
Наблюдаемость и интеграция с приложениями
MultiDBClient предоставляет уведомления во время выполнения всякий раз, когда при переключении или возврате на исходный ресурс изменяется активная база данных. Приложения могут использовать пользовательские прослушиватели событий для записи переходов, обновления метрик, запуска оповещений или синхронизации внешнего состояния. Эти прослушиватели регистрируются через EventDispatcher, передаваемый в MultiDbConfig.
Когда включена наблюдаемость redis-py, MultiDBClient записывает счетчик redis.client.geofailover.failovers. Он включает следующие атрибуты:
- db.client.geofailover.fail_from
- db.client.geofailover.fail_to
- db.client.geofailover.reason, например automatic или manual
Демонстрация регионального переключения при сбое
Чтобы увидеть, как MultiDBClient обрабатывает региональное переключение и возврат на исходный ресурс от начала до конца, ознакомьтесь с примером географического переключения при сбое для redis-py.
Заключение
Active-Active Redis берет на себя синхронизацию ваших данных между регионами, в то время как MultiDBClient помогает вашему Python-приложению оставаться на связи. Он объединяет мониторинг работоспособности, взвешенный выбор конечных точек, переключение и возврат при сбоях, повторные попытки и наблюдаемость за привычным API клиента Redis.
Не существует универсальной конфигурации для высокой доступности. Значения по умолчанию предлагают сбалансированную отправную точку, однако вам все равно следует настроить их под свою рабочую нагрузку, ожидаемую скорость переключения при сбое и допустимый уровень ложноположительных срабатываний.
Таким образом, при отключении региона у вашего Python-приложения будет четкий путь для продолжения работы.









