Evented TCP server using eventmachine.
TCP server:
require 'eventmachine'
PORT = 4545
puts "Listening on #{PORT}...\n"
class Counter
attr_accessor :total_data
def initialize
@total_data = 0
end
def total_data=(num)
@total_data += num
end
end
class EchoServer < EM::Connection
def initialize(counter)
@total = 0
@total_data = 0
@counter = counter
end
def receive_data(data)
@total += 1
@total_data += data.length
@counter.total_data = data.length
# puts "\ntotal=#{@total} \t #{data.length}\n"
# send_data(data)
# do some work, monkey with the data, kill some time:
lines = data.split("\n")
# puts "lines=#{lines.length}"
lines.each do |line|
fields = line.split("\t")
# puts "\tfields=#{fields.length}"
fields.each do |field|
# puts "\t\tfield=#{field.length}"
end
end
end
end
EventMachine.run do
counter = Counter.new
Signal.trap("INT") { EventMachine.stop }
Signal.trap("TERM") { EventMachine.stop }
EventMachine.start_server("127.0.0.1", PORT, EchoServer, counter)
EM.add_periodic_timer(30) { puts "counter=#{counter.inspect}" }
end
TCP client:
require 'socket'
start = Time.now
begin
if ARGV.empty?
port = 4545
else
port = ARGV[0].to_i
end
client = TCPSocket.new('127.0.0.1', port)
# puts "Socket::TCP_NODELAY=#{Socket::TCP_NODELAY}"
client.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) # Nagle off
# 420_000_000.times do |x| <<--- took about 40 minutes to process 128,100,000,000 of data (OS X)
1_000_000.times do |x|
# send 305 bytes:
msg = "Lorem \t ipsum \t dolor sit amet, consectetur adipiscing elit. Donec luctus enim mollis eros interdum egestas. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec vitae est pellentesque, suscipit nisl et, rutrum nulla. Nullam sed justo nisl. Donec rutrum velit odio, non sollicitudin lorem amet.\n"
# msg += "Lorem \t ipsum \t dolor sit amet, consectetur adipiscing elit. Donec luctus enim mollis eros interdum egestas. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec vitae est pellentesque, suscipit nisl et, rutrum nulla. Nullam sed justo nisl. Donec rutrum velit odio, non sollicitudin lorem amet.\n"
# msg += "Lorem \t ipsum \t dolor sit amet, consectetur adipiscing elit. Donec luctus enim mollis eros interdum egestas. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec vitae est pellentesque, suscipit nisl et, rutrum nulla. Nullam sed justo nisl. Donec rutrum velit odio, non sollicitudin lorem amet.\n"
# msg += "Lorem \t ipsum \t dolor sit amet, consectetur adipiscing elit. Donec luctus enim mollis eros interdum egestas. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec vitae est pellentesque, suscipit nisl et, rutrum nulla. Nullam sed justo nisl. Donec rutrum velit odio, non sollicitudin lorem amet.\n"
# puts "#{msg.length}\n"
client.puts(msg)
# ignore response:
# resp = client.gets
# puts "resp=#{resp}"
end
ensure
client.close if client
puts "elapsed: #{(Time.now - start)}"
end
Why ? Well, it's all the rage (or was), it's very fast, it doesn't use a lot of memory (hovered around 30K) compared to what it's processing, and there's no worries about threading and forking and all those entail.
Sure, you are not going to traipse off to the bonneville salt flats with this server and break any speed records, but after trying all of the other options this seems to work the best for pushing data into a server for various purposes. It's amazing what 50 lines of ruby code, not counting eventmachine itself, and *nix can do. Also, all of my testing was done with ruby MRI.
fin