Comments 11
Исключительно праздное любопытство — чем вам не подошел сам Faye с faye-redis?
Вместо Oj я бы использовал сразу MultiJson, так будет поддержка JRuby.
Вместо Oj я бы использовал сразу MultiJson, так будет поддержка JRuby.
Больше академический интерес, изначально код писался как пример по организации взаимодействия на сокетах и взаимодействия между узлами.
В данном случае faye здесь просто как реализация сокетов, не более. Можно было взять и Em::WebSocket, что не принципиально, но в данном случае просто показал некоторое решение, которое можно либо развить, либо как в случае с Faye взять готовое.
А за MultiJson спасибо.
Посмотрел как у них реализовано. Подход схожий, но делается упор на абстракцию в виде каналов. В итоге если нужно передавать клиент-клиент, то каждый должен подписываться на канал друг друга. Получится либо очень очень много подписок (изначально старался уйти от этого), либо придем к схеме которую описал в статье, либо что-то еще.
В данном случае faye здесь просто как реализация сокетов, не более. Можно было взять и Em::WebSocket, что не принципиально, но в данном случае просто показал некоторое решение, которое можно либо развить, либо как в случае с Faye взять готовое.
А за MultiJson спасибо.
Посмотрел как у них реализовано. Подход схожий, но делается упор на абстракцию в виде каналов. В итоге если нужно передавать клиент-клиент, то каждый должен подписываться на канал друг друга. Получится либо очень очень много подписок (изначально старался уйти от этого), либо придем к схеме которую описал в статье, либо что-то еще.
Клиент-клиент как раз нет, количество каналов будет равно количеству клиентов.
Публикуете в канал /user/:id, на который подписывается пользователь с :id, в сообщении присылаете id пользователя-отправителя.
Ну и огромный плюс faye — он не ограничен одним websocket.
Публикуете в канал /user/:id, на который подписывается пользователь с :id, в сообщении присылаете id пользователя-отправителя.
Ну и огромный плюс faye — он не ограничен одним websocket.
Да, согласен, перечитал еще раз.
У них получается что лишь одна очередь (подписка на уровне Redis), в которую все шлют уведомление о новых сообщениях, далее фильтруют и выгребают сами данные.
У них получается что лишь одна очередь (подписка на уровне Redis), в которую все шлют уведомление о новых сообщениях, далее фильтруют и выгребают сами данные.
Там чуть сложнее, вот пример ключей, которые он хранит:
1) "/clients"
5) "/channels/time"
7) "/clients/j6hb00g9ta8d8y8b9o48hmi4b2br0bh/channels"
9) "/channels/user/registered"
Т.е. общий пул соединений (клиентов), каналы, на которые подписан конкретный клиент, и очереди для каждого используемого канала.
1) "/clients"
5) "/channels/time"
7) "/clients/j6hb00g9ta8d8y8b9o48hmi4b2br0bh/channels"
9) "/channels/user/registered"
Т.е. общий пул соединений (клиентов), каналы, на которые подписан конкретный клиент, и очереди для каждого используемого канала.
А вот только дописал модуль веб-сокетов для своего движка, нужно ещё документацию сделать.
Так вот, я каждый запускаемый сервер заношу в пул, первый в списке является мастером, если падает — удаляется и следующий становится мастером.
Каждый сервер при старте проверяет — если он не мастер — подключается к нему, слушает на сообщения в специальном внутреннем шифрованом формате. Таким образом используются только веб-сокеты, даже между серверами. Когда нужно отправить сообщение определённой группе пользователей — отправляется пользователям на текущем сервере, и на мастер, который рассылает сообщение на остальные сервера.
Сам пул — MEMORY табличка с одним полем — публичным адресом сервера, например wss://example.com/WebSockets (альтернативно можно хранить в любом другом удобном месте, в том же Redis).
В моем варианте не нужно никакой шины, система сама адаптируется к падениям текущего мастера (который в последствии будет поднят, но станет уже последним в пуле). При отказоустойчивой архитектуре БД получается весьма надежно. Сами пользователи вообще нигде не регистрируются, id пользователя хранится в одном из полей соединения, так как сообщение рассылается на все сервера мне всё равно где именно и сколько одновременных подключений у конкретного пользователя.
Так вот, я каждый запускаемый сервер заношу в пул, первый в списке является мастером, если падает — удаляется и следующий становится мастером.
Каждый сервер при старте проверяет — если он не мастер — подключается к нему, слушает на сообщения в специальном внутреннем шифрованом формате. Таким образом используются только веб-сокеты, даже между серверами. Когда нужно отправить сообщение определённой группе пользователей — отправляется пользователям на текущем сервере, и на мастер, который рассылает сообщение на остальные сервера.
Сам пул — MEMORY табличка с одним полем — публичным адресом сервера, например wss://example.com/WebSockets (альтернативно можно хранить в любом другом удобном месте, в том же Redis).
В моем варианте не нужно никакой шины, система сама адаптируется к падениям текущего мастера (который в последствии будет поднят, но станет уже последним в пуле). При отказоустойчивой архитектуре БД получается весьма надежно. Сами пользователи вообще нигде не регистрируются, id пользователя хранится в одном из полей соединения, так как сообщение рассылается на все сервера мне всё равно где именно и сколько одновременных подключений у конкретного пользователя.
Получается что всё горизонтальное масштабирование упирается в сервис Redis, который вроде бы горизонтально не расширяется? В чем тогда смысл такого подхода, не проще ли для WebSocket держать отдельный сервер вместо Redis?
Что случится, когда сервер перестанет вывозить N одновременно подключенных клиентов? Нужно будет добавить еще один websocket-сервер.
А взаимодействовать между собой они будут через Redis. Что-то мне подсказывает, что сервер WebSocket уткнется в свой потолок гораздо раньше, чем Redis.
А взаимодействовать между собой они будут через Redis. Что-то мне подсказывает, что сервер WebSocket уткнется в свой потолок гораздо раньше, чем Redis.
> Что случится, когда сервер перестанет вывозить N одновременно подключенных клиентов?
Тот же самый вопрос можно задать про Redis. Вы говорите про низкий потолок WebSocket, возможно просто нужно использовать софт для WebSocker сервера который бы давал примерно такой же потолок как redis.
Впрочем определенный выигрыш я вижу тут есть, так как WebSocket и Redis будут потреблять разные ресурсы, поскольку сервер Redis не обязан поддерживать большое количество открытых TCP соединений. Однако трудно все же назвать это полностью горизонтальным масштабированием.
Тот же самый вопрос можно задать про Redis. Вы говорите про низкий потолок WebSocket, возможно просто нужно использовать софт для WebSocker сервера который бы давал примерно такой же потолок как redis.
Впрочем определенный выигрыш я вижу тут есть, так как WebSocket и Redis будут потреблять разные ресурсы, поскольку сервер Redis не обязан поддерживать большое количество открытых TCP соединений. Однако трудно все же назвать это полностью горизонтальным масштабированием.
Судя по замерам отсюда: redis.io/topics/benchmarks Redis не самое узкое место, а если все же производительность упрется в него, то всегда можно перейти на другие решения, например RabbitMQ и построить кластер.
Если например узкое место будет network IO (представим что клиенты отправляют большие объемы данных друг-другу), то сетевой интерфейс редис сервера забьется и такое горизонтальное масштабирование работать не будет.
Вообще я думаю что ваше решение вполне может подходящим к своей ситуации, но все же горизонтальность тут не полная. Наверное вы правы что при необходимости это можно дальше масштабирвать через RabbitMQ (с последним я не сталкивался).
Вообще я думаю что ваше решение вполне может подходящим к своей ситуации, но все же горизонтальность тут не полная. Наверное вы правы что при необходимости это можно дальше масштабирвать через RabbitMQ (с последним я не сталкивался).
Sign up to leave a comment.
Горизонтальное масштабирование websocket-ов на Ruby