Reputation: 9895
How can I send data to a WebSocket using Ruby in a Background Process?
Background
I already have a separate ruby file running a Websocket server using the websocket-eventmachine-server
gem. However, within my Rails application, I want to send data to the websocket in a background task.
Here is my WebSocket server:
EM.run do
trap('TERM') { stop }
trap('INT') { stop }
WebSocket::EventMachine::Server.start(host: options[:host], port: options[:port]) do |ws|
ws.onopen do
puts 'Client connected'
end
ws.onmessage do |msg, type|
ws.send msg, type: type
end
ws.onclose do
puts 'Client disconnected'
end
end
def stop
puts 'Terminating WebSocket Server'
EventMachine.stop
end
end
However, in my background process (I'm using Sidekiq), I'm not sure how to connect to the WebSocket and send data to it.
Here's my Sidekiq worker:
class MyWorker
include Sidekiq::Worker
def perform(command)
100.times do |i|
# Send 'I am on #{i}' to the Websocket
end
end
end
I was hoping to be able to do something like EventMachine::WebSocket.send 'My message!'
but I don't see an API for that or something similar. What is the correct way to send data to a WebSocket in Ruby?
Upvotes: 4
Views: 4181
Reputation: 19221
Accepted Answer:
Af your keeping your current websocket server:
You can use Iodine as a simple websocket client for testing. It runs background tasks using it's own reactor pattern based code and has a websocket client (I'm biased, I'm the author).
You could do something like this:
require 'iodine/http'
Iodine.protocol = :timers
# force Iodine to start immediately
Iodine.force_start!
options = {}
options[:on_message] = Proc.new {|data| puts data}
100.times do |i|
options[:on_open] = Proc.new {write "I am number #{i}"}
Iodine.run do
Iodine::Http.ws_connect('ws://localhost:3000', options)
end
end
P.S.
I would recommend using a framework, such as Plezi, for your websockets (I'm the author). Some frameworks let you run their code within a Rails/Sinatra app (Plezi does that and I think Faye, although not strictly a framework, does that too).
Using EM directly is quite hardcore and there are a lot of things to manage when dealing with Websockets, which a good framework helps you manage.
EDIT 3:
Iodine WebSocket client connections are (re)supported starting with Iodine 0.7.17, including TLS connections when OpenSSL >= 1.1.0
.
The following code is an updated version of the original answer:
require 'iodine'
class MyClient
def on_open connection
connection.subscribe :updates
puts "Connected"
end
def on_message connection, data
puts data
end
def on_close connection
# auto-reconnect after 250ms.
puts "Connection lost, re-connecting in 250ms"
Iodine.run_after(250) { MyClient.connect }
end
def self.connect
Iodine.connect(url: "ws://localhost:3000/path", handler: MyClient.new)
end
end
Iodine.threads = 1
Iodine.defer { MyClient.connect if Iodine.master? }
Thread.new { Iodine.start }
100.times {|i| Iodine.publish :updates, "I am number #{i}" }
EDIT 2:
This answer in now outdated, since Iodine 0.2.x doesn't include a client any longer. Use Iodine 0.1.x or a different gem for websocket clients.
Upvotes: 3
Reputation: 20116
websocket-eventmachine-server
is a websockets server.
If you want to connect to a websocket server using ruby, you can do it with some gems, like
https://github.com/igrigorik/em-websocket
: Both server and client, also based on eventmachine.
ruby-websocket-client
: Client only
Upvotes: 1