В настоящее время я работаю над веб-приложением, в котором необходимо видеть всех людей команды, которые находятся в сети.
Последний просмотренный столбец
Более простое решение — добавить столбец «last_seen» в пользовательскую таблицу и обновлять отметку времени каждый раз, когда пользователь совершает действие. В Ruby on Rails я сделаю что-то вроде этого:
class ApplicationController < ActionController::Base before_action :save_last_seen protected def save_last_seen if current_user current_user.last_seen=Time.now current_user.save end end
Итак, чтобы узнать, находится ли пользователь в сети или нет, мы можем проверить, меньше ли дата last_seen X минут.
Плюсы:
- Вам не нужно реализовывать какую-либо другую логику, у вас уже есть все в пользовательской таблице. В представлении выполните итерацию для каждого пользователя, и вы готовы к работе.
Минусы:
- Каждый раз, когда ваш пользователь звонит на сервер, происходит запись в базу данных. Это не проблема для прототипа, но может быть для рабочего приложения, которым ежедневно пользуется множество людей. Способ решить эту проблему — сохранить значение last_seen в сеансе и обновить его только в том случае, если last_seen станет старше X минут.
- У вас нет точного способа узнать, когда пользователь покидает страницу. Что, если пользователь читает очень длинную статью или комментарий, и это занимает больше X минут? Для системы этот пользователь будет не в сети.
Псевдо-реальное время с Redis
Redis почти всегда является правильным решением. Вы можете использовать список Redis и указывать идентификатор пользователя при каждом его действии. Используя встроенный срок действия (установленный на Time.now+X.minutes), мы можем забыть проверить, находится ли пользователь в сети или нет: список всегда будет содержать только идентификаторы пользователей, которые совершили какие-либо действия за последние X минут.
Вставка Redis также выполняется быстрее, чем обновление SQL (все операции вставки Redis имеют сложность O(1)), поэтому вы можете забыть о проблемах с производительностью или настроить сеанс.
Мы до сих пор не решили вторую проблему: мы не знаем, где именно пользователь покидает наше приложение. Для этого нам нужно что-то не только на заднем конце, но и на переднем.
Толкатель к спасению
Правильное решение — использовать соединение через сокет, которое отправляет сообщение каждому клиенту, когда новый пользователь подключается и/или отключается. Это несложно сделать, и я собирался реализовать, когда обнаружил, что Pusher уже это предоставляет.
Pusher — это замечательная SaaS, которая обеспечивает связь в режиме реального времени через сокетное соединение примерно за 30 секунд.
Хотя я уже использовал pusher в своем веб-приложении для синхронизации в реальном времени между разными пользователями, я не знал, что они предоставляют канал «присутствия», правильный канал, который позволяет узнать статус нового пользователя. Его также довольно легко использовать!
Сначала вам нужно немного изменить вашу нынешнюю инициализацию толкателя:
window.pusher = new Pusher(PUSHER_TOKEN, { cluster: 'eu', authEndpoint: '/presence/auth', encrypted: true, auth: { headers: { 'X-CSRF-Token': "<%= form_authenticity_token %>" } } });
и предоставьте действие на вашем сервере, чтобы позволить Pusher узнать, аутентифицирован ли пользователь или нет, и передать некоторую информацию о пользователе (например, идентификатор пользователя, имя или аватар).
def presence_auth if current_user response = Pusher.authenticate( params[:channel_name], params[:socket_id], { user_id: current_user.id, user_info: { name: current_user.name, id: current_user.id } } ) render json: response else render text: 'Forbidden', status: '403' end end
Затем создайте новый канал, имя которого должно начинаться с «presence-». Это скажет толкателю, что вам нужен канал присутствия.
var presenceChannel = pusher.subscribe("presence-XXX");
Мы почти закончили: теперь привяжите канал к этому событию, и все готово!
presenceChannel.bind( "pusher:subscription_succeeded", function(members){ console.log("the current members are", members) } ); presenceChannel.bind( "pusher:member_added", function(member){ console.log("A new member is online", member) } ); presenceChannel.bind( "pusher:member_removed", function(member){ console.log("This member is now offline", member) } );