Еще один способ узнать, кто залогинен на сайте, используя faye + redis

от автора


Проблема

Было приложение, использующее Ruby on Rails, и стандартный набор гемов (вроде devise). На одной из страниц необходимо было выводить информацию о текущих активных пользователях.

Решение

Первой же мыслью было при каждом запросе записывать в текущего юзера время этого самого запроса и, таким образом, зная таймаут сессии, можно было вычислить, кто активен, а кто нет. Но таймаут стоял порядка 15 минут, поэтому если вкладку просто закрыли — то он все еще будет «активным» на протяжении этого времени. Уменьшать таймаут сессии было нельзя. Да и вариант каждый раз обновлять запись в базе выглядел немного костыльно, учитывая, что одновременных активных юзеров было порядка 2к. Из самых быстрых и простых вариантов — реализация используя вебсокеты + redis.

Faye vs WebsocketRails

tldr; В итоге был выбран faye.

Изначальный выбор был предоставлен двумя вариантами. Гугление ответ на вопрос что лучше не дало, поэтому все плюсы и минусы — это то, что удалось накопать из доков и статей.

Плюсов у websocket-rails я не нашел, зато минусы были очевидные: последнее обновление было довольно давно, на каждое подключение открывался отдельный поток, что потенциально могло заддосить наш и так не очень мощный сервер. Faye в свою очередь работает через event machine и полностью асинхронный, плюс постоянно обновляется.

Установка

Gemfile:

gem "hiredis", "~> 0.4.0" gem 'redis' gem 'faye' gem 'faye-rails' 

Настройка

В initializers/redis.rb была добавлена инициализация подключения к redis:

Redis.current = Redis.new(host: 'localhost', port: 6379, driver: :hiredis) 

application.rb

config.middleware.delete Rack::Lock config.middleware.use FayeRails::Middleware, mount: '/faye', timeout: 25 do     map '/active_users' => ActiveUsersController     add_extension(Inc.new) end 

В этом куске происходит подключение faye по урлу ‘/faye’, и указание таймаута, что очень было важно в решении данной задачи. А так же маппинг канала на определенный обработчик, в моем случае это был ActiveUsersController. Так же добавил расширение для файе. Его код выглядит примерно так:

class Inc   def incoming(message, _request, callback)     if message["channel"] == "/active_users"       OnlineUsers.new(message["data"]["id"], message["clientId"]).online!     end     callback.call(message)   end end 

Это дало мне возможность узнавать кто отправил запрос на ‘/faye’. Внутри OnlineUsers было просто добавление id и client_ud (который выдается faye, при коннекте)юзера в редис внутрь хеша, что то вроде:

redis.hset(HASH_KEY, client_id, user_id) 

чтобы можно было достать всех активных просто по ключу хеша.

Так же в контроллере сделал монитор события «unsubscribe», которое по идее должно было срабатывать, когда закрывается вкладка, но на практике срабатывало через раз. Так же срабатывало когда пользователь кликал на логаут и после клика удалял из редиса нашего клиента и по истечении таймаута, когда от клиента не слышно ничего.

channel '/channel_name' do   monitor :unsubscribe do     remove_online_user(client_id)   end end 

На фронте был простой скрипт:

client = new Faye.Client('/faye'); client.subscribe("/active_users", function(message){}) client.publish('/active_users', {id: user_id}); client.disable('autodisconnect'); 

Для faye был поднят отдельный thin сервер, который слушал только порт на котором вещал faye. Таким образом, получилось сделать возможность мониторинга онлайн пользователей с дельтой в 30 секунд.

В итоге, что бы получить список id всех онлайн юзеров достаточно

redis.hgetall(HASH_KEY).values.uniq 

ссылка на оригинал статьи http://habrahabr.ru/post/256471/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *