$30 off During Our Annual Pro Sale. View Details »

EM vs Node

EM vs Node

Andrew Nesbitt

October 29, 2011
Tweet

More Decks by Andrew Nesbitt

Other Decks in Technology

Transcript

  1. Is
    EventMachine
    a worthy alternative to
    Node.js

    View Slide

  2. Andrew Nesbitt
    github.com/andrew
    @teabass

    View Slide

  3. forwardtechnology.co.uk

    View Slide

  4. Node.js
    nodejs.org

    View Slide

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

    View Slide

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

    View Slide

  7. Synchronous I/O

    View Slide

  8. Asynchronous I/O

    View Slide

  9. 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

    View Slide

  10. EventMachine
    github.com/eventmachine/eventmachine

    View Slide

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

    View Slide

  12. Goliath
    github.com/postrank-labs/goliath

    View Slide

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

    View Slide

  14. Fight!

    View Slide

  15. Hello world
    github.com/andrew/helloworlds

    View Slide

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

    View Slide

  17. 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/');

    View Slide

  18. 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
    }

    View Slide

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

    View Slide

  20. 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

    View Slide

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

    View Slide

  22. URL Shortener

    View Slide

  23. 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

    View Slide

  24. 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

    View Slide

  25. 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

    View Slide

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

    View Slide

  27. Things to consider

    View Slide

  28. Testing

    View Slide

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

    View Slide

  30. DSLs

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  34. Libraries

    View Slide

  35. Deployment

    View Slide

  36. Coffeescript
    jashkenas.github.com/coffee-script

    View Slide

  37. 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

    View Slide

  38. 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

    View Slide

  39. Rails

    View Slide

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

    View Slide

  41. Conclusion

    View Slide

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

    View Slide

  43. 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

    View Slide

  44. Is
    EventMachine
    a worthy alternative to
    Node.js

    View Slide

  45. Yes

    View Slide

  46. Node or EM are not a
    magic bullets

    View Slide

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

    View Slide

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

    View Slide