Допустим, есть сайт с множеством пользователей онлайн. На сайте реализована система уведомлений. В таблице уведомлений у каждой записи есть флаг status (значение 0 обозначает непрочитанное уведомление, значение 1 означает прочитанное). Как сделать постоянную проверку на наличие новых уведомлений, чтобы вывести количество новых уведомлений где-нибудь на сайте?
Задумываюсь о ajax запросах раз в секунду, но думаю с немалым онлайном будет большая нагрузка на сервер.
Пробовал использовать websocket с демоном на php: не смог реализовать отправку уведомлений для конкретного пользователя (сообщение отправляется для всех пользователей онлайн).
Есть ли какие-нибудь способы реализовать такую идею безобидным образом?
Использую PHP на стороне сервера, так что node.js и тп не предлагать.
Ratchet + websockets + fallback в long pooling (все на базе reactphp сделать и связать с основным приложением через очередь сообщений на rabbitmq/activemq/zeromq/resq/etc либо своими кастылями с базой данных). То что у вас отправлялось всем пользователем это лично ваша проблема - значит вы не разобрались как работать с websockets. У вас соединения должны быть привязаны к пользователям.
ratchet не хочу использовать, т.к. не хочу лишнее прикручивать. и вправду, с сокетами не разобрался. Но где есть нормальная краткая литература по работе с сокетами на php?
@uranus235 работа с сокетами - такая же как и работа с сокетами везде. У вас есть соединение - составляете массив оных с id пользователя к примеру в качестве ключа. То есть когда приходит новое соединение, производим аунтефикацию оного (то есть выясняем кто к нам стучится), и добавляем в массивчик. отправлять принимать - в рамках соединения идет, можно разруливать через stream_select например (в зависимости от реализации) какие соединения содержат данные для чтения а какие нет а какие отвалились.
@uranus235 можно но зачем? Писать очередной велосипед? Если собираетесь выкатываться в продакшен - лучше взять ReactPHP + ratchet. Он по надежнее phpdeamon да и у последнего несколько другое предназначение.
И вообще, мне казалось развернуть ratchet проще чем разбираться с phpdeamon.
@Fesor каждый пользователь добавляется в массив, это однозначно. Но а что если я хочу использовать сокет для переписки аля вконтакте? У меня в базе все пользователи по id прописаны а не ip. При отправке сообщения от 1-го пользователя 2-у, это сообщение должны увидеть только эти 2 пользователя. Как теперь сокету гадать какие у этих пользователей ip?
@uranus235 вопервых, когда вы открываете соединение. вам извесны IP. Во вторых, давайте обрисуем простенький чатик, статей по которым уже просто тьма.
У нас есть наш скрипт, который принимает соединения и держит их в массивчике
userid => connection.
У нас есть так же цикл который постоянно мониторит соединения на предмет наличия данных для чтения. Например соединение для пользователя Bob имеет что-то на чтение. Считываем. Bob (а точнее клиент который Bob использует) прислал сообщение для пользователя Nick: {"message": "Hi, Nick", "sendTo": 154}
где 154 id ника.
можем записать сообщение в базу (или отправить в очередь что бы не грузить довольно чувствительный к затупам и блокировкам скрипт) и проверить, есть ли у нас соединение для пользователя с id 154 - есть - отправляем. Нет, забиваем - обрабатываем следующее соединение.
ratchet реализует основные вещи для удобной работы с websockets.
@uranus235 по ip вы определяете endpoint (оконечная точка, компьютер пользователя), вам нужно как-то потом это новое соединение к пользователю привязать. У вас на одном компе и с одного ip может быть несколько соединений и несколько пользователей. Просто когда клиент устанавливает соединение, он отправляет сразу и какието данные, типа свой токен выданный ему при авторизации.
Вообще забудьте про ip, его знает соединение и операционная система, вам знать что куда отправляется не нужно. Как организовывать аунтефикацию соединений и привязку их к пользователям вашего приложения/сайта решать вам. Фишка ratchet в том что он берет на себя отслеживание состояний соединений и т.д. вам только события эти разруливать нужно. скажем новое соединение было открыто - вызывается ивент onConnect к примеру, в него передается новоиспеченное соединение. Грубо говоря мы можем положить его сначала в список не авторизованных соединений и добавить для секьюрности таймстамп когда это дело было получено (затем можно будет просроченные соединения удалять, если от клиента не поступило никакой инфы скажем за 10 секунд).
при onMessage для соединения, которое еще небыло авторизовано, мы ожидаем получать данные для аунтефикации пользователя. Проверяем, если все хорошо - переносим из списка не авторизованных в список авторизованных с привязкой к id пользователя. Теперь пользователь онлайн, мы можем ему чего отправлять и все такое. Если же не все ок и что-то пошло не так - закрываем соединение (можем перед этим еще и ошибку какую послать). Клиент должен это дело уже сам разруливать.
Если не хотите поднимать комет сервер у себя воспользуйтись одним из Saas решений.
Вот несколько comet сервисов.
[Ru] comet-server.ru
[En] https://www.tambur.io
[En] pusher.com