Спасибо тебе Максим огромное! Ежели бы не ты, сидел бы мой сервачок на пэхапэ и дальше
В общем то, всю "ненужную" часть чатилки я реализовал (ну там добавление в друзья, группы, перенос в группы, удаление, просмотр информации, голосование за принятие в сеть и т.п.). Теперь нужно организовывать сами комнаты и переписку в них. Собственно, пока что я для реализации "онлайн"-переписки остановился на библиотеке socket.io, понравилась она мне своей простотой и мощью.
Для начала, потренироваться в обращении с библиотекой, я решил сделать некое подобие онлайн-оффлайн для пользователей как это было в Аське. Т.е. вошёл - тебе присвоился и отправился всем кому надо статус "онлайн", вышел - соответственно "оффлайн". И вот у меня возник вопрос - а как отправлять данные на нужные сокеты (т.е. только тем у кого ты в друзьях)?
Я сейчас реализовал это ровно так, как вижу сам.
Для начала, моё "api" разделилось на 2 части - часть основанная на чистой node.js (всякая мишура для обвязки чата) и часть использующая socket.io (онлайн-офлайн, комнаты и переписка).
Далее, я каждому пользователю добавил "скрытое" поле
in_friend, которое содержит всех кто добавил его в друзья. Сделал я это исключительно из соображений скорости - пробежаться по одному массиву/объекту явно быстрее, чем перебрать всю базу и посмотреть у кого же наш вошедший пользователь в друзьях. Собственно, тут всё просто. А вот дальше началась жесть.
Для того что бы запоминать сокеты для клиентов я создал массив
sockets, в который вносятся новые сокеты или обновляются старые при обращении через socket.io. Т.е. массив не содержит данных больше, чем должен содержать. Сравнение "старый-новый" происходит по моему собственному полю _id, которое я внёс в объект session у сокетов. Если сокет с сессией содержащей _id уже есть - заменяем на новый, на данном этапе это не критично, потом, конечно, может не кисло так аукнуться (в конце расписал почему), но будем разбираться с проблемами по мере их поступления, сейчас не об этом.
Так вот, собственно в сокетах реализовал два события, online и offline, которые берут из базы инфу о пользователе (id пользователя передаётся вместе с событием online/offline), достают из поля in_friend все id тех у кого пользователь состои в друзьях, потом я пробегаюсь по массиву с друзьями и внутри по массиву с сокетами, найдя нужный сокет отправляю соответствующее событие тем кого найду в этом массиве. Смущает один момент. Пока количество сокетов крайне мало (я вот имею возможность тестить на 5-6 машинах), перебор массива не представляет опасности. А если сокетов в массиве будет тысяча? Не уничтожит ли это мой сервер, если одновременно событие online/offline передадут хотя бы 20 человек, каждый из которых, допустим, состоит в друзьях у 40 человек? Ведь это уже будет фактически 40*1000*20 = 800000 циклов с отправкой данных по сокетам, что просто нереально дофига на такое маленькое событие как передача статуса online/offline. И точно такое же количество циклов будет и на приглашение в комнату, и на отправку сообщения. Т.е. нагрузка будет очень и очень недурственная только из за такого моего подхода к сокетам.
зыЖ Подсчёт количества циклов очень грубый, у меня стоит break в цикле с сокетами после совпадения _id и отправки статуса. Так как у меня сокеты с _id пользователя не повторяются, то этот подход с выходом из цикла вполне применим, как мне кажется. Но этот же подход накладывает очень существенное ограничение - один пользователь будет сидеть только с одного клиента, причём где зашёл последним, там и будет работать всё, старые сокеты будут замещены новым.
зыыЖ Забыл упомянуть, изредка клиентом посылается "alive"-пакет (раз в 60 секунд сейчас сделал), который записывает отославшему пользователю статус online в базе данных и записывает время получения этого пакета. При запросе списка друзей другими пользователями (у кого в списке есть наш пользователь) идёт проверка дат пакетов "alive", если разница между текущей датой и датой "alive" > 60 секунд - пользователю в базе записывается статус offline. Работает
Но, опять же, вопрос возможной нагрузки стоИт остро.
зыыыЖ Сейчас пришла в голову идея хранить все живые сокеты пользователя в базе данных, допустим в поле sockets. Это должно избавить от перебора общего огромного массива сокетов, ведь нужные сокеты (а сокет же в данном случае простой JS объект?) мы сможем достать только лишь опираясь на массив in_friends текущего пользователя. Надо будет попробовать так сделать.