I'm studying how to use ZeroMQ together with EventMachine.
To test things out, I wrote a small program in ruby (echo client server) where i used XREQ and XREP sockets. The client application is sending messages to server (consecutive numbers) and getting them back in responce. The interval between sendings is 0.1s.
Everything works... until a certain moment. When current number reaches about 400, server just freezes and doesn't respond to client anymore. I tested this on several computers, and still got that strange issue.
The code is pretty straightforward:
server.rb
require 'rubygems'
require 'bundler/setup'
require 'em-zeromq'
Thread.abort_on_exception = true
ADDRESS = 'tcp://127.0.0.1:2091'
class EMServerHandler
attr_reader :received
def on_readable(socket, messages)
client_identity = messages.shift.copy_out_string #getting client identity from the 1st part of the message
messages.shift #skip the delimeter
messages.each do |m|
msg = m.copy_out_string
puts "server received from #{client_identity}: " + msg
socket.send_msg("#{client_identity}",'',"#{msg}") #echo message back to the client
end
end
end
trap('INT') do
EM::stop()
end
puts "Program started (with zmq #{ZMQ::Util.version.join('.')})."
EM.run do
EventMachine.epoll
ctx = EM::ZeroMQ::Context.new(1)
server = ctx.bind(ZMQ::XREP, ADDRESS, EMServerHandler.new, {:identity => "server"})
end
client.rb
require 'rubygems'
require 'bundler/setup'
require 'em-zeromq'
Thread.abort_on_exception = true
ADDRESS = 'tcp://127.0.0.1:2091'
class EMClientHandler
attr_reader :received
def on_readable(socket, messages)
messages.shift #skip the delimeter
messages.each do |m|
puts "client recieved: " + m.copy_out_string
end
end
end
trap('INT') do
EM::stop()
end
puts "Program started (with zmq #{ZMQ::Util.version.join('.')})."
EM.run do
EventMachine.epoll
ctx = EM::ZeroMQ::Context.new(1)
puts "client"
puts "enter client name >> "
identity = gets.strip
client = ctx.connect(ZMQ::XREQ, ADDRESS, EMClientHandler.new, {:identity => identity})
client.send_msg('', "hello from client #{identity}")
count = 0
EM::PeriodicTimer.new(0.1) do
client.send_msg('', "#{count += 1}")
end
end
Please help me figure out the reason for this.
Your ZeroMQ context is being reaped by the garbage collector.
You need to move your call to EM::ZeroMQ::Context#new outside of the EM loop.
See the README
At last I figured out that this issue only appeared when using ruby 1.9.3p0, so it feels like this is a bug of that version of ruby.
With ruby 1.9.2 everything works like a charm.
Related
im building a small game in ruby to practice programming, so far everything has went well but im trying to implement multiplayer support, i can connect to the server and i can send information but when I try to read form the server it just freezes and my screen goes completely black. and i cant find the cause, ive read the documentation for the gem im using for TCP and i dont know, maybe i missed something, but if any of you have some insight I would really appreciate it
heres the repo if this code isnt enough
https://github.com/jaypitti/ruby-2d-gosu-game
heres the client side code
class Client
include Celluloid::IO
def initialize(server, port)
begin
#socket = TCPSocket.new(server, port)
rescue
$error_message = "Cannot find game server."
end
end
def send_message(message)
#socket.write(message) if #socket
end
def read_message
#socket.readpartial(4096) if #socket
end
end
heres the gameserver
require 'celluloid/autostart'
require 'celluloid/io'
class Server
include Celluloid::IO
finalizer :shutdown
def initialize(host, port)
puts "Starting Server on #{host}:#{port}."
#server = TCPServer.new(host, port)
#objects = Hash.new
#players = Hash.new
async.run
end
def shutdown
#server.close if #server
end
def run
loop { async.handle_connection #server.accept }
end
def handle_connection(socket)
_, port, host = socket.peeraddr
user = "#{host}:#{port}"
puts "#{user} has joined the arena."
loop do
data = socket.readpartial(4096)
data_array = data.split("\n")
if data_array and !data_array.empty?
begin
data_array.each do |row|
message = row.split("|")
if message.size == 10
case message[0]
when 'obj'
#players[user] = message[1..9] unless #players[user]
#objects[message[1]] = message[1..9]
when 'del'
#objects.delete message[1]
end
end
response = String.new
#objects.each_value do |obj|
(response << obj.join("|") << "\n") if obj
end
socket.write response
end
rescue Exception => exception
puts exception.backtrace
end
end # end data
end # end loop
rescue EOFError => err
player = #players[user]
puts "#{player[3]} has left"
#objects.delete player[0]
#players.delete user
socket.close
end
end
server, port = ARGV[0] || "0.0.0.0", ARGV[1] || 1234
supervisor = Server.supervise(server, port.to_i)
trap("INT") do
supervisor.terminate
exit
end
sleep
it just freezes and my screen goes completely black. and i cant find the cause
A good trick you can look at is attaching to your process with either rbspy or rbtrace to see that is going on when it is stuck.
You can also try first reducing dependencies here a bit and doing this with a simple threadpool prior to going full async with celluloid or event machine.
First of all you should not be rescuing Exception all over the place. Wrapping long begin rescue blocks around nested iterators is begging for trouble.
It sounds like a threading issues, memory and/or CPU but that's just a guess. Try to monitor your resources or use some performance checking gems. But for the love of Satoshi Nakamoto, please write some test coverage and see your methods fail miserably, then fix them!
Some of these may help:
group :development do
gem 'bullet', require: false
gem 'flamegraph', require: false
gem 'memory_profiler', require: false
gem 'rack-mini-profiler', require: false
gem 'seed_dump'
gem 'stackprof', require: false
gem 'traceroute', require: false
end
I'm developing API, based on EventMachine echo server. It listens requests on specific port and returns html-page with qr-code on it, which is generated depending on params from request query. The problem is that evaluating method, which packs string into qr-code takes from 8 to 11 seconds, it is unacceptable. I have no ideas why it happens, except it could be associated with event-machine.
P.S. In irb the same code RQRCode::QRCode.new(my_string, :size => 10,:level => :l) takes less than 1 second.
I have tried two different gems: rqrcode and barby+rqrcode. Both show the same results.
Code example:
require 'eventmachine'
require 'rqrcode'
class Handler < EventMachine::Connection
def receive_data(data)
puts Time.now
qrcode = RQRCode::QRCode.new('some_string', :size => 10,:level => :l)
puts Time.now
return qrcode
end
end
EventMachine::run {
EventMachine::start_server("0.0.0.0", 8081, Handler)
puts "Listening..."
}
Output:
2015-05-12 18:03:38 +0300
2015-05-12 18:03:48 +0300
It looks like you need to call close_connection. Here's EventMachine's echo server example
require 'eventmachine'
module EchoServer
def post_init
puts "-- someone connected to the echo server!"
end
def receive_data data
send_data ">>>you sent: #{data}"
close_connection if data =~ /quit/i # <---- This is what you're missing -----
end
def unbind
puts "-- someone disconnected from the echo server!"
end
end
# Note that this will block current thread.
EventMachine.run {
EventMachine.start_server "127.0.0.1", 8081, EchoServer
}
If your timestamps are always (nearly) exactly 10 seconds apart, I'd wager the request is ending because of a timeout rather than because of a response. After you respond with the QR data, call close_connection to let the browser know you're finished properly.
Edit It also looks like you're not calling send_data (and referencing Tume instead of Time). It looks like you need to use send_data, not return. Is your code snippet what's actually running on your server?
Update Running in debug mode was significantly slowing down QR generation.
The problem was that I've run server in debug-mode from RubyMine. Starting server from console solves the problem, qr-code generation takes about 1 sec.
I have a faye server (nodejs) running on localhost, and I am trying to setup a server side ruby client which needs to publish on the server on a regular basis. This is the code I am trying to use.
(Please ignore the commented code to start with).
I make a class variable ##client and initialize it as soon as the class loads. I define a class method pub whose task is to publish something on the faye server.
In the end, I just call the pub method twice. The first publication callback is received successfully, but the second publication doesn't make either of callback or the errback. And since the control has not been given back to the app, the app just hangs there.
If I make the gobal variable $client (currently commented), the behaviour is the same. But if I make the client everytime pub is called, then the publish goes on smoothly. I initiate it in EM.run loop or outside, the behavior is same. (as expected)
I don't want to make a new connection everytime I want to publish something since that defeats the purpose. Also, if I create a new client in EM.run everytime I call the method, the client connections don't close by themselves. I can see them open in lsof command as open files, and soon I'll start getting too many open files error I think.
I don't really understand Event Machine correctly, maybe I am missing something there.
require 'faye'
require 'eventmachine'
# $client = Faye::Client.new('http://localhost:5050/faye')
class Fayeclient
puts "#{__LINE__}: Reactor running: " + EM.reactor_running?.to_s
# if !defined? ##client or ##client.nil?
##client = Faye::Client.new('http://localhost:5050/faye')
puts "Created client: " + ##client.inspect
# end
def self.pub
puts "#{__LINE__}: Reactor running: " + EM.reactor_running?.to_s
# client = Faye::Client.new('http://localhost:5050/faye') #$client
# client = ##client
EM.run {
#client = Faye::Client.new('http://localhost:5050/faye') #$client
puts "#{__LINE__}: Reactor running: " + EM.reactor_running?.to_s
puts ##client.inspect
publication = ##client.publish('/foo', 'text' =>'Hello world')
puts "Publishing: #{publication.inspect}"
# puts "Publication methods: #{publication.methods}"
publication.callback do
puts "Did it #{publication.inspect}"
EM.stop_event_loop
puts "#{__LINE__}: Reactor running: " + EM.reactor_running?.to_s
# puts "#{client.methods}"
# puts client.inspect
# client.remove_all_listeners
# puts client.inspect
end
publication.errback do |error |
puts error.inspect
EM.stop_event_loop
end
}
puts "Outside event loop"
puts "#{__LINE__}: Reactor running: " + EM.reactor_running?.to_s
end
end
Fayeclient.pub
Fayeclient.pub
EM.run call is blocking, you have to run it on a separate thread, and eventually join it when all is over. In the example I'm using Singleton but it's up to you.
This does correctly the 2 faye calls.
#!/usr/bin/env ruby
#
require 'faye'
require 'singleton'
require 'eventmachine'
class Fayeclient
include Singleton
attr_accessor :em_thread, :client
def initialize
self.em_thread = Thread.new do
EM.run
end
self.client = Faye::Client.new('http://localhost:8890/faye')
end
def pub
puts "#{__LINE__}: Reactor running: " + EM.reactor_running?.to_s
puts client.inspect
publication = client.publish('/foo', 'text' =>'Hello world')
puts "Publishing: #{publication.inspect}"
publication.callback do
puts "Did it #{publication.inspect}"
EM.stop_event_loop
puts "#{__LINE__}: Reactor running: " + EM.reactor_running?.to_s
end
publication.errback do |error |
puts error.inspect
EM.stop_event_loop
end
end
end
Fayeclient.instance.pub
Fayeclient.instance.pub
Fayeclient.instance.em_thread.join
In my personal experience, anyway, having to deal with EventMachine inside Rails application can be a mess, some webserver uses EM, other does not so, and when you want to test from console it may not work as expected.
My solution is to fallback to http calls:
RestClient.post "http://localhost:#{Rails.configuration.faye_port}/faye", message: {foo: 'bar'}.to_json
I found this solution simpler and easy to customize, if you don't need to receive message from this piece of code.
I am trying to implement a request-response pattern using the em-zeromq gem, but I can't get the response socket to send a message back to the request socket in its handler. I have written some very simple code to test it:
em_req.rb
require 'em-zeromq'
client_id = ARGV[0] ? ARGV[0].to_i : 1
message = ARGV[1] || "Foo"
Thread.abort_on_exception = true
class ReqHandler
attr_reader :received
def on_readable(socket, messages)
messages.each do |m|
puts "Received message from server: #{m.copy_out_string}"
end
end
end
trap('INT') do
EM.stop
end
ctx = EM::ZeroMQ::Context.new(1)
EM.run do
conn = ctx.connect(ZMQ::REQ, 'tcp://127.0.0.1:9000', ReqHandler.new, identity: "client#{client_id}")
conn.socket.send_string(message)
end
em_rep.rb
require 'em-zeromq'
Thread.abort_on_exception = true
class ResponseHandler
attr_reader :received
def on_readable(socket, messages)
message = messages.first.copy_out_string
puts "Received message from client: #{message}"
socket.send_msg("re: #{message}")
end
end
trap('INT') do
EM.stop
end
ctx = EM::ZeroMQ::Context.new(1)
EM.run do
socket = ctx.bind(ZMQ::REP, 'tcp://127.0.0.1:9000', ResponseHandler.new)
end
I have written similar code using the push-pull pattern and got that to work, but for request-response all I get is the response code printing "Received message from client1: Foo" but the reply never reaches the request code. I suspect it has to do with writing to the socket in the response code's handler, because the same thing happens when I use a request-router pattern. The only time it works is when I send a message from the server without sending a message from the client first (using push-pull).
Any ideas about what might be causing this? The author of the gem isn't maintaining it anymore, but I thought I would post this issue anyway in the hopes of other developers with similar experiences seeing this.
I am using em-zeromq 0.2.2 on Ruby 1.9.2p290.
I commmited a fix in the master branch which should solve your problem, can you give it a try ?
You can use bundler to easily test it:
Create a file called Gemfile in your application folder:
source :rubygems
gem 'em-zeromq', :git => "git://github.com/andrewvc/em-zeromq.git"
And add this on top of your ruby files:
require 'rubygems'
require 'bundler/setup'
And last run this in the application folder ($ is your prompt):
$ bundle
Now you can execute your ruby files they will use the latest code from github
Edit: I am the new maintainer for the em-zeromq gem.
I have an eventmachine app where one script is reading from a file, sending data to another script line by line, and the "server" script is acting upon that data. Unfortunately, the "server" script fails to execute receive_data as it should. I know that a connection is being made because it eecutes post_init, and I know the sender script is sending data. Here is some of my code along with how I start the server.
module BT_Server
def post_init
puts "-- someone connected to the echo server!"
end
def receive_data(data)
puts "hi"
int, time, *int_macs = data.split("-")
# more stuff that isn't needed here
end
def bt_left(dev)
dev.save
if t = Device.macs.index(dev.mac)
Device.all[t].add_int(dev.int, dev.t_0, dev.t_l)
else
Device.new(dev.mac, dev.int, dev.t_0, dev.t_l)
end
return false
end
def unbind
puts "disconnection"
end
end
EventMachine::run {
EventMachine::start_server 'localhost', 8081, BT_Server
puts t_0 = Time.new
puts 'listening...'
}
Note: I have the Module definition in a separate file, along with my classes, which I require into the server script, if that makes any difference.
i tested your code and it outputs 'hi' every time i send something via telnet.
from my point of view, the code is correct.
are you sure the sending script is working? try with a manual telnet on port 8081.
regards.