Введение
Представьте себе типичную утреннюю рутину: миллионы пользователей открывают приложения, отправляют сообщения и ждут, что их запросы обрабатываются мгновенно. Все это требует стабильных, надежных систем обмена сообщениями. Когда речь идет о масштабируемых приложениях с высокой нагрузкой, разработчики часто обращаются к паттернам Pub/Sub (публикации и подписки). Одной из популярных технологий для этого является Redis — система in-memory, которая может стать ядром таких систем. Но как сделать эту систему высокодоступной (HA), чтобы она оставалась надежной даже при сбоях узлов?
Сегодня мы рассмотрим, как создать высокодоступный Redis Pub/Sub кластер, который выдерживает нагрузки и гарантирует, что сообщения не теряются даже при отказе одного из серверов.
Что такое Pub/Sub и почему это важно?
Pub/Sub (Publisher-Subscriber) — это архитектурный паттерн, который упрощает взаимодействие между компонентами сложных распределённых систем, разделяя отправителей (издателей) и получателей (подписчиков) сообщений. В отличие от прямого обмена данными между компонентами, модель Pub/Sub создает слой абстракции, где один компонент публикует сообщение, а другие компоненты подписываются на его получение. При этом издатели и подписчики могут существовать независимо друг от друга, не зная о точном местоположении и количестве участников.
Как работает Pub/Sub?
В системе Pub/Sub издатель отправляет сообщения в канал (или тему), а подписчики, которые подписаны на этот канал, получают опубликованные сообщения. Канал выступает посредником между издателем и подписчиками. Это позволяет компонентам приложения обмениваться информацией асинхронно, не блокируя выполнение своих операций.
Когда издатель отправляет сообщение в канал, он не заботится о том, кто и когда это сообщение получит. Подписчики, в свою очередь, получают только те сообщения, которые опубликованы в каналы, на которые они подписаны. Важной особенностью Pub/Sub является то, что подписчики могут не знать, сколько издателей существует, а издатели могут не знать, сколько подписчиков получают их сообщения.
Пример использования Pub/Sub в реальных приложениях
Рассмотрим сценарий в системе доставки еды, где Pub/Sub может быть использован для отправки уведомлений о новых заказах. В такой системе процесс может выглядеть следующим образом:
Клиент делает заказ через приложение. Как только заказ оформлен, система публикует сообщение в канал
orders
, указывая детали заказа (что заказано, когда и куда доставить).Кухня подписана на канал
orders
, чтобы оперативно получать новые заказы. Как только сообщение поступает в канал, его сразу обрабатывает сотрудник кухни или автоматизированная система, которая готовит блюдо.Система доставки может быть подписана на другой канал
deliveries
, где публикуются сообщения, когда заказ готов и нужно организовать его доставку. Каждый подписчик (например, водитель или доставка дронов) получает информацию и действует в зависимости от своей зоны ответственности.
Этот подход делает взаимодействие между компонентами гибким, масштабируемым и асинхронным. Компоненты могут работать параллельно, не блокируя друг друга. Это особенно важно в системах, где требуется высокая производительность и способность обрабатывать большое количество запросов в реальном времени.
Почему Pub/Sub важен в масштабируемых системах?
Асинхронная коммуникация — одно из ключевых преимуществ Pub/Sub. В отличие от традиционных методов, где один компонент напрямую отправляет данные другому (что может вызвать задержки и блокировки), в модели Pub/Sub взаимодействие происходит независимо от времени обработки. Это позволяет системе лучше масштабироваться, так как компоненты не блокируют друг друга, и взаимодействие между ними становится более гибким. Например, в системе оповещений, если один из подписчиков недоступен, остальные продолжают получать сообщения, что позволяет избежать полной остановки системы.
Модель Pub/Sub также идеальна для сценариев, где отправителю сообщений не нужно знать о наличии или количестве получателей. Например, в системе оповещений или событийного мониторинга, где одно событие может быть интересно нескольким независимым подписчикам.
Ограничения и необходимость высокой доступности
Хотя Pub/Sub обеспечивает гибкость и масштабируемость, у него есть критические ограничения, если рассматривать его на основе одного сервера Redis. По умолчанию, Redis Pub/Sub не сохраняет сообщения. Это означает, что если подписчик в момент публикации сообщения не подключён, он пропустит это сообщение. Это может стать проблемой в критически важных системах, где сообщения должны гарантированно доставляться каждому подписчику.
Но более серьёзное ограничение связано с тем, что один сервер Redis не обеспечивает высокой доступности. Если сервер Redis выйдет из строя, все данные, которые передаются через Pub/Sub, будут потеряны, а приложение перестанет функционировать. Например, если сервер Redis, на котором опубликованы заказы на кухню, перестанет работать, вся информация о заказах будет потеряна, что может привести к нарушению бизнес-процессов.
Решение: использование Redis в высокодоступной конфигурации (HA)
Чтобы обеспечить надёжную работу системы Pub/Sub в критически важных приложениях, необходимо настроить Redis в режиме высокой доступности (HA). В высокодоступной архитектуре сообщения и данные должны быть реплицированы на несколько узлов, чтобы в случае сбоя одного из узлов система могла продолжить работу без потерь.
Для достижения высокой доступности используется кластеризация Redis (Redis Cluster) или Redis Sentinel:
Redis Cluster — это распределённая система с шардированием данных, где каждый мастер-узел хранит определённую часть данных и имеет одну или несколько реплик. В случае сбоя мастер-узла одна из его реплик становится новым мастером. Это помогает обеспечить отказоустойчивость, так как данные распределяются между несколькими узлами.
Redis Sentinel — это система управления фейловером, которая следит за состоянием узлов Redis и автоматически переключает роли между мастером и репликами в случае сбоя. Sentinel также уведомляет подключённые клиенты о том, что произошло переключение на новый мастер.
Использование Redis в режиме высокой доступности гарантирует, что даже если один сервер выйдет из строя, сообщения и данные, передаваемые через Pub/Sub, будут обработаны другими узлами, что важно для непрерывной работы систем.
Почему Redis?
Redis как Pub/Sub-брокер привлекателен своей легкостью в настройке и высокой производительностью. Он поддерживает практически моментальную отправку и доставку сообщений. Кроме того, его механизм репликации позволяет настроить высокодоступный кластер с минимальными накладными расходами.
Однако есть нюанс: стандартный Pub/Sub в Redis не сохраняет сообщения, если подписчик не подключен. Это может быть проблемой в высоконагруженных системах, где сообщения критически важны. Поэтому нам необходимо продумать стратегию для обработки отказов.
Создание HA кластера Redis Sentinel
Для обеспечения высокой доступности (HA) Redis, можно использовать механизм Redis Sentinel. Sentinel — это система управления фейловером и мониторинга, которая автоматически переключает роли между мастер-узлом и репликами, если мастер выходит из строя. Эта технология идеально подходит для обеспечения отказоустойчивости в системах, где важна непрерывная работа, и где требуется минимальное время простоя при сбоях.
Redis Sentinel обеспечивает высокую доступность путем управления ролью мастера и реплик, но не распределяет данные между узлами. Все ключи и данные хранятся на мастере и реплицируются на реплики. Поэтому для продюсеров и консьюмеров важно всегда подключаться именно к мастеру, так как только он может принимать записи и обрабатывать запросы на публикацию в каналах Pub/Sub.
Продюсеры и консьюмеры в Sentinel
Продюсеры подключаются к мастер-узлу, так как только мастер может принимать новые записи и публиковать сообщения в каналы Pub/Sub.
Консьюмеры также должны подключаться к мастер-узлу, поскольку только мастер транслирует сообщения по каналам Pub/Sub.
В случае, если мастер выходит из строя, Redis Sentinel выполняет фейловер и переводит одну из реплик в роль нового мастера. Продюсеры и консьюмеры должны переподключиться к новому мастеру для продолжения работы. Клиенты Redis можно настроить так, чтобы они автоматически находили новый мастер через Sentinel.
Как работает Redis Sentinel
Redis Sentinel отслеживает состояние всех узлов Redis (мастера и реплик), регулярно отправляя запросы PING. Если мастер-узел перестает отвечать на запросы в течение определенного времени, Sentinel помечает его как недоступный и инициирует процесс фейловера. В рамках фейловера одна из реплик становится новым мастером, и клиенты перенаправляются на него. Это позволяет системе продолжать работу без существенных сбоев.
Мониторинг: Sentinel непрерывно отслеживает состояние мастера и реплик, проверяя их доступность через механизм PING-PONG.
Обнаружение сбоя: если Sentinel не получает ответ от мастера в течение заданного времени (
down-after-milliseconds
), он отмечает его как "недоступный".Выбор нового мастера: Sentinel запускает процесс выборов среди реплик, чтобы определить, какая реплика станет новым мастером. Выбор происходит на основе ряда критериев, таких как задержка репликации и доступность.
Обновление конфигурации: новый мастер становится главным узлом, и Sentinel обновляет клиентов Redis, чтобы они подключались к новому мастеру.
Продюсеры и консьюмеры: все клиенты, которые были подключены к старому мастеру, должны переподключиться к новому мастеру, чтобы продолжить публикацию и получение сообщений.
Таким образом, продюсеры и консьюмеры работают исключительно с мастером, и при фейловере им нужно переключиться на новый мастер.
Ограничения использования Redis Sentinel
Несмотря на отказоустойчивость, которую обеспечивает Redis Sentinel, есть несколько важных ограничений:
Отсутствие шардирования: Sentinel работает с мастер-реплика топологиями, но не поддерживает шардирование данных. Это означает, что каждый мастер управляет всей базой данных, что может стать узким местом для производительности при высоких нагрузках.
Отказ мастера и потеря сообщений: если основной узел (мастер) выходит из строя, Redis Sentinel переключает роль мастера на одну из реплик. Однако все сообщения, отправленные через Pub/Sub до фейловера, будут потеряны, поскольку Pub/Sub не сохраняет сообщения на диске или в памяти для повторной доставки.
Прерывание подписок: когда происходит фейловер, клиенты Redis должны переподключиться к новому мастеру. Это может привести к тому, что подписки на Pub/Sub-каналы будут разорваны, и подписчики могут пропустить сообщения, отправленные в промежутке между разрывом соединения и повторным подключением.
Сложность управления крупными кластерами: Redis Sentinel подходит для управления небольшими кластерами. Для крупных систем, требующих горизонтального масштабирования и шардирования, лучше использовать Redis Cluster.
Шаги настройки Redis Sentinel
Для настройки отказоустойчивого кластера Redis с Sentinel, следует:
Настроить несколько инстансов Redis: минимальная конфигурация включает один мастер и как минимум две реплики для обеспечения надёжности.
Установить и настроить Redis Sentinel: каждый Sentinel будет мониторить состояние всех узлов Redis и автоматически переключать роли между ними при сбоях.
Настроить клиентов для работы с Sentinel: клиенты Redis должны быть настроены так, чтобы автоматически находить новый мастер после фейловера.
Пример конфигурации Redis Master и Replica
Конфигурация Redis Master (redis-master.conf
):
port 6379
bind 0.0.0.0
protected-mode yes
dir /data
dbfilename dump.rdb
appendonly yes # Включает персистентность
Конфигурация Redis Replica (redis-replica.conf
):
port 6379
bind 0.0.0.0
protected-mode yes
dir /data
dbfilename dump.rdb
appendonly yes
replicaof redis-master 6379 # Указываем мастера для репликации
Пример конфигурации Redis Sentinel (sentinel.conf
):
port 26379
bind 0.0.0.0
dir /tmp
protected-mode no
# Указываем, что Sentinel будет следить за мастером с именем "mymaster" на порту 6379
sentinel monitor mymaster redis-master 6379 2 # 2 - это количество Sentinel'ов, которые должны согласиться, что мастер недоступен
# Время, через которое Sentinel будет считать мастер недоступным (в миллисекундах)
sentinel down-after-milliseconds mymaster 5000
# Время для фейловера (в миллисекундах)
sentinel failover-timeout mymaster 10000
# Как часто Sentinel будет проверять состояние реплик
sentinel parallel-syncs mymaster 1
Пример Docker Compose файла для Redis Sentinel
Теперь давайте создадим docker-compose.yml, который позволяет запустить высокодоступную конфигурацию Redis с одним мастером, двумя репликами и тремя инстансами Sentinel.
docker-compose.yml
:
version: '3.8'
services:
redis-master:
image: redis:7.0-alpine
container_name: redis-master
hostname: redis-master
ports:
- "6379:6379"
volumes:
- ./redis-master.conf:/usr/local/etc/redis/redis.conf
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
redis-replica1:
image: redis:7.0-alpine
container_name: redis-replica1
hostname: redis-replica1
depends_on:
- redis-master
volumes:
- ./redis-replica.conf:/usr/local/etc/redis/redis.conf
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
environment:
- REDIS_REPLICATION_MODE=replica
networks:
- redisnet
redis-replica2:
image: redis:7.0-alpine
container_name: redis-replica2
hostname: redis-replica2
depends_on:
- redis-master
volumes:
- ./redis-replica.conf:/usr/local/etc/redis/redis.conf
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
environment:
- REDIS_REPLICATION_MODE=replica
networks:
- redisnet
redis-sentinel1:
image: redis:7.0-alpine
container_name: redis-sentinel1
hostname: redis-sentinel1
depends_on:
- redis-master
ports:
- "26379:26379"
volumes:
- ./sentinel.conf:/usr/local/etc/redis/sentinel.conf
command: ["redis-server", "/usr/local/etc/redis/sentinel.conf", "--sentinel"]
redis-sentinel2:
image: redis:7.0-alpine
container_name: redis-sentinel2
hostname: redis-sentinel2
depends_on:
- redis-master
ports:
- "26380:26379"
volumes:
- ./sentinel.conf:/usr/local/etc/redis/sentinel.conf
command: ["redis-server", "/usr/local/etc/redis/sentinel.conf", "--sentinel"]
redis-sentinel3:
image: redis:7.0-alpine
container_name: redis-sentinel3
hostname: redis-sentinel3
depends_on:
- redis-master
ports:
- "26381:26379"
volumes:
- ./sentinel.conf:/usr/local/etc/redis/sentinel.conf
command: ["redis-server", "/usr/local/etc/redis/sentinel.conf", "--sentinel"]
networks:
redisnet:
driver: bridge
Что тут вообще происходит?
redis-master-1, redis-master-2, и redis-master-3: эти три сервиса создают мастер-узлы Redis, которые будут выполнять шардирование данных.
redis-replica-1, redis-replica-2, и redis-replica-3: эти узлы являются репликами для каждого мастера, которые будут использоваться для фейловера в случае отказа мастера.
Каждый узел имеет уникальный порт, начиная с 6379 до 6384. Это позволяет избежать конфликтов и развернуть несколько инстансов Redis на одной машине.
Запуск и работа Redis Sentinel
Чтобы запустить кластер Redis с Sentinel:
Убедитесь, что у вас есть конфигурационные файлы для мастера, реплик и Sentinel.
Запустите все сервисы с помощью
docker-compose
:
docker-compose up -d
Теперь у вас работает высокодоступный кластер Redis с одним мастер-узлом и двумя репликами, которые находятся под управлением трёх инстансов Sentinel.
Проверка статуса кластера
Для проверки статуса кластера Redis с Sentinel используйте следующую команду, подключаясь к одному из Sentinel:
docker exec -it redis-sentinel1 redis-cli -p 26379 sentinel masters
Эта команда покажет состояние мастера и его реплик, а также информацию о том, какой из узлов активен в данный момент.
Пример реализации Pub/Sub в Redis Sentinel
Ниже приведены примеры издателя (Publisher) и подписчика (Subscriber) с использованием Pub/Sub в кластере Redis, управляемом Sentinel.
Пример издателя (Publisher):
import redis
def publish_message():
redis_client = redis.StrictRedis(host='localhost', port=6379, decode_responses=True)
channel = 'orders'
for i in range(5):
message = f'Order {i + 1}'
redis_client.publish(channel, message)
print(f"Published: {message}")
if __name__ == "__main__":
publish_message()
Что тут происходит?
Издатель подключается к мастеру Redis и публикует сообщения в канал
orders
.Клиент работает с Sentinel, который отслеживает фейловер и автоматически направляет его на новый мастер, если текущий мастер выйдет из строя.
Пример подписчика (Subscriber):
import redis
def subscribe_channel():
redis_client = redis.StrictRedis(host='localhost', port=6379, decode_responses=True)
pubsub = redis_client.pubsub()
pubsub.subscribe('orders')
print("Subscribed to 'orders' channel.")
while True:
message = pubsub.get_message()
if message and not message['type'] == 'subscribe':
print(f"Received: {message['data']}")
time.sleep(0.1)
if __name__ == "__main__":
subscribe_channel()
Что тут происходит?
Подписчик подключается к Redis и подписывается на канал
orders
.Как только сообщение публикуется в этот канал, подписчик его получает.
Конфигурация HA-кластера Redis Cluster
Redis Cluster отличается от Redis Sentinel тем, что он поддерживает горизонтальное масштабирование и шардирование данных. Redis Cluster разделяет данные между несколькими мастер-узлами (шардами), каждый из которых имеет одну или несколько реплик для обеспечения отказоустойчивости.
Шаги настройки Redis Cluster
Создать несколько Redis-узлов: нам нужны как минимум три мастер-узла и три реплики, чтобы обеспечить высокую доступность и отказоустойчивость.
Настроить кластеризацию: Redis Cluster использует шардирование данных с распределением по слотам, и все узлы должны быть настроены для работы в режиме кластера.
Связать мастер-узлы с репликами: реплики будут назначены для каждого мастер-узла для обеспечения фейловера в случае отказа.
Пример конфигурации для Redis Cluster
Конфигурация Redis Cluster (redis.conf
):
port 6379
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
appendfilename "appendonly.aof"
dir "/data"
bind 0.0.0.0
protected-mode no
Эта конфигурация включает:
cluster-enabled yes — включает режим кластера.
cluster-config-file nodes.conf — файл конфигурации кластера, который будет автоматически создаваться Redis при запуске.
appendonly yes — включает персистентность с использованием AOF (Append-Only File) для сохранности данных.
Docker Compose файл для запуска HA кластера Redis Cluster
Теперь создадим docker-compose.yml
, который позволяет запустить Redis Cluster с тремя мастер-узлами и тремя репликами.
docker-compose.yml
:
version: '3.8'
services:
redis-master-1:
image: redis:7.0-alpine
container_name: redis-master-1
hostname: redis-master-1
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
volumes:
- ./redis-master.conf:/usr/local/etc/redis/redis.conf
networks:
- redisnet
ports:
- "6379"
expose:
- "6379"
redis-master-2:
image: redis:7.0-alpine
container_name: redis-master-2
hostname: redis-master-2
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
volumes:
- ./redis-master.conf:/usr/local/etc/redis/redis.conf
networks:
- redisnet
ports:
- "6380"
expose:
- "6380"
redis-master-3:
image: redis:7.0-alpine
container_name: redis-master-3
hostname: redis-master-3
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
volumes:
- ./redis-master.conf:/usr/local/etc/redis/redis.conf
networks:
- redisnet
ports:
- "6381"
expose:
- "6381"
redis-replica-1:
image: redis:7.0-alpine
container_name: redis-replica-1
hostname: redis-replica-1
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
volumes:
- ./redis-master.conf:/usr/local/etc/redis/redis.conf
networks:
- redisnet
ports:
- "6382"
expose:
- "6382"
redis-replica-2:
image: redis:7.0-alpine
container_name: redis-replica-2
hostname: redis-replica-2
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
volumes:
- ./redis-master.conf:/usr/local/etc/redis/redis.conf
networks:
- redisnet
ports:
- "6383"
expose:
- "6383"
redis-replica-3:
image: redis:7.0-alpine
container_name: redis-replica-3
hostname: redis-replica-3
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
volumes:
- ./redis-master.conf:/usr/local/etc/redis/redis.conf
networks:
- redisnet
ports:
- "6384"
expose:
- "6384"
networks:
redisnet:
driver: bridge
Пояснение Docker Compose файла:
redis-master-1, redis-master-2, и redis-master-3: эти три сервиса создают мастер-узлы Redis, которые будут выполнять шардирование данных.
redis-replica-1, redis-replica-2, и redis-replica-3: эти узлы являются репликами для каждого мастера, которые будут использоваться для фейловера в случае отказа мастера.
Каждый узел имеет уникальный порт, начиная с 6379 до 6384. Это позволяет избежать конфликтов и развернуть несколько инстансов Redis на одной машине.
Запуск кластера Redis Cluster
После запуска всех контейнеров с помощью команды
docker-compose up -d
Создайте кластер с помощью команды redis-cli
. Для этого подключитесь к одному из мастер-узлов и выполните команду
docker exec -it redis-master-1 redis-cli --cluster create \
redis-master-1:6379 redis-master-2:6380 redis-master-3:6381 \
redis-replica-1:6382 redis-replica-2:6383 redis-replica-3:6384 \
--cluster-replicas 1
Эта команда создаст кластер с тремя мастер-узлами и тремя репликами, где каждая реплика будет назначена одному из мастеров.
Что тут происходит?
--cluster create
: Команда для создания нового кластера Redis.--cluster-replicas 1
: Опция указывает, что каждому мастер-узлу должна быть назначена одна реплика для обеспечения отказоустойчивости.
Проверка кластера
После создания кластера вы можете проверить его состояние с помощью команды:
docker exec -it redis-master-1 redis-cli --cluster check redis-master-1:6379
Эта команда проверит состояние кластера и покажет информацию о слотах и назначенных узлах.
Как Redis Cluster распределяет ключи?
Redis Cluster использует 16,384 слота шардирования для распределения данных между узлами. Когда вы создаете кластер, Redis автоматически распределяет эти слоты между мастер-узлами, и каждый мастер отвечает за определенные слоты. Это распределение происходит при создании кластера с помощью команды redis-cli --cluster create
.
Процесс распределения ключей по узлам
Ключ хешируется с помощью алгоритма CRC16.
Результат хеширования используется для назначения ключа одному из 16,384 слотов.
Слоты равномерно распределяются между мастер-узлами при создании кластера, и каждый мастер-узел отвечает за свою группу слотов.
Когда кластер запущен, Redis Cluster автоматически перенаправляет операции с ключами на мастер-узел, который отвечает за соответствующий слот.
Само распределение ключей не указывается явно в конфигурационных файлах, таких как redis.conf
, потому что Redis Cluster автоматически управляет этим процессом. Однако, команда создания кластера, которую мы выполняем после запуска контейнеров, распределяет слоты между мастер-узлами:
redis-cli --cluster create \
redis-master-1:6379 redis-master-2:6380 redis-master-3:6381 \
redis-replica-1:6382 redis-replica-2:6383 redis-replica-3:6384 \
--cluster-replicas 1
В этой команде:
Узлы
redis-master-1
,redis-master-2
, иredis-master-3
становятся мастер-узлами кластера.Redis автоматически распределяет 16,384 слота между этими мастер-узлами.
Реплики
redis-replica-1
,redis-replica-2
, иredis-replica-3
назначаются соответствующим мастерам для обеспечения отказоустойчивости.
Если у нас три мастер-узла, Redis может распределить слоты примерно следующим образом:
redis-master-1
отвечает за слоты 0–5461.redis-master-2
отвечает за слоты 5462–10922.redis-master-3
отвечает за слоты 10923–16383.
При добавлении нового ключа Redis Cluster автоматически определяет, какой узел будет обрабатывать этот ключ, в зависимости от того, в какой слот он попадает.
После создания кластера вы можете проверить распределение слотов с помощью команды:
redis-cli --cluster check redis-master-1:6379
Эта команда покажет, какой мастер-узел отвечает за какой диапазон слотов, а также статус кластера.
Как Redis Cluster управляет подключениями клиентов?
В Redis Cluster консьюмеры (как и продюсеры) не подключаются напрямую к конкретным мастер-узлам для обработки определённых ключей. Вместо этого они могут подключаться к любому узлу в кластере, а Redis Cluster сам перенаправляет запросы на нужный мастер-узел, который отвечает за данные, связанные с запрашиваемыми ключами. Давайте разберем, как это работает на практике.
Redis Cluster использует механизм, который делает взаимодействие с распределенными узлами прозрачным для клиентов. Вот что происходит под капотом:
Подключение к любому узлу: консьюмер (или продюсер) может подключаться к любому узлу в кластере, даже если этот узел не является мастером для интересующих ключей. Это избавляет от необходимости знать, какой узел отвечает за конкретные данные.
Перенаправление (Redis Cluster Redirect): если узел, к которому подключился клиент, не обслуживает запрашиваемый ключ (то есть этот ключ принадлежит другому слоту, который управляется другим мастер-узлом), Redis автоматически отправляет клиенту ответ с кодом MOVED. Этот ответ указывает клиенту, к какому узлу нужно обратиться для выполнения операции с этим ключом.
Клиентская библиотека: большинство клиентских библиотек Redis, поддерживающих режим кластера (например,
redis-py
,Jedis
для Java илиioredis
для Node.js), автоматически обрабатывают перенаправления. Когда библиотека получает команду MOVED, она перенаправляет запрос на нужный узел кластера. Это позволяет клиенту работать с Redis Cluster так, как будто это единый сервер, хотя под капотом происходит перенаправление на нужные узлы.
Как это работает на практике
Допустим, у нас есть Redis Cluster с тремя мастер-узлами, которые распределяют между собой 16,384 слота шардирования. Консьюмеру не нужно заранее знать, какой узел отвечает за конкретный слот, чтобы получить данные с ключом.
Процесс работы выглядит примерно так:
Консьюмер подключается к любому узлу в кластере, например, к
redis-master-1
.Запрос на данные: консьюмер отправляет запрос на получение данных по определённому ключу, например,
GET mykey
.Определение слота: Redis вычисляет слот для ключа
mykey
с помощью хеш-функции CRC16. Допустим, ключ попадает в слот, который обслуживаетсяredis-master-2
.Ответ с кодом MOVED: если консьюмер подключён не к тому узлу (например, к
redis-master-1
), узел возвращает сообщение MOVED с указанием узлаredis-master-2
, который обслуживает нужный слот.Автоматическое перенаправление: клиентская библиотека получает ответ MOVED и перенаправляет запрос на узел
redis-master-2
, где и происходит обработка запроса.Получение данных: консьюмер получает данные с узла, который отвечает за ключ.
Таким образом, консьюмеры могут подключаться к любому узлу, а кластер сам заботится о том, чтобы перенаправить запрос на правильный узел. Клиентская библиотека автоматически обрабатывает это взаимодействие.
Оптимизация запросов и фейловер в Redis Cluster
Повторные запросы и оптимизация
Кэширование слотов: чтобы избежать постоянных перенаправлений, клиентские библиотеки Redis часто кэшируют информацию о слотах. Это означает, что после первого перенаправления библиотека будет знать, какой узел отвечает за конкретный слот, и в следующий раз отправит запрос напрямую к нужному узлу без необходимости перенаправления.
Фейловер и репликация: если мастер-узел выходит из строя, Redis Cluster автоматически поднимает одну из его реплик в роли нового мастера. Клиентские библиотеки Redis Cluster обновляют свою информацию о слотах после фейловера и перенаправляют запросы на новый мастер.
Фейловер и перенаправление запросов
При фейловере в Redis Cluster (когда мастер-узел выходит из строя), Redis назначает одну из реплик в качестве нового мастера. Клиентские библиотеки Redis автоматически обновляют свою информацию о слотах после фейловера.
Фейловер: когда мастер выходит из строя, Redis Cluster автоматически поднимает реплику на место мастера.
Перенаправление: клиентские библиотеки получат сообщение MOVED, если попытались отправить запрос на старый мастер, и перенаправят запрос на новый мастер.
Обновление кэша: после получения MOVED клиентская библиотека обновит свой кэш слотов, чтобы отправлять запросы на новый мастер.
Пример Pub/Sub для Redis Cluster
Для реализации продюсера и консьюмера в Pub/Sub с использованием Redis Cluster, который поддерживает работу с перенаправлением запросов между узлами кластера, можно использовать библиотеку redis-py
(Python) или аналогичную библиотеку для другого языка, поддерживающую кластерные режимы Redis. Мы это сделаем на Python с использованием redis-py-cluster
.
Требования:
Redis Cluster уже запущен и работает с тремя мастер-узлами.
Мы будем использовать библиотеку
redis-py-cluster
, которая поддерживает автоматическое перенаправление запросов между узлами.
Установка зависимостей
Убедитесь, что у вас установлена библиотека redis-py-cluster
. Если нет, установите ее с помощью pip
:
pip install redis-py-cluster
Реализация продюсера
Продюсер будет публиковать сообщения в определённый канал Pub/Sub, а Redis Cluster сам позаботится о том, какой мастер-узел будет обрабатывать этот запрос.
from redis.cluster import RedisCluster
def publish_messages():
# Подключаемся к одному из узлов кластера
startup_nodes = [{"host": "127.0.0.1", "port": "6379"}] # один из мастер-узлов кластера
redis_cluster = RedisCluster(startup_nodes=startup_nodes, decode_responses=True)
channel_name = 'my_channel'
for i in range(1, 6):
message = f"Message {i}"
# Публикуем сообщение в канал Pub/Sub
redis_cluster.publish(channel_name, message)
print(f"Published: {message}")
if __name__ == "__main__":
publish_messages()
Что тут происходит?
RedisCluster: мы создаем клиент Redis, который подключается к одному из узлов кластера.
publish: используем метод
publish
для отправки сообщения в канал Pub/Sub. Redis Cluster сам позаботится о том, чтобы перенаправить запрос на нужный узел, если это необходимо.Сообщения публикуются в канал
my_channel
, и Redis Cluster автоматически обработает публикацию.
Реализация консьюмера
Консьюмер будет подписываться на канал Pub/Sub и получать сообщения, даже если данные распределены по разным узлам.
from redis.cluster import RedisCluster
def consume_messages():
# Подключаемся к одному из узлов кластера
startup_nodes = [{"host": "127.0.0.1", "port": "6379"}] # один из мастер-узлов кластера
redis_cluster = RedisCluster(startup_nodes=startup_nodes, decode_responses=True)
channel_name = 'my_channel'
# Подписываемся на канал Pub/Sub
pubsub = redis_cluster.pubsub()
pubsub.subscribe(channel_name)
print(f"Subscribed to {channel_name}. Waiting for messages...")
# Получаем сообщения
while True:
message = pubsub.get_message()
if message and not message['type'] == 'subscribe':
print(f"Received: {message['data']}")
# Маленькая задержка для снижения нагрузки на процессор
time.sleep(0.1)
if __name__ == "__main__":
consume_messages()
Объяснение:
pubsub(): Мы используем метод
pubsub()
для подписки на канал Pub/Sub.subscribe(): Подписываемся на канал
my_channel
. Redis Cluster автоматически перенаправляет запросы на узел, который обрабатывает этот канал.get_message(): Этот метод проверяет наличие новых сообщений в канале. Как только сообщение поступает, мы его обрабатываем.
Консьюмер также подключается к любому узлу кластера и получает сообщения через механизм Pub/Sub.
Как это работает?
Подключение: продюсер и консьюмер могут подключаться к любому узлу кластера.
Хеширование ключа канала: Redis Cluster автоматически рассчитывает слот, соответствующий каналу Pub/Sub (
my_channel
), с использованием хеш-функции CRC16.Перенаправление: если узел, к которому подключился продюсер или консьюмер, не обслуживает слот, связанный с каналом, Redis Cluster отправляет клиенту команду MOVED, перенаправляя его к правильному узлу.
Обработка: когда сообщение публикуется или получено, Redis Cluster заботится о том, чтобы данные обрабатывались правильным узлом, независимо от того, где изначально был сделан запрос.
Заключение
Внедрение высокодоступного кластера Redis для Pub/Sub — это ключевой шаг в создании отказоустойчивой и масштабируемой системы обмена сообщениями. Использование Redis Sentinel для управления фейловерами или Redis Cluster для автоматического шардирования данных и обработки ключей позволяет обеспечить бесперебойную работу приложений, даже при сбое отдельных узлов.
Ключевой момент заключается в том, что правильно настроенная архитектура Redis может справляться с высокими нагрузками, обеспечивать гибкость и сохранять производительность даже в условиях отказа узлов. Sentinel управляет репликацией и фейловерами, гарантируя, что продюсеры и консьюмеры смогут продолжать свою работу, автоматически переподключаясь к новому мастеру. Redis Cluster добавляет горизонтальное масштабирование, позволяя распределять ключи и данные по множеству узлов, а клиентские библиотеки автоматически перенаправляют запросы на нужные узлы.
Для сценариев, требующих высокой производительности и надежности, таких как системы заказов или уведомлений в реальном времени, Redis Pub/Sub с высокой доступностью становится незаменимым инструментом. При этом необходимо учитывать такие ограничения, как потеря сообщений при фейловере и необходимость в дополнительных механизмах хранения сообщений для обеспечения гарантированной доставки.
Рассмотренные в статье примеры конфигураций и кода показывают, как можно построить такую систему на практике, используя Docker для развертывания Redis кластера и Python для работы с Pub/Sub. Это позволяет разработчикам быстро начать работу с Redis и настроить высокодоступную систему обмена сообщениями.
Дополнительные ресурсы
Книги
"Redis Essentials", авторы Maxwell Dayvson Da Silva и Hugo Lopes Tavares (2015)
Эта книга охватывает ключевые темы Redis, включая его настройку и конфигурацию для обеспечения высокой доступности с помощью Redis Sentinel и кластеризации. Это отличный ресурс для понимания, как Redis используется в реальных продуктивных средах.
"Redis in Action", автор Josiah L. Carlson (2013)
Данная книга является подробным исследованием Redis, охватывающим различные сценарии использования и передовые функции, такие как Pub/Sub, репликация и персистентность. В книге также рассматриваются практические примеры настройки высокой доступности.
"Mastering Redis", автор Jeremy Nelson (2016)
Книга погружается в детали работы Redis и предоставляет информацию о том, как настраивать Redis для кластеризации и обеспечения высокой доступности. Она будет особенно полезна профессионалам, стремящимся оптимизировать Redis в распределённых средах.
"Designing Data-Intensive Applications", автор Martin Kleppmann (2017)
Хотя эта книга не является специфичной для Redis, она изучает распределённые системы, репликацию и консистентность — важные концепции для настройки высокой доступности с использованием инструментов, таких как Redis.
Статьи и ресурсы
"Redis Cluster Tutorial" от Redis.io
Официальное руководство, которое объясняет, как настроить Redis Cluster, а также детально рассматривает механизм распределения слотов и управление фейловером. Это обязательное руководство для работы с высокой доступностью Redis.
"Redis High Availability Guide: Setting Up Redis with Sentinel", Hevo Data
Статья предоставляет подробное руководство по настройке Redis Sentinel для обеспечения высокой доступности, включая фейловер и конфигурации мастер-реплик.
"Caching at Scale with Redis", автор Lee Atchison
Эта электронная книга фокусируется на том, как эффективно масштабировать Redis и оптимизировать его производительность, обсуждая стратегии кэширования в высоконагруженных средах.
"Best Practices for Redis High Availability", Redis Labs
В этом документе описываются лучшие практики для конфигурирования Redis с использованием Sentinel и кластеризации для обеспечения высокой доступности. Также представлены рекомендации по репликации и управлению фейловером.