如何使用 Ruby 连接到多个 WebSockets?

How to connect to multiple WebSockets with Ruby?

使用 faye-websocket 和 EventMachine,代码看起来与 faye-websocket 的客户端示例非常相似:

require 'faye/websocket'
require 'eventmachine'

def setup_socket(url)
    EM.run {
      ws = Faye::WebSocket::Client.new(url)

      ws.on :open do ... end

      ws.on :message do ... end

      ws.on :close do ... end
    }
end

我想同时打开多个连接。我不能简单地多次调用 setup_socket,因为执行不会退出 EM.run 子句。我已经尝试在不同的线程中多次 运行 setup_socket 作为:

urls.each do |url|
    Thread.new { setup_socket(url) }
end

但它似乎没有做任何事情,因为 puts 语句没有到达输出。

我并不局限于使用 faye-websocket,但似乎大多数人都使用这个库。如果可能的话,我想避免多线程。我也不希望失去随时间进行更改(例如添加新的 websocket)的可能性。因此,不希望在 EM.run 子句中移动 URL 迭代,而是启动多个 EM 会更有益。我以一种非常干净的方式找到了 example for starting multiple servers via EM。我正在寻找类似的东西。

如何同时连接到多个 WebSockets?

这是一种方法。

首先,您必须接受 EM 线程需要 运行。没有这个线程,您将无法处理任何当前连接。所以你无法解决这个问题。

然后,为了向 EM 线程添加新的 URLs,您需要某种方式从主线程到 EM 线程进行通信,这样您就可以告诉它启动一个新连接。这可以通过 EventMachine::Channel.

来完成

所以我们现在可以构建的是这样的:

@channel = EventMachine::Channel.new

Thread.new {
  EventMachine.run {
     @channel.subscribe { |url| 
       ws = Faye::...new(url)
       ...
    }
  }
}

然后在主线程中,任何时候你想向事件循环添加一个新的URL,你只需使用这个:

def setup_socket(url)
  @channel.push(url)
end

这是另一种方法...使用 Iodine 的原生 websocket 支持(或 Plezi framework)而不是 em-websocket...

...我有偏见(我是作者),但我认为它们使它变得容易得多。此外,Plezi 还提供了 Redis 的自动缩放功能,因此很容易扩展。

这是一个使用 Plezi 的例子,其中每个控制器都像一个通道,有自己的 URL 和 Websocket 回调(尽管我认为 Plezi 的自动调度比较低级别的 on_message 回调更容易).此代码可以放在 config.ru 文件中:

require 'plezi'

# Once controller / channel for all members of the "Red" group
class RedGroup
  def index # HTTP index for the /red URL
    "return the RedGroup client using `:render`".freeze
  end
  # handle websocket messages
  def on_message data
    # in this example, we'll send the data to all the members of the other group.
    BlueGroup.broadcast :handle_message, data
  end
  # This is the method activated by the "broadcast" message
  def handle_message data
    write data # write the data to the client.
  end
end
# the blue group controller / channel
class BlueGroup
  def index # HTTP index for the /blue URL
    "return the BlueGroup client using `:render`".freeze
  end
  # handle websocket messages
  def on_message data
    # in this example, we'll send the data to all the members of the other group.
    RedGroup.broadcast :handle_message, data
  end
  # This is the method activated by the "broadcast" message
  def handle_message data
    write data
  end
end
# the routes
Plezi.route '/red', RedGroup
Plezi.route '/blue', BlueGroup
# Set the Rack application
run Plezi.app

P.S.

我写这个答案也是因为 em-websocket 在某些情况下可能会失败或占用资源。我不确定细节,但在 websocket-shootout benchmark and the AnyCable Websocket Benchmarks.

上都注明了