分享 用 ActionCable + Redis 实现在线人数统计功能

Zegendary · November 30, 2017 · Last by Zegendary replied at November 30, 2017 · 2910 hits

最近在项目中做了个直播 + 在线聊天的功能,然后老大希望直播时可以看到在线人数。

相关文章比较少,于是爬了两天坑,想了个比较简单的方案:

  1. 服务端通过periodically :collect_online_users, every: 10.secondschannel每 10 秒发布collect_online_users的动作
  2. 客户端收到collect_online_users发布的消息后通过 perform("save_user_id") 调用服务端save_user_id方法,把id存入缓存中
  3. 服务端通过periodically :boardcast_user_numbers, every: 1.minutes 每分钟发布在线用户数
  4. 客户端接收到用户数并更新

具体实现

  1. 配置 redis 参考:使用 Redis 处理 Rails Model 缓存

  2. 服务端代码

# app/channels/live_channel.rb 
class LiveChannel < ApplicationCable::Channel
  periodically :boardcast_user_numbers, every: 1.minutes
  periodically :collect_online_users, every: 10.seconds
  after_subscribe :boardcast_user_numbers

  def subscribed
    stream_for live
  end

  def unsubscribed
  end

  def save_user_id
    if $redis_user_attendances.get(live.id).nil?
      $redis_user_attendances.set live.id, [current_user.id].to_json
    else
      users = JSON.parse($redis_user_attendances.get(live.id))
      users.push(current_user.id)
      $redis_user_attendances.set live.id, users.to_json
    end
  end

  def collect_online_users
    LiveChannel.broadcast_to live, type: "comfirm_online"
  end

  def boardcast_user_numbers
    users = JSON.parse($redis_user_attendances.get(live.id))
    count = users.uniq.length
    $redis_user_attendances.set(live.id,"[]") //清空redis缓存
    LiveChannel.broadcast_to live, {type: "update_online_user_number",data:count}
  end

  private
  def live
    Live.find(params[:id])
  end
end
  1. 客户端代码 (vue)
let vm = this
this.$cable.subscriptions.create({
  channel: 'LiveChannel',
  id: this.live.id
}, {
  received: function ({type, data}) {
    if (type == "update_online_user_number") {
      vm.user_online_numbers = data //记录用户数
    } else if (type == "comfirm_online"){
      this.perform("save_user_id")
    }
  }
})

功能比较简单 就是记录在线人数而已 😂

本来想用 ActionCable 提供的 subscribedunsubscribed来记录,但是调试过程中发现 unsubscribed 并不可靠,服务器不会每次都能发现取消订阅的连接

个人感觉应该反向 客户端每 10s 发送一个 perform("save_user_id") 然后服务端 redis 里面设置一个 12s 过期的 key。。。。就行了 服务器批量下发没什么意义啊在这个场景里(个人感觉)

this.$cable.subscriptions 这个怎么实现的啊?

Reply to menghuanwd
Vue.use({
  install(V) {
    V.prototype.$cable = ActionCable.createConsumer()
  }
})
Reply to hging

有道理,你这么一说是感觉没必要

竟然可以使用actioncable,你们是服务端渲染的吗?还是完全分离了用的包呀。

Reply to didmehh

服务端渲染 layout,客户端渲染数据交互部分,客户端用的actioncable

You need to Sign in before reply, if you don't have an account, please Sign up first.