Rails 怎样使用 websocket 建立 one to one 的聊天,类似 ruby-china 的推送

flowerwrong · 2014年11月03日 · 最后由 maolan 回复于 2017年11月06日 · 9369 次阅读

我最近在学习 websocket-rails (https://github.com/websocket-rails/websocket-rails), 可以用来建立多人聊天室,但是不知道怎么建立 one to one 的聊天,类似 qq 的私人聊天,我没有找到相关的 api. 请指点下,谢谢。


socket.io 我也看了下,他有提供private message这个 event,可以监听fromto.


ruby-china 的通知机制也类似,推送到个人。于是我参考了 ruby-china 的代码,发现是用的 faye-rails (https://github.com/jamesotron/faye-rails).

主要逻辑如下:
服务器:
# https://github.com/ruby-china/ruby-china/blob/master/app/models/notification/base.rb#L20
# coding: utf-8
class Notification::Base
  store_in collection: 'notifications'

  field :read, default: false
  belongs_to :user

  index read: 1
  index user_id: 1, read: 1

  scope :unread, -> { where(read: false) }

  after_create :realtime_push_to_client
  after_update :realtime_push_to_client

  def realtime_push_to_client
    if self.user
      hash = self.notify_hash
      hash[:count] = self.user.notifications.unread.count
      FayeClient.send("/notifications_count/#{self.user.temp_access_token}", hash) # 注意self.user.temp_access_token 和 FayeClient
    end
  end
end

# https://github.com/ruby-china/ruby-china/blob/55a3b35d9bd3db028221a4b6470a2f4e04c802dd/app/models/faye_client.rb
# faye_client.rb
require 'net/http'
class FayeClient
  def self.send(channel, params)
    Thread.new {
      params[:token] = Setting.faye_token
      message = {channel: channel, data: params}
      uri = URI.parse(Setting.faye_server)
      Net::HTTP.post_form(uri, message: message.to_json)
    }
  end
end

# user model
def temp_access_token
  Rails.cache.fetch("user-#{self.id}-temp_access_token-#{Time.now.strftime("%Y%m%d")}") do
    SecureRandom.hex
  end
end
客户端:
initNotificationSubscribe : () ->
  return if not App.access_token?
  faye = new Faye.Client(App.faye_client_url)
  faye.subscribe "/notifications_count/#{App.access_token}", (json) ->
    span = $("#user_notifications_count span")
    new_title = document.title.replace(/^\(\d+\) /,'')
    if json.count > 0
      span.addClass("badge-error")
      new_title = "(#{json.count}) #{new_title}"
      url = App.fixUrlDash("#{App.root_url}#{json.content_path}")
      console.log url
      $.notifier.notify("",json.title,json.content,url)
    else
      span.removeClass("badge-error")
    span.text(json.count)
    document.title = new_title
  true

具体来说通过 token 来区分和维护多个 channel, 即每一个用户和 server 之间酒有一个 channel, 不过 faye-rails 不是走的 websocket. 见官网Faye is a publish-subscribe messaging system based on the Bayeux protocol. 但是官方的架构有说:

# http://faye.jcoglan.com/architecture.html
    Persistent connections using WebSocket
    Long-polling via HTTP POST
    Cross Origin Resource Sharing
    Callback-polling via JSON-P

这也是我疑惑不解的地方。可能作者加了 websocket 部分的实现。

Since version 0.5, Faye has supported WebSockets as a network transport for sending messages to the browser. The code that handles this is decoupled from the rest of the library and can be used to make your own WebSocket applications.

These classes are available as a stand-alone library, faye-websocket
推荐文章:

https://github.com/DavidEGrayson/websocket-chat/blob/master/rws1/server.rb

https://blog.engineyard.com/2013/getting-started-with-ruby-and-websockets

http://www.jianshu.com/p/64114b220410

可以看看 RabbitMQ 提供多种消息推送模式 http://www.rabbitmq.com/getstarted.html

bunny 这个 ruby 实现的客户端也超简单 http://rubybunny.info/articles/getting_started.html

通过 STOMP 插件 配合 sockjs 非常方便 http://www.rabbitmq.com/stomp.html https://github.com/sockjs/sockjs-client

#1 楼 @zhang_soledad 谢谢。我的目的其实不是推送。而是 one to one 的聊天。如果用 pub/sub 实现那就是 1:1,我觉得这样做不值得。换句话说,我想用 websocket 或者 amqp 等其他一些协议来实现 1 对 1 的聊天。

one to one 的聊天,用 channel 的意义不大吧,可以直接收到 A 的消息,然后转发给 B 的。

#3 楼 @42thcoder 可以直接收到 A 的消息, 然后转发给 B 的. 这里就是关键,我可能转发给 B, 也可能转发给 BCDEF......问题就是怎么去转发,我可以识别是哪个发来的 (在 message 里面带 username).但是我看到的 websocket 实现除了 socket.io 有fromto,其他都没有。

#4 楼 @flowerwrong 维护一个在线用户列表,每个用户可能有多个 connection. 转发给 B, 就是往 B 的所有 connection 中发消息. 可以看源码,文档未必有写清楚。

#4 楼 @flowerwrong

websocket-rails 中可以这样写:

WebsocketRails.users[receiver.id].send_message :new_message, { content: 'Hey there~' }

@flowerwrong @42thcoder 你们好,请问,websocket-rails 有在生产环境正式的用起吗?这东西靠谱吗?方便的话,还请指点一下。谢谢!

我觉得这种东西自己开发太麻烦了,就别自己捣鼓了,找个第三方,方便,GoEasy 就挺不错的,我昨天试了一下,代码简洁易懂,几分钟我就洗了一个自己的实时推送功能;官网:http://goeasy.io/

需要 登录 后方可回复, 如果你还没有账号请 注册新账号