Какая логика работы в Socket.io с несколькими вкладками логичней?
Приветствую коллеги, перечитал уже 3 десятка страниц, но так и не нашел более-менее правильного на мой взгляд решения.
Входящие данные:
Веб-сервер NGINX
Вся логика написана на PHP
nodejs + socket.io использую для работы с веб-сокетами
Проект - самописная CRM система.
Задача выглядит таким образом:
1) За каждым менеджером закреплен конкретный заказ с указанным телефоном клиента. При поступлении звонка на АТС, я отлавливаю номер звонящего PHP'шкой, определяю что это за заказ и какому менеджеру он принадлежит.
2. Как должно быть) В socket.io летит JSON с этой инфой, он в свою очередь передает инфу в браузер, где у менеджера сразу же открывается новая вкладка с этим заказом.
2. Как сейчас (dev mode) - если у менеджера открыто 3 вкладки, то будет открыто 3 вкладки новых автоматически с этим заказом. Если открыто 15, а менеджеры иногда так делают при прозвоне, то откроется 15 дополнительно.
Как я могу это реализовать, но это какой-то костыльный вариант.
1) При первом входе в систему в socket-io загружается инфа о менеджере где ключом будет его PHPSESSID. Тогда при каждом открытии странице не придётся foreach'ить весь массив, а достаточно будет проверить есть ли уже в массиве users['текущий PHPSESSID'].
2) Но тут возникает иная проблема, Каждый раз когда нужно будет определить какому менеджеру отправить сообщение, нам придётся бегать по циклу и на втором уровне вложенности сверять manager_id пришедший от php с подключенными.
3) Как сделать так, чтобы на каждый запрос открывалась только 1 вкладка? При каждом установлении соединения клиента с socket.io писать в одно из "свойств" текущего менеджера на какую последнюю страницу он заходил, и сверять location.path+location.search с тем что в адресной строке браузера. Почему костыль? Потому, что строка запроса может быть символов 200, а то и под 800 и построчное сравнение совсем не в тему.
Может есть уже готовое решение для данной проблемы? Какой то "socket.io курильщика" получается.
Я бы проблему решал на клиенте, нужно реализовать "общение" между вкладками и всегда проверять, чтобы соединение с сокет-сервером держала только одна вкладка.
Далее если человек переключает вкладки - тогда только активная вкладка соединяется с сокет-сервером, все остальные соединение обрывают. Если человек ушел на сторонный ресурс, значит соединение будет только у последней активной вкладки.
Вот примерно о таком варианте, я и думал. В таком случае, моя проблема решается очень просто.
Тогда у меня будет доп. вопрос, а насколько логично, постоянно разрывать и устанавливать соединения? Не будет ли приложение работать быстрее, если во всех вкладках открыты соединения и я их не разрываю при переходе между вкладками? А как это на VDSке скажется? Насколько это может увеличить/снизить нагрузку на неё? На данный момент системой пользуется пара десятков человек, а когда их будет несколько тысяч? В этом же и есть смысл сокетов, что соединение открыто и дальше у нас нет практически никаких "накладных расходов".
Роман: На самом деле "практически нет накладных расходов" - это неправда. Ну если представить другой вариант, что вы производите какие-нибудь манипуляции на сервере для определения источника, тогда получается следующая картина:
1. Ваш сайт использует 10 000 человек.
2. У каждого человека в серднем открыто 2 вкладки
3. Таким образом у вас открыто 20 000 соединений, т.е. вы искусственно увеличиваете количество соединений.
4. На сервере есть какой-то скрипт, который будет соединяться с базой, например, чтобы определить, кто это: пользователь или вкладка, что тоже создаст накладные расходы.
Так что мое сугубо личное мнение дисконнекты выгоднее в этом плане, ну и опять же это будет не ajax, который каждую секунду будет долбить ваш сервер и спрашивать как дела.
1) Если 1 обращение к базе при каждому из 20 тыс. соединений, тут согласен, а если мы берём что то элементарное, например broadcasting и то редко?
2) Есть ли какие либо практические рекомендации вообще для работы с сокетами, типа когда нужно разрывать соединение, а когда можно плодить сколько угодно?
3) Можно ли к одному сокету подключить 1-10 млн. клиентов (на 1 порт)? Вопрос в плане, поддерживает это технология и каким образом это даст нагрузку на сервер? Если мы работаем с http тут понятно как просчитать кол-во запросов в секунду, с сокетами не понятно)
Роман: просто возьмите и протестируйте. Напишите сервер, напишите клиентский скрипт имитирующий сначала 1000 соединений, потом 500 соединение, но с пересоединением, замерьте нагрузку. Напишите статью по этому поводу, может кому-то тоже интересно. ;)
Артем Кисленко: с предложенной вами реализацией возникли сложности, которые не ясно как решать.
Отключение и подключение сокета можно сделать при onblur, onfocus, тут вопросов нет.
У нас всегда должна быть открыта всего 1 вкладка с сайта и 1 соединение, даже если человек уходит на другой сайт или сворачивает браузер.
Следовательно нужно добавить проверку на то, последняя ли это вкладка и нужно ли засчитывать onblur и разрывать соединения.
Единственный возможный вариант писать и читать кол-во вкладок с помощью localStorage, но это работает не корректно, если юзер открывает сразу несколько вкладок, т.к. они сразу же не успели все загрузиться, то и значения которые будут браться на localStorage пересекаются, и если у нас было 4 вкладки и мы быстро откроем 10, то у всех у них по окончании загрузки выведется цифра 5. :((
Роман: Я, конечно, не понимаю всех деталей. Но почему-то мне, кажется, что можно делать действия (что-то считывать, устанавливать соединение и т.д.), только после полной загрузки страницы, это возможно?
Артем Кисленко: https://gist.github.com/anonymous/d3680602c7d91c0d...
пример клиентской части. В данной реализации почти всё ОК, все соединения разрываются, если мы закрываем вкладки. Сейчас каким то образом нужно сделать так, что бы на одной из вкладок оно не разрывалось. То что вы предлагаете не работает, т.к. если мы быстро откроем вкладки, то они возьмут одно и то же значение из локальногоХранилища, т.к. страницы ещё не загружены и каждая из них не успели обновить значение.