В прошлом уроке Pub/Sub терял сообщения, если подписчика не было на месте. Для письма «ваш заказ принят» так нельзя: письмо должно уйти, даже если сервис рассылки перезапускался. В уроке про списки мы уже собрали простую очередь писем на RPUSH и LPOP. Сегодня доводим её до рабочего состояния: работник ждёт задачи, не тратя запросы впустую, и не теряет их, если упал.
Чеки на кухне
Представь кухню ресторана. Официант пишет заказ на чеке и вешает его на планку. Повар снимает чек с другого края, готовит блюдо и выбрасывает чек. Если заказов нет, повар не бегает к планке каждую секунду, он просто ждёт, пока зазвенит звонок. Поваров может быть несколько, и каждый чек достаётся ровно одному из них.
А теперь повар снял чек и ушёл домой посреди готовки. Чек у него в кармане, блюдо никто не приготовит. Хорошие кухни делают иначе: снятый чек повар кладёт на свою полочку «готовлю», а выбрасывает его, только когда блюдо отдано. Если повар пропал, старший смотрит на его полочку и возвращает чеки на планку.
Это и есть очередь задач. Официант это производитель, повар это потребитель, планка это список в Redis.
Производитель и работник
Магазин оформил заказ и кладёт задачу «отправить письмо» в голову списка:
127.0.0.1:6379> LPUSH queue:emails order:15
(integer) 1
Работник забирает задачи с хвоста. Кладём слева, забираем справа: первой уйдёт самая старая задача, как в очереди в кассу.
В уроке 7 работник спрашивал RPOP и на пустой очереди получал (nil). Чтобы не спрашивать снова и снова, есть блокирующая версия BRPOP. Посмотрим на настоящем Redis с двумя терминалами. Работник в первом запускает ожидание на 10 секунд, очередь пуста:
127.0.0.1:6379> BRPOP queue:emails 10
Ответа нет, redis-cli висит. Через две секунды магазин во втором терминале кладёт задачу:
127.0.0.1:6379> LPUSH queue:emails order:15
(integer) 1
И работник в первом терминале тут же получает её:
1) "queue:emails"
2) "order:15"
(2.02s)
BRPOP возвращает пару: из какого списка взята задача и сама задача. Список приходит в ответе, потому что ждать можно сразу на нескольких: BRPOP queue:urgent queue:emails 10 сначала проверит срочную очередь. (2.02s) дописал redis-cli: столько длилось ожидание.
Если работников несколько и все ждут на BRPOP, каждая задача достанется одному из них. В этом главное отличие от Pub/Sub, где сообщение получает каждый подписчик.
Работник упал: задача пропала
У схемы с BRPOP есть дыра. Работник забрал задачу, и в этот момент её больше нет в Redis, она только в памяти процесса. Если процесс упал, не успев отправить письмо, задача потеряна навсегда. Как чек в кармане ушедшего повара.
урок из курса
Урок из курса «Redis с нуля». Курс покупается один раз и остаётся навсегда: 500 ₽.
Оплата пока не подключена: премиум и курсы открываются промокодом.