Кому
- Тем кто на
Ruby on Rails
- Кто желает знать кто из пользователей онлайн, но ещё не задумывался как
- Для кого не проблема использовать
Redis
(по причине хостинга например)
Решение вопроса в сети пользователь или нет — это наверное как правило установка временной метки при обращении пользователя к приложению, а при необходимости узнать его (пользователя) текущий статус — сверка с этой временной меткой. Какой подход выбрать — решать Вам, но тот подход который предлагаю я — прост и не использует SQL
базу данных, вместо этого используется Redis
и одна из его встроенных возможностей — время жизни ключа (expire
).
Собственно реализация
Инит
# config/initializers/redis.rb $redis_onlines = Redis.new
Выше простейший подход, но я рекомендую следующий
# config/initializers/redis.rb $redis_onlines = Redis.new path: "/tmp/redis.sock", db: 15, driver: :hiredis
path: "/tmp/redis.sock"
— использоватьsocket
-подключение, если это возможноdriver: :hiredis
— драйверhiredis
быстрееdb: 15
— использовать определённую базу данных, по умолчанию используется нулевая, но я рекомендую оставить её для тестирования, прикладных задач, чего нибудь другого. Нет проблем в использовании именно нулевой базы данных — суть в том, чтобы она была строго определена под онлайн-пользователей и больше ни для чего.
Gemfile
# Gemfile gem 'redis' gem 'hiredis' # optional
Не забудьте запустить bundle
Устанавливаем в online
current_user
скорее всего уже используется Вами — это тот метод, который возвращает текущего пользователя или nil
— если пользователь не вошёл.
def current_user @current_user ||= User.find_by_id( session[ :user_id ] ) end
# app/controllers/application_controller.rb after_filter :set_online # Для Rails 4 используйте: # after_action :set_online # после каждого запроса выполнить set_online private def set_online if !!current_user # обёртка в pipelined для ускорения, два запроса пойдут как один без ожидания # ответа, использование multi-exec вместо pipelined # не даёт такого прироста в производительности $redis_onlines.pipelined do # не нужно значение, нужен только ключ $redis_onlines.set( current_user.id, nil ) # устанавливаем время жизни ключа - 10 минут, через 10 мину ключ удалиться $redis_onlines.expire( current_user.id, 10*60 ) end end end
В сети?
# app/models/user.rb def online? # если время жизни ключа истекло - то вернёт false, иначе true $redis_onlines.exists( self.id ) end
Небольшой бонус — список онлайн пользователей
# app/cpntrollers/application_controller.rb def all_who_are_in_touch $redis_onlines.keys # => [ "123", "234", "1", "23" ] # вернёт массив с id онлайн пользователей end
На этом и всё
Небольшая переработка для anonymous
Для отслеживания анонимных посетителей (тех кто не зарегистрировался/не вошёл) подход аналогичный, с небольшим дополнением.
# app/controllers/application_controller.rb def set_online if !!current_user $redis_onlines.pipelined do # вошедшему пользователю к ключу добавляем префикс "user:" перед id $redis_onlines.set( "user:#{current_user.id}", nil ) $redis_onlines.expire( "user:#{current_user.id}", 10*60 ) end else $redis_onlines.pipelined do # не вошедшему пользователю добавляем префикс "ip:" и записываем его id адрес $redis_onlines.set( "ip:#{request.remote_ip}", nil ) $redis_onlines.expire( "ip:#{request.remote_ip}", 10*60 ) end end end
# app/models/user.rb def online? $redis_onlines.exists( "user:#{self.id}" ) end
# app/cpntrollers/application_controller.rb # все вошедшие пользователи онлайн (массив с их id) def all_signed_in_in_touch $redis_onlines.scan_each( match: 'user*' ).to_a end # количество не вошедших пользователей онлайн def all_anonymous_in_touch $redis_onlines.scan_each( match: 'ip*' ).to_a.size end # количество всех пользователей онлайн def all_who_are_in_touch $redis_onlines.dbsize end
Ну и совсем чуть чуть по поводу размера базы данных
FLUSHALL
перед этим) и вот этот небольшой скрипт на ruby. Для 9000 онлайн пользователей и 9000 онлайн анонимусов получилось так:
- пустая база данных: 810.75K
- 18000 записей: 3.49M
Аналогично для 65000 + 65000
- 130000 записей: 18.66M
Немножечко ссылок по теме (англ.):
ссылка на оригинал статьи http://habrahabr.ru/post/216047/
Добавить комментарий