0-60 with Goliath: Building High Performance Ruby Web-Services
Upcoming SlideShare
Loading in...5
×

Like this? Share it with your network

Share

0-60 with Goliath: Building High Performance Ruby Web-Services

  • 45,569 views
Uploaded on

 

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
45,569
On Slideshare
10,380
From Embeds
35,189
Number of Embeds
29

Actions

Shares
Downloads
68
Comments
0
Likes
21

Embeds 35,189

http://www.igvita.com 34,664
http://feeds.igvita.com 166
http://igvita.com 88
http://www.newsblur.com 59
http://bgror.com 55
http://lanyrd.com 24
http://localhost 22
http://translate.googleusercontent.com 20
http://webcache.googleusercontent.com 14
http://newsblur.com 12
http://coderwall.com 11
http://jellu.tistory.com 9
http://201.217.50.130 7
http://xianguo.com 6
http://www.hanrss.com 5
https://www.google.ca 3
http://plus.url.google.com 3
http://feeds.feedburner.com 3
http://beta.igvita.com 3
http://tweetree.com 3
http://protopage.com 2
http://www.directrss.co.il 2
http://igvita.com HTTP 2
http://127.0.0.1 1
https://www.google.com.tw 1
http://207.46.192.232 1
http://www.slideshare.net 1
http://213.8.145.174 1
http://api.cy1.cynny.com 1

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. 0-60 with Goliath
    building high-performance (Ruby) web services
    Ilya Grigorik
    @igrigorik
  • 2. conn =EM::HttpRequest.new('http://gogaruco.com')
    r1 =conn.get :path => "speakers.html", :keepalive => true# 250 ms
    r2 =conn.get :path => "schedule.html"# 300 ms
    # wait until done …
    Answer:
    All of the above!
    Total execution time is:
    250 ms
    300 ms
    550 ms
    ~ 65% truthiness
    ~ 25% truthiness *
    ~ 10% truthiness **
    HTTP Quiz
    this is not a trick question…
  • 3. “SPDY? We can’t even get HTTP right…”
    the why, how, and how-to of Goliath
  • 4. Server
    Client
    20 ms
    TCP handshake
    HTTP Request
    • headers, body
    40 ms
    processing
    Multi-part body (*)
    Terminate connection
    HTTP 1.0
    RFC 1945 (1996)
    + 40ms TCP setup (network)
    + 20ms request (network)
    + 40ms processing
    + 20ms response (network)
    66% of time in network overhead
  • 5. Benchmark client RTT, not just the server processing time
    A public service announcement…
  • 6. Keep-alive
    • Re-use open connection
    • 7. No multiplexing, serial
    • 8. Default to “on”
    Pipelining
    • No multiplexing
    • 9. Parallel requests
    HTTP 1.1
    RFC 2616 (1999)
  • 10. + 40ms TCP setup (network)
    + 20ms request (network)
    + 40ms processing
    + 20ms response (network)
    x 40ms TCP setup (network)
    + 20ms request (network)
    + 40ms processing
    + 20ms response (network)
    200ms for two requests
    Small win over HTTP 1.0
    Keep-alive
    RFC 2616 (1999)
    * One gotcha…
  • 11. Net:HTTP
    Keep-alive
    RFC 2616 (1999)
    Connection: close < ugh!
  • 12. + 40ms TCP setup (network)
    + 20ms request (network)
    + 40ms processing
    + 20ms response (network)
    60% of time in network overhead
    120ms for two requests – 50% improvement!
    Pipelining
    RFC 2616 (1999)
  • 13. Connection setup: 50ms
    Request 1: 300ms
    Request 2: 250ms
    Total time:
    ~250 ms
    ~300 ms
    ~350 ms
    ~600 ms
    Pipelining Quiz
    RFC 2616 (1999)
  • 14. There is just one small gotcha…
    Making HTTP Pipelining Usable on the Open Web
    http://tools.ietf.org/html/draft-nottingham-http-pipeline-01
  • 15. conn =EM::HttpRequest.new('http://gogaruco.com')
    r1 =conn.get :path => "speakers.html", :keepalive => true# 250 ms
    r2 =conn.get :path => "schedule.html"# 300 ms
    Total execution time is:
    250 ms
    300 ms
    550 ms
    Keep-alive what? HTTP 1.0!
    ~ 65% truthiness
    ~ 25% truthiness *
    ~ 10% truthiness **
    Good: Keep-alive + Pipelining
    Bad: Keep-alive + Garbage
    “I’m confused”
    HTTP in the wild
    it’s a sad state of affairs
    Keep-alive: mostly works – yay!
    Pipelining: disabled (except in Opera)
  • 16. HTTP can be a high-performance transport
    Goliath is our attempt to make it work
  • 17.
    • Goliath == v3 API stack
    • 18. Open source in 2011
    • 19. Growing community
    • 20. “Social Analytics”
    • 21. Rails frontend
    • 22. Ruby backend
    • 23. 95%+ of traffic via API’s
    +
    +
    Brief History
    Goliath @ PostRank
  • 24. Rails

    HTTP API
    HTTP API
    HTTP API
    HTTP API
    SQL
    SQL
    SQL

    SQL
    SQL
    Solr
    SQL
    • Separate logical & physical services
    • 25. Easy to tune, easy to maintain, easy to “scale”
    • 26. Stable code, fault-tolerance
    PRO
    • Higher upfront ops cost
    • 27. Lots of cross-service communication
    CON
  • 28.
  • 29. www.goliath.io
    • Single responsibility web-services
    • 30. AsyncHTTP response streaming + progressive notifications
    • 31. AsyncHTTP request streaming + progressive notifications
    • 32. Multiple requests within the same VM
    • 33. Keep-alive support
    • 34. Pipelining support
    • 35. Ruby API & “X-Ruby friendly”
    • 36. Easy to maintain & test
    … lower ops costs
    … full HTTP 1.1 support
    … Ruby polyglot!
  • 37. Client API
    (optional) Fibers
    optional async
    “Sync API”
    Routing
    Middleware
    async-rack
    0.3 ms
    (streaming) HTTP Parser
    Ruby, JRuby, Rubinius …
    HTTP 1.1
    EventMachine
    Network
    Goliath
    Optimize bottom up + minimal client API
  • 38. Goliath: Hands on…
  • 39. require'goliath'
    classHello< Goliath::API
    defresponse(env)
    [200, {}, "Hello World"]
    end
    end
    $>ruby hello.rb-sv –p 8000 –e production
    Hello World
    Simple Goliath server
  • 40. classHello< Goliath::API
    use Goliath::Rack::Params
    use Goliath::Rack::JSONP
    use Goliath::Rack::Validation::RequestMethod, %w(GET)
    use Goliath::Rack::Validation::RequiredParam, {:key => 'echo'}
    defresponse(env)
    [200, {}, {pong: params['echo’]}]
    end
    end
    Middleware
    No rackup file
  • 41. classBonjour< Goliath::API
    defresponse(env)
    [200, {}, "bonjour!"]
    end
    end
    classRackRoutes< Goliath::API
    map '/version'do
    run Proc.new { |env| [200, {}, ["Version 0.1"]] }
    end
    get "/bonjour", Bonjour
    not_found('/') do
    # run Proc. new { ... }
    end
    end
    Routing
    simple, but powerful
  • 42. classAsyncUpload< Goliath::API
    defon_headers(env, headers)
    env.logger.info'received headers: '+headers
    end
    defon_body(env, data)
    env.logger.info'received data chunk: '+ data
    end
    defon_close(env)
    env.logger.info'closing connection'
    end
    defresponse(env)
    # called when request processing is complete
    end
    end
    Async Request Processing
    don’t need to wait for the full request…
  • 43. classStream< Goliath::API
    defresponse(env)
    pt=EM.add_periodic_timer(1) { env.stream_send("hello") }
    EM.add_timer(10) do
    pt.cancel
    env.stream_send("goodbye!")
    env.stream_close
    end
    streaming_response 202, {'X-Stream' => 'Goliath’}
    end
    end
    Async/Streaming Response
    don’t need to render full response…
  • 44. * Goliath::SPDY
    classWebsocket< Goliath::WebSocket
    defon_open(env)
    env.logger.info”WebSocket opened”
    end
    defon_message(env, msg)
    env.logger.info”WebSocket message: #{msg}”
    end
    defon_close(env)
    env.logger.info”WebSocket closed”
    end
    defon_error(env, error)
    env.logger.error error
    end
    end
    Web-Sockets
    simple backend extension
  • 45. Ruby 1.9: Fibers
  • 46. defresponse(env)
    conn =EM::HttpRequest.new(’http://google.com/')
    r1 =conn.get :query => {:q => 'gogaruco'}
    r1.callback do
    r2 =conn.get :query => {:q => 'rubyconf'}
    r2.callback { ... }
    r2.errback { ... }
    end
    r2.errback { ... }
    streaming_response 200, {}
    end
    Async fun
    { { {} } } …
  • 47. defresponse(env)
    conn =EM::HttpRequest.new(’http://google.com/')
    r1 =conn.get :query => {:q => 'gogaruco'}
    if r1.error?
    r2 =conn.get :query => {:q => 'rubyconf'}
    else
    # ...
    end
    [200, {}, r2.response]
    end
    Async + Fibers
    Ruby gives us a choice
  • 48. EM-Synchrony:
    http://github.com/igrigorik/em-synchrony
    multi =EventMachine::Synchrony::Multi.new
    multi.add :a, db.aquery("select sleep(1)")
    multi.add :b, db.aquery("select sleep(1)")
    res =multi.perform
  • 61. describe HttpLogdo
    it 'forwards to our API server'do
    with_api(HttpLog, api_options) do |api|
    get_request({}, err) do |c|
    c.response_header.status.should== 200
    c.response_header[’X-Header'].should =='Header'
    c.response.should=='Hello from Responder'
    end
    end
    end
    end
    Integration Testing
    simple end-to-end testing
  • 62. Goliath
    • http://github.com/postrank-labs/goliath
    • 63. http://goliath.io/
    • 64. Google Group: http://groups.google.com/group/goliath-io
    • 65. Examples: GIT repo / examples
    thoughts, questions?
    now or later: ilya@igvita.com