Первым заходом команда настояла на стандартном пути: создавать по очереди на каждое устройство и биндинги устройств к инстансам сервисов
Сначала хотели на etcd, но потом чтобы не усложнять, решили сами написать распределение (ахаха когда написал понял насколько это тупо, хотя по-прежнему возможно)
Ну, кроме того, что допустили ошибку из-за которой если один сервис падает, приходится убивать и поднимать все сразу, во-вторых, из-за определенных проблем, часто не все инстансы были доступны, поэтому часть контроллеров просто не обрабатывалась
Хотели переделать на БД,
как я писал в посте выше, но поняли, что БД будет плохо от кол-ва UPDATE статусов сообщений, а добавлять еще один инстанс / использовать другую БД, ну можно, но хотелось что-то попроще
Тогда с коллегой пришли к гибридному решению:
- Парсеры отправляют события, что они готовы обработать сообщений
- Сервисы сокетов (в которые приходят сообщения от устройств) получают эти события, смотрят какие контроллеры сейчас не в обработке, отправляет (тоже через событие) сообщения одного из них и лочит в redis (просто SET locked:${controller_id})
- Парсер пока парсит полученное сообщение сообщение делает раз в Х секунд запись в redis с текущей датой (SET message-health-check:${controller__id}), а в конце обработки удаляет лок из redis (DEL locked:${controller_id})
- Сервисы сокетов раз в N секунд проверяют когда были записи с последней датой в redis, если они были раньше чем Y секунд, то значит парсер умер не доработав сообщения и тогда мы разлачиваем контроллер
- Ну и напоследок подписались на удаление лока, чтобы разлачивать локально
Что мы этим получили:
- Парсеры и Сокеты могут как масштабироваться, так и падать в любом кол-ве, потому что теперь они полностью stateless (контроллеры не завязаны на них) и система продолжит работать
- Поскольку Парсеры теперь работают по pull модели, мы можем просто делать rate-limit даже не уровне кода
- Каждый отдельный сокет управляет только своим набором сообщений
- Никакой синхронной коммуникации (все на событиях), что дает сервисам возможность падать и подниматься сколько угодно раз
- Поскольку локи в redis, даже при переключении контроллера между сокетами мы случайно не возьмем его в обработку
Короче, если уж решили "stateless", то и масштабирование надо строить по stateless, то есть, не привязывать что-либо, к чему либо