Как вам связка SockJS/Redis на Node.JS для личных сообщений на HighLoad проекте?
Суть:
Есть сайт с более чем 150 тыс. уников в сутки. До 10 тыс. человек в онлайне. На сайте есть система диалогов/личных сообщений. Под примитивным AJAX long-polling к PHP-обработчику с выборкой все, разумеется, легло. Необходимо было реализовать более оптимальный способ. Сообщения обязательно должны сохраняться в СУДБ (для истории и поиска). Купили отдельный сервер под node.js, придумали такую архитектуру:
При загрузке страницы с диалогом берутся сообщения через API на PHP из MySQL (как история).
Клиент подписывается через SockJS к серверу с NodeJS на поток из Redis этого канала (диалога).
При отправке сообщения оно дублируется как в Redis (для потока оппонента), так и в MySQL (для истории).
Оппонент получает сообщение из потока в Redis в real-time.
Был сделан бенчмарк на 1000 соединений с потоком 10 сообщений в секунду на каждое. Сервер спокойно выдержал, нода брала не более 50 мб оперативки вместе с ОС. Всего на сервере 64 гб.
Теперь, собственно, вопросы:
1) Насколько хорош этот велосипед?
2) Как идентифицировать собеседников на сервере ноды и делать канал только для них?
3) Насколько безопасно это в плане конфиденциальности переписки?
пилю похожий велик, но с использованием centrifugo, 50000 тысяч держал (не у меня, я в процессе и явно не эксперт ) но 1 и 2 centrifugo решает легко и из коробки, на счет 3 не могу сказать, но вроде все хорошо
Олег Гамега: Тоже использую Centrifugo. Перешел на нее после неудачных опытов с Socket.io, который выжирал всю память как не в себя. Centrifugo показывает себя отлично.
Олег Гамега: хм, проверка юзерида отправителя только на клиенте? Или как? Нашел там пример, где на клиенте объявляется юзерид, из него и прочей соли генерируется токен, НО. Если я укажу ручками чужой юзерид, я буду получать сообщения на его имя? Как это на сервере контролируется?
привет, я автор Centrifugo - Центрифуга идеально ложится под описанные требования, большой плюс, что не нужно будет кардинальным образом менять работающий бэкенд. По поводу вопроса про user ID и чужие сообщения – если правильно организовать каналы, то, конечно, чужие сообщения прочитать будет нельзя. Центрифуга знает id юзера, так как он указывается при подключении. Причем он подтвержден токеном на основе секретного ключа, что исключает его подмену до тех пор пока не утек секретный ключик, естественно:) Соответственно если использовать канал с решеткой (напр. personal#1674 где 1674 – это id пользователя, подробнее в документации) – то только юзер с id 1674 сможет подписаться на этот канал. Соответственно сообщения юзеру нужно публиковать в него. Еще есть приватные каналы с дополнительным подтверждением AJAX-запросом через бэкенд приложения (начинаются с доллара $personal1674 – опять же подробнее в доке, аналогично приватным каналам сервиса pusher.com). Также некоторые используют трудно угадываемые имена каналов просто - на основе id юзера и какого-то алгоритма шифрования (то есть только бэкенд и пользователь знают имя этого канала в итоге) - подобрать такой оч сложно, и на практике некоторые именно так и поступают. То есть существует 3 разных способа так или иначе защитить канал от "прослушки". Какой лучше - решает каждый для себя.
carroll: неправильные настройки каналов/неймспейсов, в данном случае скорее всего не хватает publish: true в настройках, но вообще публиковать новые сообщения нужно с бекенда через API, а не напрямую с клиента - это только в демках с клиента проще публиковать. Еще я вижу presence not available - это потому что presence также не включена в настройках каналов. Вот эта глава очень важна для понимания настроек - https://fzambia.gitbooks.io/centrifugal/content/se...
carroll: честно говоря вообще не понимаю за что отвечает данный код - можешь открыть issue на гитхабе в phpcent репозитории, потому что здесь все-таки не место как мне кажется для подобных вопросов? в issue нужно хотя бы описать что должен делать этот код, для чего он вообще. можно в гиттер-чатик если удобнее - https://gitter.im/centrifugal/centrifugo
FZambia: а названия каналов по приватным чатам лучше генерировать таким образом?
Отправитель: ID 1
Получатель: ID 2,3,4,5
Общий канал с названием: 1-2-3-4-5
carroll: смотря какой механизм приватности используется. Я не вижу тут ни решетки, ни знака доллара в начале канала - то есть по большому счету на этот канал сможет любой подписаться. Расскажу, как у нас были организованы приватные чаты. Диалог был между 2-мя пользователями. Комната была сущностью в базе данных с уникальным длинным id, который невозможно подобрать за разумное время. Когда кто-то писал в чат комнаты, мы знали id комнаты. Этот id был в имени канала - что-то вроде room_cehfwif87wyf8wtf8t4f6n3468nf648ft843fnt438fn8 в итоге получалось. Соответственно передавали эти имена только тем клиентам которые могли на них подписаться, другие не знали. И когда кто-то писал новое сообщения в чатик - просто публиковали его в этот канал. Другие механизмы приватности я описывал выше - это каналы с решеткой (#) или приватные каналы (с $ в начале).
С аутентефикаций наладил, вроде все корректно работает. Отдает: {"$pm2202293a6f3913e7e04bc3e42099fe58":{"sign":"7bb8313a20803e7f2a944ad6fa98835c22e093221d2e8911d8ef9ad36ae02127"}}
но вообще публиковать новые сообщения нужно с бекенда через API, а не напрямую с клиента
А почему лучше с бекенда ?
Например, в чате когда клиент написал сообщение, его ведь лучше сразу напрямую послать по WS в центрифугу и остальные его быстрее получат. Разве нет ?
ramax495, дизайн Центрифуги таков, что она работает в отвязке от бизнес-логики приложения, по сути являясь лишь транспортом реал-тайм сообщений от бекенда клиенту. Но не в обратную сторону - так как в таком случае ваш бекенд не узнает о сообщении ничего (оно просто пройдет сквозь Центрифугу до других юзеров в канале). А что если вам нужно его валидировать, сохранить в базу данных, затроттлить? поэтому в большинстве случаев новое сообщение нужно сначала отправить на бекенд для выполнения всех действий необходимых приложению - и только потом разослать потенциальным получателям в канал. В теории можно было бы отправлять его по вебсокету в Центрифугу, которая потом делала запрос в бекенд по HTTP, например. Но на самом деле в такой схеме выгода не очевидна - сообщение проходит лишнее звено.