Slide 1

Slide 1 text

Is EventMachine a worthy alternative to Node.js

Slide 2

Slide 2 text

Andrew Nesbitt github.com/andrew @teabass

Slide 3

Slide 3 text

forwardtechnology.co.uk

Slide 4

Slide 4 text

Node.js nodejs.org

Slide 5

Slide 5 text

"Evented I/O for V8 JavaScript" Concurrent, scalable network programs Speaks HTTP natively Asynchronous, non-blocking by design

Slide 6

Slide 6 text

"Realtime" applications Proxy servers Web APIs What would you use it for?

Slide 7

Slide 7 text

Synchronous I/O

Slide 8

Slide 8 text

Asynchronous I/O

Slide 9

Slide 9 text

var p_client = new Db('integration_tests_20', new Server("127.0.0.1", 27017, {}), {'pk':CustomPKFactory}); p_client.open(function(err, p_client) { p_client.dropDatabase(function(err, done) { p_client.createCollection('test_custom_key', function(err, collection) { collection.insert({'a':1}, function(err, docs) { collection.find({'_id':new ObjectID("aaaaaaaaaaaa")}, function(err, cursor) { cursor.toArray(function(err, items) { test.assertEquals(1, items.length); // Let's close the db p_client.close(); }); }); }); }); }); }); Callback hell gist.github.com/1309020

Slide 10

Slide 10 text

EventMachine github.com/eventmachine/eventmachine

Slide 11

Slide 11 text

"Realtime" applications Proxy servers Web APIs What would you use it for?

Slide 12

Slide 12 text

Goliath github.com/postrank-labs/goliath

Slide 13

Slide 13 text

Rack API Middleware support Ruby fibers No callback hell 1.9.2, jruby, rubinius

Slide 14

Slide 14 text

Fight!

Slide 15

Slide 15 text

Hello world github.com/andrew/helloworlds

Slide 16

Slide 16 text

Rack Example class HelloWorld def call(env) [200, {'Content-Type' => 'text/html'}, ["Hello Rack"]] end end run HelloWorld.new

Slide 17

Slide 17 text

Node.js Example var http = require('http'); http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello Node\n'); }).listen(9292, "127.0.0.1"); console.log('Server running at http://127.0.0.1:9292/');

Slide 18

Slide 18 text

EventMachine Example require 'rubygems' require 'eventmachine' require 'evma_httpserver' class HelloWorld < EM::Connection include EM::HttpServer def post_init super no_environment_strings end def process_http_request response = EM::DelegatedHttpResponse.new(self) response.status = 200 response.content_type 'text/html' response.content = 'Hello EventMachine' response.send_response end end EM.run{ EM.start_server '0.0.0.0', 9292, HelloWorld }

Slide 19

Slide 19 text

Goliath Example require 'rubygems' require 'goliath' class Proxy < Goliath::API def response(env) [200, {}, "Hello Goliath"] end end

Slide 20

Slide 20 text

Rack Example class HelloWorld def call(env) [200, {'Content-Type' => 'text/html'}, ["Hello Rack"]] end end run HelloWorld.new require 'rubygems' require 'goliath' class Proxy < Goliath::API def response(env) [200, {}, "Hello Goliath"] end end

Slide 21

Slide 21 text

rack => 423 goliath => 2125 node => 6084 em => 8814 Performance (requests/s) ab -n 1000 -c 100 http://localhost:9000/

Slide 22

Slide 22 text

URL Shortener

Slide 23

Slide 23 text

var http = require('http'), connect = require('connect'), redis = require("redis"), client = redis.createClient(), b62 = require('base62'); var server = connect.createServer(connect.query()); server.use('/', connect.router(function(app){ app.get('/', function(req, res, next){ res.writeHead(200, {'Content-Type': 'text/html'}); if (req.query.url){ client.keys('*',function(err, result){ key = b62.encode(result.length) client.set(key, req.query.url, function(err, result){ res.write('http://localhost:9000/'+key) res.end() }); }); } else { res.write('Hi') res.end() } }); app.get('/:key', function(req, res, next){ client.get(req.params.key, function(err, result){ res.writeHead(302, {'Location': result}); res.end() }); }); }) ); server.listen(9000); console.log('Server running at http://127.0.0.1:9000/'); Node Example github.com/andrew/node-shortener

Slide 24

Slide 24 text

class Shortener < EM::Connection include EM::HttpServer attr_reader :http_request_uri, :http_query_string def post_init super no_environment_strings end def process_http_request redis = EM::Protocols::Redis.connect path = @http_request_uri.to_s query = @http_query_string.to_s response = EM::DelegatedHttpResponse.new(self) case path when '/' if @http_query_string params = URI.decode_www_form(query) url = params.assoc('url').last.chomp redis.keys '*' do |result| key = result.size.base62_encode redis.set key, url do |result| response.status = 200 response.content_type 'text/html' response.content = "http://localhost:9000/#{key}" response.send_response end end else response.status = 200 response.content_type 'text/html' response.content = 'Hi' response.send_response end else redis.get path[1..-1] do |result| response.status = 302 response.headers["Location"] = result response.send_response end end end end EM.run{ EM.start_server '0.0.0.0', 9000, Shortener } EM Example github.com/andrew/em-shortener

Slide 25

Slide 25 text

REDIS = EventMachine::Synchrony::ConnectionPool.new(:size => 5) do Redis.connect end class Redirect < Goliath::API use Goliath::Rack::Params def response(env) url = REDIS.get params[:key] if url [302, {:location => url}, nil] else [404, {}, 'Not Found'] end end end class Create < Goliath::API use Goliath::Rack::Params def response(env) if params['url'] key = REDIS.keys.size.base62_encode REDIS.set(key, params['url']) [200, {}, "http://#{env['HTTP_HOST']}/#{key}"] else [200, {}, "Hi!"] end end end class Shortener < Goliath::API map "/" do run Create.new end map "/:key" do run Redirect.new end end Goliath Example github.com/andrew/goliath-shortener

Slide 26

Slide 26 text

goliath => 1048 node => 3660 em => 7943 Performance (requests/s) ab -n 1000 -c 100 http://localhost:9000/2W

Slide 27

Slide 27 text

Things to consider

Slide 28

Slide 28 text

Testing

Slide 29

Slide 29 text

vowsjs.org github.com/tmm1/em-spec Node EM

Slide 30

Slide 30 text

DSLs

Slide 31

Slide 31 text

Sinatra get '/hi' do "Hello World!" end sinatrarb.com

Slide 32

Slide 32 text

Express.js app.get('/', function(req, res){ res.send('Hello World'); }); expressjs.com app.get "/", (req, res) -> res.send "Hello World"

Slide 33

Slide 33 text

class HomeAction < Cramp::Action def start render "Hello World" finish end end Cramp cramp.in

Slide 34

Slide 34 text

Libraries

Slide 35

Slide 35 text

Deployment

Slide 36

Slide 36 text

Coffeescript jashkenas.github.com/coffee-script

Slide 37

Slide 37 text

var http = require('http'), connect = require('connect'), redis = require("redis"), client = redis.createClient(), b62 = require('base62'); var server = connect.createServer(connect.query()); server.use('/', connect.router(function(app){ app.get('/', function(req, res, next){ res.writeHead(200, {'Content-Type': 'text/html'}); if (req.query.url){ client.keys('*',function(err, result){ key = b62.encode(result.length) client.set(key, req.query.url, function(err, result){ res.write('http://localhost:9000/'+key) res.end() }); }); } else { res.write('Hi') res.end() } }); app.get('/:key', function(req, res, next){ client.get(req.params.key, function(err, result){ res.writeHead(302, {'Location': result}); res.end() }); }); }) ); server.listen(9000); console.log('Server running at http://127.0.0.1:9000/'); Node Example github.com/andrew/node-shortener

Slide 38

Slide 38 text

http = require("http") connect = require("connect") redis = require("redis") client = redis.createClient() b62 = require("base62") server = connect.createServer(connect.query()) server.use "/", connect.router((app) -> app.get "/", (req, res, next) -> res.writeHead 200, "Content-Type": "text/html" if req.query.url client.keys "*", (err, result) -> key = b62.encode(result.length) client.set key, req.query.url, (err, result) -> res.write "http://localhost:9000/" + key res.end() else res.write "Hi" res.end() app.get "/:key", (req, res, next) -> client.get req.params.key, (err, result) -> res.writeHead 302, Location: result res.end() ) server.listen 9000 console.log "Server running at http://127.0.0.1:9000/" Coffeescript Example

Slide 39

Slide 39 text

Rails

Slide 40

Slide 40 text

async-rails github.com/igrigorik/async-rails

Slide 41

Slide 41 text

Conclusion

Slide 42

Slide 42 text

Node Pros Shared code between client and server Vibrant Community Lightning Fast Cons Javascript Callback hell Less mature

Slide 43

Slide 43 text

EventMachine Pros It’s ruby! Mature Clever DSLs Also fast Cons More difficult to write than normal ruby Restricted to EM libraries Not the new hotness

Slide 44

Slide 44 text

Is EventMachine a worthy alternative to Node.js

Slide 45

Slide 45 text

Yes

Slide 46

Slide 46 text

Node or EM are not a magic bullets

Slide 47

Slide 47 text

Go - http://golang.org/ Erlang - http://erlang.org/ Twisted - http://twistedmatrix.com Other languages

Slide 48

Slide 48 text

Fin. speakerdeck.com/u/andrew/p/em-vs-node