thisismydesign
thisismydesign

Reputation: 25082

How to connect to multiple WebSockets with Ruby?

Using faye-websocket and EventMachine the code looks very similar to faye-websocket's client example:

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

I'd like to have multiple connections open parallely. I can't simply call setup_socket multiple times as the execution won't exit the EM.run clause. I've tried to run setup_socket multiple times in separate threads as:

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

But it doesn't seem to do anyhting as the puts statements don't reach the output.

I'm not restricted to use faye-websocket but it seemed most people use this library. If possible I'd like to avoid multithreading. I'd also not like to lose the possiblity to make changes (e.g. add a new websocket) over time. Therefore moving the iteration of URLs inside the EM.run clause is not desired but instead starting multiple EMs would be more beneficial. I found an example for starting multiple servers via EM in a very clean way. I'm looking for something similar.

How can I connect to multiple WebSockets at the same time?

Upvotes: 3

Views: 1351

Answers (2)

Myst
Myst

Reputation: 19221

Here's another way to do it... Use Iodine's native websocket support (or the Plezi framework) instead of em-websocket...

...I'm biased (I'm the author), but I think they make it a lot easier. Also, Plezi offers automatic scaling with Redis, so it's easy to grow.

Here's an example using Plezi, where each Controller acts like a channel, with it's own URL and Websocket callback (although I think Plezi's Auto-Dispatch is easier than the lower level on_message callback). This code can be placed in a config.ru file:

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.

I wrote this answer also because em-websocket might fail or hog resources in some cases. I'm not sure about the details, but it was noted both on the websocket-shootout benchmark and the AnyCable Websocket Benchmarks.

Upvotes: 0

Casper
Casper

Reputation: 34308

Here's one way to do it.

First, you have to accept that the EM thread needs to be running. Without this thread you won't be able to process any current connections. So you just can't get around that.

Then, in order to add new URLs to the EM thread you then need some way to communicate from the main thread to the EM thread, so you can tell it to launch a new connection. This can be done with EventMachine::Channel.

So what we can build now is something like this:

@channel = EventMachine::Channel.new

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

Then in the main thread, any time you want to add a new URL to the event loop, you just use this:

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

Upvotes: 3

Related Questions