Is there any reason that in my application, the send_data method lags terribly? My telnet (many other clients tested with same result) window wait 1-2 seconds before displying the data sent.
The following application has no lag:
require 'eventmachine'
class AreaServer < EventMachine::Connection
attr_accessor :options, :status
def receive_data(data)
send_data("I got: #{data}\r\n")
end
end
EM.run do
EM.start_server '192.168.0.199', 4000, AreaServer do |conn|
conn.options = {:my => 'options'}
conn.status = :OK
end
end
so I suspect it's not a networking problem.
It seemed the output stream kept open after writing, so I tried to find a solution to close this stream.
If you write close_connection_after_writing to receive_data, the stream will be closed after write. I guess you should turn off the keep_alive option.
class AreaServer < EventMachine::Connection
attr_accessor :options, :status
def receive_data(data)
send_data("I got: #{data}\n")
close_connection_after_writing
end
end
Related
Using eventmachine gem i am trying to send and receive data on localhost. Following is the code of my client and server files.
server.rb
class BCCServer < EM::Connection
attr_accessor :server_socket
def post_init
puts "BCC Server"
end
def recieve_data(data)
puts "Received data: #{data}"
send_data "You sent: #{data}"
end
end
EM.run do
EM.start_server("0.0.0.0", 3000, BCCServer)
end
client.rb
class DCClient < EventMachine::Connection
def post_init
puts "Sending "
send_data "send data"
close_connection_after_writing
end
def receive_data(data)
puts "Received #{data.length} bytes"
end
def unbind
puts 'Connection Lost !'
end
end
EventMachine.run do
EventMachine::connect("127.0.0.1", 3000, DCClient)
end
I executed both server and client files in separate console. Following is the output of client
Client output
Sending
Connection Lost !
Server output
BCC Server
............>>>10
In the server file i have printed the data received but its showing "............>>>10". Where i am doing the mistake?
Thanks
if you look at the EM::Connection implementation
https://github.com/eventmachine/eventmachine/blob/master/lib/em/connection.rb
def receive_data data
puts "............>>>#{data.length}"
end
Method receive_data returns exactly what you are experiencing.
That means original method gets called and not yours. That means one thing. You have a typo in a method which you tried to override :)
In BCCServer you have
recieve_data(data)
instead of
receive_data(data)
Using EM::stop_server schedules the server to be stopped. How can I know when the server is actually stopped?
require 'eventmachine'
class TestServer < EM::Connection
def post_init
send_data "Welcome\r\n"
end
end
class TestClient < EM::Connection
def receive_data(data)
puts data
end
end
Thread.new do
EM.run { }
end
while !EM.reactor_running? ; end
g = EM::start_server('127.0.0.1', 6667, TestServer)
EM::stop_server(g)
#sleep(1)
EM::connect('127.0.0.1', 6667, TestClient)
sleep
Running the above outputs Welcome. However if I uncomment sleep(1), nothing is printed.
Is there a way to achieve something similar without using sleep? Like using EM.reactor_running? to check if EM reactor's has started but to check if a server has been stopped.
My first question concerning Ruby.
I'm trying to test EventMachine interaction inside the Reactor loop - I guess it could be classified as "functional" testing.
Say I have two classes - a server and a client. And I want to test both sides - I need to be sure about their interaction.
Server:
require 'singleton'
class EchoServer < EM::Connection
include EM::Protocols::LineProtocol
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
end
def unbind
puts "-- someone disconnected from the echo server!"
end
end
Client:
class EchoClient < EM::Connection
include EM::Protocols::LineProtocol
def post_init
send_data "Hello"
end
def receive_data(data)
#message = data
p data
end
def unbind
puts "-- someone disconnected from the echo server!"
end
end
So, I've tried different approaches and came up with nothing.
The fundamental question is - could I somehow test my code with RSpec, using should_recive?
EventMachine parameter should be a class or a module, so I can't send instantiated/mocked code inside. Right?
Something like this?
describe 'simple rspec test' do
it 'should pass the test' do
EventMachine.run {
EventMachine::start_server "127.0.0.1", 8081, EchoServer
puts 'running echo server on 8081'
EchoServer.should_receive(:receive_data)
EventMachine.connect '127.0.0.1', 8081, EchoClient
EventMachine.add_timer 1 do
puts 'Second passed. Stop loop.'
EventMachine.stop_event_loop
end
}
end
end
And, if not, how would you do it with EM::SpecHelper? I have this code using it, and can't figure out what I'm doing wrong.
describe 'when server is run and client sends data' do
include EM::SpecHelper
default_timeout 2
def start_server
EM.start_server('0.0.0.0', 12345) { |ws|
yield ws if block_given?
}
end
def start_client
client = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
yield client if block_given?
return client
end
describe "examples from the spec" do
it "should accept a single-frame text message" do
em {
start_server
start_client { |client|
client.onopen {
client.send_data("\x04\x05Hello")
}
}
}
end
end
end
Tried a lot of variations of these tests and I just can't figure it out. I'm sure I'm missing something here...
Thanks for your help.
The simplest solution that I can think of is to change this:
EchoServer.should_receive(:receive_data)
To this:
EchoServer.any_instance.should_receive(:receive_data)
Since EM is expecting a class to start a server, the above any_instance trick will expect any instance of that class to receive that method.
The EMSpecHelper example (while being official/standard) is quite convoluted, I'd rather stick with the first rspec and use any_instance, just for simplicity's sake.
I'm new to ruby and maybe this is a very simple question..
I'd like to use eventmachine to develop a simulator for my tests.
Following example in documentation I can write something like this:
require 'eventmachine'
class Server< EM::Connection
def receive_data data
send_data data
close_connection_after_writing
end
end
#Note that this will block current thread.
EventMachine.run {
EventMachine.start_server '127.0.0.1','8080', Server
}
But I wonder if there is a way to use an instance of class, something like:
require 'eventmachine'
class Server< EM::Connection
attr_accessor :response
def receive_data data
send_data #response
close_connection_after_writing
end
end
server1 = Server.new
server1.response = "foo"
#Note that this will block current thread.
EventMachine.run {
EventMachine.start_server '127.0.0.1','8080', server1
}
I try to read source code..but it's too hard for me.
I'm surely missing something, but I don't know how to do something like this.
As I say there was something that I was missing.
You can add parameters for class to be instantiated :
class Server< EM::Connection
def initialize par
puts "I'm server number#{par}"
end
def receive_data data
send_data data
close_connection_after_writing
end
end
EventMachine.run {
EventMachine.start_server '127.0.0.1','8080', Server,1
}
EventMachine.run {
EventMachine.start_server '127.0.0.1','8080', Server,2
}
So I will customize instance behaviour with parameters
I have a consumer which pulls messages off of a queue via an evented subscription. It takes those messages and then connects with a rather slow http interface. I have a worker pool of 8 and once those are all filled up I need to stop pulling requests from the queue and have the fibers that are working on the http jobs keep working. Here is an example I've thrown together.
def send_request(callback)
EM.synchrony do
while $available <= 0
sleep 2
puts "sleeping"
end
url = 'http://example.com/api/Restaurant/11111/images/?image%5Bremote_url%5D=https%3A%2F%2Firs2.4sqi.net%2Fimg%2Fgeneral%2Foriginal%2F8NMM4yhwsLfxF-wgW0GA8IJRJO8pY4qbmCXuOPEsUTU.jpg&image%5Bsource_type_enum%5D=3'
result = EM::Synchrony.sync EventMachine::HttpRequest.new(url, :inactivity_timeout => 0).send("apost", :head => {:Accept => 'services.v1'})
callback.call(result.response)
end
end
def display(value)
$available += 1
puts value.inspect
end
$available = 8
EM.run do
EM.add_periodic_timer(0.001) do
$available -= 1
puts "Available: #{$available}"
puts "Tick ..."
puts send_request(method(:display))
end
end
I have found that if I call sleep within a while loop in the synchrony block, the reactor loop gets stuck. If I call sleep within an if statement(sleeping just once) then most times it is enough time for the requests to finish but it is unreliable at best. If I use EM::Synchrony.sleep, then the main reactor loop will keep creating new requests.
Is there a way to pause the main loop but have the fibers finish their execution?
sleep 2
...
add_periodic_timer(0.001)
Are you serious?
Have you ever though how many send_request's are sleeping in the loop? And it's adding 1000 every second.
What about this:
require 'eventmachine'
require 'em-http'
require 'fiber'
class Worker
URL = 'http://example.com/api/whatever'
def initialize callback
#callback = callback
end
def work
f = Fiber.current
loop do
http = EventMachine::HttpRequest.new(URL).get :timeout => 20
http.callback do
#callback.call http.response
f.resume
end
http.errback do
f.resume
end
Fiber.yield
end
end
end
def display(value)
puts "Done: #{value.size}"
end
EventMachine.run do
8.times do
Fiber.new do
Worker.new(method(:display)).work
end.resume
end
end