Upgrade to Pro — share decks privately, control downloads, hide ads and more …

A Brief Aside on Request Queueing

A Brief Aside on Request Queueing

John Pignata

March 13, 2013
Tweet

More Decks by John Pignata

Other Decks in Programming

Transcript

  1. Response Time t h e a m o u n

    t o f t i m e required to process a single request
  2. require "java" require "socket" import "java.util.concurrent.Executors" pool = Executors.new_fixed_thread_pool(2) server

    = TCPServer.new(3000) loop do if pool.active_count < pool.maximum_pool_size client = server.accept pool.execute do sleep 1 client.puts Time.now client.close end end end
  3. require "java" require "socket" import "java.util.concurrent.Executors" size = ARGV[0].to_i pool

    = Executors.new_fixed_thread_pool(size) server = TCPServer.new(3000) loop do if pool.active_count < pool.maximum_pool_size client = server.accept pool.execute do sleep 1 client.puts Time.now client.close end end end
  4. require "socket" server = TCPServer.new(3000) server.listen(1024) loop do client =

    server.accept sleep 1 client.puts Time.now client.close end
  5. Client Server SYN J connect called create entry on listen

    queue SYN K, ACK J+1 connect returns; client considers socket open ACK K+1 connection acceptable by server process
  6. Unicorn workers each call accept() on the q u e

    u e a n d w o r k connections directly from it
  7. P u m a w i l l c a

    l l accept() and pass connections to its worker thread pool
  8. Thin, via EventMachine, will call accept() 10 times per reactor

    tick and place connections into a separate queue
  9. this is generally invisible to your application as it is

    happening underneath it i n t h e k e r n e l o r application server
  10. elapsed wall clock time: 400ms thin 50ms 50ms 50ms 50ms

    50ms 50ms 50ms 50ms Request Queue Response Time + Latency 50ms 100ms 150ms 200ms 250ms 300ms 350ms 400ms
  11. elapsed wall clock time: 100MS unicorn worker 50ms 50ms 50ms

    50ms 50ms 50ms 50ms 50ms Request Queue Response Time + Latency 50ms 50ms 50ms 50ms 100ms 100ms 100ms 100ms unicorn worker unicorn worker unicorn worker
  12. Rails 50ms 130ms 20ms 9.5secs 340ms 9ms 2.3secs 693ms Request

    Queue Response Time + Latency 50ms 180ms 200ms 9.7secs 10secs 10secs 12.3secs 13secs
  13. n o a l g o r i t h

    m w i l l compensate for an unstable distribution in response time
  14. work through a buffering intermediary to allow the worker to

    immediately flush the response and move on
  15. jp@oeuf:~$ ping -c 5 www.stanford.edu PING www-v6.stanford.edu (171.67.215.200): 56 data

    bytes 64 bytes from 171.67.215.200: icmp_seq=0 ttl=252 time=91.151 ms 64 bytes from 171.67.215.200: icmp_seq=1 ttl=252 time=96.684 ms 64 bytes from 171.67.215.200: icmp_seq=2 ttl=252 time=93.497 ms 64 bytes from 171.67.215.200: icmp_seq=3 ttl=252 time=96.360 ms 64 bytes from 171.67.215.200: icmp_seq=4 ttl=252 time=92.683 ms --- www-v6.stanford.edu ping statistics --- 5 packets transmitted, 5 packets received, 0.0% packet loss round-trip min/avg/max/stddev = 91.151/94.075/96.684/2.138 ms
  16. # Forgive me father for I [@daveyeu] have sinned. if

    defined?(Thin) class Thin::Connection def process_with_log_connections size = backend.instance_variable_get("@connections").size Stats.measure("thin.connections", size) process_without_log_connections end alias_method_chain :process, :log_connections end end
  17. # https://gist.github.com/jpignata/5084567 class UnicornConnectionMonitor def initialize(app, options = {}) @app

    = app @statsd = options.fetch(:statsd) end def call(env) Raindrops::Linux.tcp_listener_stats(“0.0.0.0:3000”).each do |_, stats| @statsd.measure("unicorn.connections.active", stats.active) @statsd.measure("unicorn.connections.queued", stats.queued) end @app.call(env) end end