Как составить наиболее эффективный алгоритм групповой рассылки сообщений по каналам WebSocket?
Здравствуйте, я занимаюсь реализацией обмена сообщений через WebSocket и у меня возник вопрос в реализации обмена сообщениями между клиентом и сервером.
У нас есть channel с параметрами channel_id и массивом пользователей который содержит условно от 2 до n количество пользователей, где у пользователя есть user_id и username. С клиента одного из пользователей поступает запрос на создание текстового сообщения содержащий message_id и content, где message_id - это индификатор сообщения, а content это его содержимое. Сообщение поступив на сервер вызывает событие, которое рассылает поступившее сообщение другим клиентам а так же записывает данное сообщение в базу данных. А так же у нас есть Map, которая содержит key = user_id и value = socket_channel_id, она является общим хранилищем всех соединений которые на данный момент подключены к сервису. Теперь немного к описанию алгоритма, мы делаем запрос в базу данных по id канала, получаем список пользователей, проверяем на содержание user_id в общей Map, если он подключен, присылаем ему в сокет данное сообщение. Вроде бы всё логично и работающе, но проблема в чём. В дальнейшем я более чем уверен что это будет проблемой производительности. Ведь соединений может быть как 2 так и 1_000_000+, что уже будет плохо и перебор всех элементов такой Map на каждом создание сообщения будет не очень хорошо сказываться, а так же, в примере это групповой канал, который может содержать так же от до 2 до бесконечного числа пользователей. И таким образом это станет очень уязвимым местом в приложении. И отсюда у меня возник вопрос, возможно есть какие либо стандарты или практики реализации групповой рассылки через socket. Спасибо.
У тебя сервер хранит все открытые сокеты клиентов, по той же логике, как оптимизируют загрузку файлов в несколько потоков, сделай и рассылку. Создай пул воркеров, создай очередь на отправку сообщений, воркер по завершению отсылки будет брать следующее сообщение на отправку.
Такой подход позволит реализовать воркеров на нескольких нодах, мало того, можно сразу очереди по какому то правилу (хеш от id например, но это фиксирует количество нод, для смены придется весь кластер перезапускать) раскидывать между нодами, тогда и синхронизацией очередей между нодами не нужно заморачиваться.
Вам по-любому придется отправить сообщение каждому пользователю, кто онлайн и подписан на канал.
Единственное, что вы можете соптимизировать - это нахождение списка пользователей.
Вам надо как-то побыстрее определить, какие пользователи сейчас онлайн из данного канала. Можно или поддерживать эту информацию в памяти (это будет map из channel_id в set user_id). Когда пользователь выходит в онлайн, надо добавить его в структуру данных для всех каналов, на которые он подписан. Когда пользователь отваливается - удаляйте его из памяти и чистите структуру (надо удалить ключ из map, если там значение осталось пустым).
Еще вариант: сделать это прямо в базе данных. Для каждого пользователя поддерживайте флаг - онлайн ли он. И в базе данных спрашивайте список всех онлайн пользователей, которые подписаны на нужный канал. Можно даже ваш map user_id->socket_channel_id в базу запихать.
Ведь соединений может быть как 2 так и 1_000_000+, что уже будет плохо и перебор всех элементов такой Map на каждом создание сообщения будет не очень хорошо сказываться, а так же, в примере это групповой канал, который может содержать так же от до 2 до бесконечного числа пользователей.
В общем это вполне рабочая ситуация. Mail ru делали что-то похожее и для таких ситуаций написали свою либу для работы с вебсокетами, тут статья, также вроде у них было еще видео на highload, где также объясняли проблемы и пути решения
но на сколько я понял про эффективную параллельную передачу данных там речи не шло
Не уверен, что правильно понимаю эту фразу, но засунуть в сообщение для 3кк соединений это не долго, каналы сами по себе достаточно эффективно работают параллельно (точнее конкурентно, но в данном случае не думаю что это настолько принципиально), в статье пункт 3.3.1 Goroutine pool есть ненмного текста про воркер пул, который будет обрабатывать это достаточно эффективно.