Почему Redis для очередей?
У меня сложилось впечатление, что Redis может стать хорошим кандидатом для внедрения системы массового обслуживания. До этого момента мы использовали нашу базу данных MySQL с опросом или RabbitMQ. С RabbitMQ у нас было много проблем - клиентские библиотеки очень плохие и глючные, и мы хотели бы не тратить слишком много времени на их исправление, несколько проблем с консолью управления сервером и т. Д. И на время По крайней мере, мы не стремимся к миллисекундам или серьезному увеличению производительности, поэтому, пока система имеет архитектуру, которая интеллектуально поддерживает очередь, мы, вероятно, в хорошей форме.
Хорошо, так что это фон. По сути, у меня есть очень классическая, простая модель очереди - несколько производителей, производящих работу, и несколько потребителей, потребляющих работу, и как производители, так и потребители должны иметь возможность разумного масштабирования. Оказывается, наивный PUBSUB
не работает, так как я не хочу, чтобы все подписчики потребляли работу, я просто хочу, чтобы один подписчик получил работу. На первый взгляд, это выглядит как BRPOPLPUSH
интеллектуальный дизайн.
Можем ли мы использовать BRPOPLPUSH?
Базовый дизайн BRPOPLPUSH
- у вас есть одна рабочая очередь и очередь выполнения. Когда потребитель получает работу, он атомарно помещает элемент в очередь выполнения, а когда он завершает работу, то это LREM
он. Это предотвращает черную работу, если клиенты умирают, и делает мониторинг довольно легким - например, мы можем сказать, есть ли проблема, заставляющая потребителей занимать много времени для выполнения задач, в дополнение к сообщению, если существует большой объем задач.
Это обеспечивает
- работа доставляется ровно одному потребителю
- работа попадает в очередь выполнения, поэтому она не может быть черной дырой, если потребитель
Недостатки
- Мне кажется довольно странным, что лучший дизайн, который я нашел, на самом деле не использует,
PUBSUB
так как именно на этом и сосредоточено большинство постов в блогах об организации очереди над Redis. Поэтому я чувствую, что упускаю что-то очевидное. Единственный способ, который я использую,PUBSUB
не выполняя задачи дважды, - это просто отправить уведомление о том, что работа пришла, что потребители могут затем неблокироватьRPOPLPUSH
. - Невозможно запросить более одного рабочего элемента за раз, что является проблемой производительности. Не очень большой для нашей ситуации, но довольно очевидно, что эта операция не была рассчитана на высокую пропускную способность или эта ситуация
- Короче говоря: я что-то глупо пропускаю?
Также добавляем тег node.js, потому что это язык, с которым я в основном имею дело. Узел может предложить некоторые упрощения в реализации, учитывая его однопоточный и неблокирующий характер, но, кроме того, я использую библиотеку node-redis, и решения должны или могут быть чувствительны к ее сильным и слабым сторонам.
источник
До сих пор я сталкивался с некоторыми трудностями, я хотел бы документировать здесь.
Как вы справляетесь с логикой переподключения?
Это сложная проблема и особенно сложная проблема при проектировании и реализации очереди сообщений. Сообщения должны иметь возможность стоять в очереди где-то, когда потребители находятся в автономном режиме, поэтому простой паб-саб недостаточно силен, и потребители должны повторно подключаться в состоянии прослушивания. Блокирование всплывающих окон - это сложное состояние, потому что это неидемпотентное состояние прослушивания . Прослушивание должно быть идемпотентной операцией, однако при работе с разъединением в отношении блокирующего всплывающего сообщения вам доставляет большое удовольствие размышлять о том, произошло ли разъединение сразу после того, как операция прошла успешно или непосредственно перед операцией. Это не непреодолимо, но нежелательно.
Кроме того, операция прослушивания должна быть максимально простой. В идеале он должен иметь следующие свойства:
В частности, я выбрал плохую конструкцию, в которой повторный ввод блокирующего всплытия зависел от успеха предыдущих операций, который был хрупким и требовал тщательного обдумывания.
Сейчас я предпочитаю решение Redis PUBSUB + RPOPLPUSH. Это отделяет уведомление о работе от потребления работы, что позволяет нам выделить чистое решение для прослушивания. PUBSUB несет ответственность только за уведомление о работе. Атомная природа RPOPLPUSH отвечает за потребление и делегирование работы только одному потребителю. Сначала это решение казалось излишне сложным по сравнению с блокировкой, но теперь я вижу, что осложнение вовсе не было ненужным; это было решение сложной проблемы.
Однако это решение не совсем тривиально:
Обратите внимание, что дизайн PUBSUB / RPOPLPUSH также имеет проблемы с масштабированием. Каждый потребитель получает легкое уведомление о каждом сообщении, что означает, что у него есть ненужное узкое место. Я подозреваю, что возможно использовать каналы, чтобы осквернить работу, но это, вероятно, хитрый дизайн, чтобы сработать хорошо.
источник
Поэтому основной причиной выбора использования RabbitMQ вместо Redis являются сценарии сбоев и кластеризация.
Эта статья действительно объясняет это лучше всего, поэтому я просто предоставлю ссылку:
https://aphyr.com/posts/283-jepsen-redis
Redis Sentinel и совсем недавно Redis кластеризация не в состоянии обработать ряд очень простых сценариев сбоя, которые сделали его плохим выбором для очереди.
RabbitMQ имеет свой собственный набор проблем, однако, как говорится, он невероятно надежен в производстве и является хорошей очередью сообщений.
Вот пост для кролика:
https://aphyr.com/posts/315-jepsen-rabbitmq
Когда вы смотрите на теорию CAP (согласованность, доступность и обработка разделов), вы можете выбрать только 2 из 3. Мы используем RMQ для CP (согласованность и обработка разделов) с нашей загрузкой сообщений, если мы недоступны, это не так. конец света. Чтобы не потерять сообщения, мы используем ignore для обработки разделов, чтобы не потерять сообщения. Дубликаты могут быть обработаны, так как источник управляет UUID.
источник