2010-04-13 Reactor Pattern & Event Driven Programming 2

3,518 views

Published on

Published in: Technology
0 Comments
6 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
3,518
On SlideShare
0
From Embeds
0
Number of Embeds
23
Actions
Shares
0
Downloads
60
Comments
0
Likes
6
Embeds 0
No embeds

No notes for slide

2010-04-13 Reactor Pattern & Event Driven Programming 2

  1. 1. Reactor Pattern & Event-Driven Programming A scalable concurrent approach, using EventMachine with Thin as an example Lin Jen-Shin, http://godfat.org/
  2. 2. Reactor Pattern & Event-Driven Programming A scalable concurrent approach, using EventMachine with Thin as an example Lin Jen-Shin, http://godfat.org/
  3. 3. Reactor Pattern & Event-Driven Programming http://godfat.org/slide/2010-04-13-reactor-pattern-and-2.pdf Lin Jen-Shin, http://godfat.org/
  4. 4. Table of Contents • concurrency, why and how in network • Event-Driven Programming explained in Flash with Ruby syntax • Reactor Pattern in EventMachine with Thin • how Thin works • how EventMachine works
  5. 5. Event-Driven Programming register method(:do_something) loop{ loop{ # you control the flow # event loop control the flow, do_something # later it calls your callback } event = pop_event_queue dispatch event if event }
  6. 6. Reactor Pattern register method(:handle) loop{ loop{ data = read data = partial_read handle data event = process data } dispatch event if event }
  7. 7. Table of Contents • how Thin works • how EventMachine works
  8. 8. Table of Contents • how Thin works • how EventMachine works
  9. 9. Table of Contents • how Thin works • how EventMachine works • how AMQP works
  10. 10. Table of Contents • how Thin works • how EventMachine works • how AMQP works • how Unicorn and Rainbows! works
  11. 11. Reactor Pattern Request (resource)
  12. 12. Reactor Pattern EventMachine Request (demultiplexer (resource) + dispatcher)
  13. 13. Reactor Pattern EventMachine Request Thin (or AMQP) (demultiplexer (resource) (request handler) + dispatcher)
  14. 14. Reactor Pattern EventMachine Request Thin (or AMQP) (demultiplexer (resource) (request handler) + dispatcher) Rack Thin handler
  15. 15. Reactor Pattern EventMachine Request Thin (or AMQP) (demultiplexer (resource) (request handler) + dispatcher) Rack Rails Rack Thin rack env adapter handler
  16. 16. Reactor Pattern EventMachine Request Thin (or AMQP) (demultiplexer (resource) (request handler) + dispatcher) Rack Rails Rack Thin Rails rack env adapter handler
  17. 17. your rails application Reactor Pattern EventMachine Request Thin (or AMQP) (demultiplexer (resource) (request handler) + dispatcher) Rack Rails Rack Thin Rails rack env adapter handler
  18. 18. how Thin works Thin (or AMQP) (request handler)
  19. 19. how Thin works • Thin::Server
  20. 20. how Thin works • Thin::Server • Thin::Backends::TcpServer # communicate with EventMachine
  21. 21. how Thin works • Thin::Server • Thin::Backends::TcpServer # communicate with EventMachine • Thin::Connection # EventMachine event handler
  22. 22. how Thin works • Thin::Server • Thin::Backends::TcpServer # communicate with EventMachine • Thin::Connection # EventMachine event handler • Thin::Request # partial HTTP request parsing # Rack env builder
  23. 23. how Thin works Thin::Server
  24. 24. how Thin works Thin::Server Backends::TcpServer
  25. 25. how Thin works Thin::Server Backends::TcpServer Connection
  26. 26. how Thin works Thin::Server Backends::TcpServer Connection Request
  27. 27. how Thin works Thin::Server Backends::TcpServer Connection Connection Request Request
  28. 28. how Thin works Thin::Server Backends::TcpServer Connection Connection Connection Request Request Request
  29. 29. how Thin works thin 1.2.7 codename No Hup
  30. 30. how Thin works thin 1.2.7 codename No Hup # in lib/thin/backends/tcp_server.rb:16 # in Thin::TcpServer#connect EventMachine.start_server( @host, @port, Thin::Connection, &method(:initialize_connection)) # rack app, backend ref, timeout, etc
  31. 31. how Thin works thin 1.2.7 codename No Hup # in lib/thin/connection.rb:42 # in Thin::Connection#receive_data process if @request.parse(data) # true: parsed, so process! # false: we need more data!
  32. 32. how Thin works thin 1.2.7 codename No Hup # in lib/thin/request.rb:82 # in Thin::Request#parse @request = @parser.execute(@env, @data, @nparsed) # @env: Rack env # @data: HTTP header buffer # @nparsed: index of parsed data
  33. 33. how Thin works thin 1.2.7 codename No Hup // in ext/thin_parser/thin.c:335 // in thin.c#Thin_HttpParser_execute thin_http_parser_execute(http, dptr, dlen, from); // http: HTTP parser pointer // dptr: HTTP header data pointer // dlen: HTTP header data length // form: previous @nparsed
  34. 34. how Thin works thin 1.2.7 codename No Hup // in ext/thin_parser/parser.rl:102 // in parser.rl#thin_http_parser_execute // (it’s mongrel’s http parser) size_t thin_http_parser_execute( http_parser *parser, const char *buffer, size_t len, size_t off)
  35. 35. how Thin works thin 1.2.7 codename No Hup Ragel is a finite state machine compiler with output support for C, C++, Objective-C, D, Java and Ruby source code.
  36. 36. how Thin works thin 1.2.7 codename No Hup Ragel is a finite state machine compiler with output support for C, C++, Objective-C, D, Java and Ruby source code. • Mongrel HTTP parser • Hpricot HTML/XML parser • JSON parser
  37. 37. how Thin works thin 1.2.7 codename No Hup # in lib/thin/connection.rb:42 # in Thin::Connection#receive_data process if @request.parse(data) # true: parsed, so process! # false: we need more data!
  38. 38. how Thin works thin 1.2.7 codename No Hup # in lib/thin/connection.rb:52 # in Thin::Connection#process if threaded? @request.threaded = true EventMachine.defer(method( :pre_process), method(:post_process)) else @request.threaded = false post_process(pre_process) end
  39. 39. how EventMachine works eventmachine 0.12.10 # in lib/eventmachine.rb:1045 # in EventMachine.defer unless @threadpool require ‘thread’ @threadpool = [] @threadqueue = ::Queue.new @resultqueue = ::Queue.new spawn_threadpool end @threadqueue << [op||blk,callback]
  40. 40. how Thin works thin 1.2.7 codename No Hup # in lib/thin/connection.rb:68 # in Thin::Connection#pre_process @request.async_callback = method(:post_process) # ... response = AsyncResponse catch(:async) do # Process the request calling the Rack adapter response = @app.call(@request.env) end response
  41. 41. how Thin works thin 1.2.7 codename No Hup # in lib/thin/connection.rb:95 # in Thin::Connection#post_process @response.status, @response.headers, @response.body = *result # ... @response.each do |chunk| trace { chunk } send_data chunk end
  42. 42. Reactor Pattern • resources • synchronous event demultiplexer • dispatcher • request handler (Thin::Connection) by wikipedia
  43. 43. Table of Contents • how Thin works • how EventMachine works • how AMQP works • how Unicorn and Rainbows! works
  44. 44. how EventMachine works eventmachine 0.12.10
  45. 45. how EventMachine works eventmachine 0.12.10 # in lib/eventmachine.rb:571 # in EventMachine.start_server s = if port start_tcp_server server, port else start_unix_server server end @acceptors[s] = [klass,args,block] # s: server (in Reactor) uuid # klass: Thin::Connection # args: [] # block: method(:initialize_connection)
  46. 46. how EventMachine works eventmachine 0.12.10 # in lib/eventmachine.rb:50 case $eventmachine_library when :pure_ruby require ‘pr_eventmachine’ when :extension require ‘rubyeventmachine’ when :java require ‘jeventmachine’
  47. 47. how EventMachine works eventmachine 0.12.10 # in lib/pr_eventmachine.rb:318 # in EventMachine.run loop { @current_loop_time = Time.now break if @stop_scheduled run_timers # timer event break if @stop_scheduled # epoll, kqueue, etc crank_selectables break if @stop_scheduled # close scheduling if client timeout run_heartbeats
  48. 48. how EventMachine works eventmachine 0.12.10 # in lib/eventmachine.rb:1445 # in EventMachine.event_callback elsif opcode == ConnectionData c = @conns[conn_binding] or raise ConnectionNotBound, “received data #{data} for unknown signature:” “#{conn_binding}” c.receive_data data elsif opcode == LoopbreakSignalled # opcode: event enum (int) # conn_binding: connection uuid # data: received data
  49. 49. how Thin works thin 1.2.7 codename No Hup # in lib/thin/connection.rb:42 # in Thin::Connection#receive_data process if @request.parse(data) # true: parsed, so process! # false: we need more data!
  50. 50. how EventMachine works eventmachine 0.12.10 # in lib/eventmachine.rb:1427 # in EventMachine.event_callback elsif opcode == ConnectionAccepted accep,args,blk = @acceptors[conn_binding] raise NoHandlerForAcceptedConnection unless accep c = accep.new data, *args @conns[data] = c blk and blk.call(c) c # (needed?) elsif opcode == ConnectionCompleted # conn_binding: server uuid # data: connection uuid
  51. 51. how Thin works thin 1.2.7 codename No Hup # in lib/thin/backends/tcp_server.rb:16 # in Thin::TcpServer#connect EventMachine.start_server( @host, @port, Thin::Connection, &method(:initialize_connection)) # rack app, backend ref, timeout, etc
  52. 52. how EventMachine works eventmachine 0.12.10 # in lib/pr_eventmachine.rb:256 module EventMachine TimerFired = 100 ConnectionData = 101 ConnectionUnbound = 102 ConnectionAccepted = 103 ConnectionCompleted = 104 LoopbreakSignalled = 105 end
  53. 53. Table of Contents • how Thin works • how EventMachine works • how AMQP works • how Unicorn and Rainbows! works
  54. 54. how AMQP works • AMQP::BasicClient # extend to AMQP::Client
  55. 55. how AMQP works • AMQP::BasicClient # extend to AMQP::Client • AMQP::Client # included into EventMachine::Connection
  56. 56. how AMQP works amqp 0.6.7
  57. 57. how AMQP works amqp 0.6.7 # in lib/amqp.rb:79 # in AMQP.start EM.run{ @conn ||= connect *args @conn.callback(&blk) if blk @conn }
  58. 58. how AMQP works amqp 0.6.7 # in lib/amqp.rb:18 # in AMQP.connect Client.connect *args
  59. 59. how AMQP works amqp 0.6.7 # in lib/amqp/client.rb:188 # in AMQP::Client.connect opts = AMQP.setting.merge(opts) EM.connect opts[:host], opts[:port], self, opts
  60. 60. how Thin works thin 1.2.7 codename No Hup # in lib/thin/backends/tcp_server.rb:16 # in Thin::TcpServer#connect EventMachine.start_server( @host, @port, Thin::Connection, &method(:initialize_connection)) # rack app, backend ref, timeout, etc
  61. 61. how EventMachine works eventmachine 0.12.10 # in lib/eventmachine.rb:1571 # in EventMachine.klass_from_handler klass = if handler and handler.is_a?(Class) raise ArgumentError, “must provide module or #{klass.name}” unless klass >= handler handler elsif handler Class.new(klass){ include handle } else klass # klass: EventMachine::Connection end # handler: Thin::Connection or AMQP::Client
  62. 62. how AMQP works amqp 0.6.7 # in lib/amqp/client.rb:115 # in AMQP::Client#receive_data while frame = Frame.parse(@buf) log ’receive’, frame process_frame frame end
  63. 63. how AMQP works • AMQP::Frame # basic building block of AMQP data stream
  64. 64. how AMQP works • AMQP::Frame # basic building block of AMQP data stream • AMQP::Buffer # frame buffer and parser
  65. 65. how AMQP works • AMQP::Frame # basic building block of AMQP data stream • AMQP::Buffer # frame buffer and parser • AMQP::Protocol::Connection # used in BasicClient#process_frame
  66. 66. how AMQP works • MQ # easy to use, high level wrapper
  67. 67. how AMQP works • MQ # easy to use, high level wrapper • MQ::Queue # the entities which receive messages
  68. 68. how AMQP works • MQ # easy to use, high level wrapper • MQ::Queue # the entities which receive messages • MQ::Exchange # the entities to which messages are sent
  69. 69. how AMQP works • MQ # easy to use, high level wrapper • MQ::Queue # the entities which receive messages • MQ::Exchange # the entities to which messages are sent by wikipedia
  70. 70. how AMQP works # default connection MQ.new.queue(‘name’) # default exchange (direct) MQ.new.publish(‘name’) #-- convenience wrapper (read: HACK) # for thread-local MQ object MQ.queue(‘name’) MQ.publish(‘name’)
  71. 71. how AMQP works MQ.queues # all created queues MQ.exchanges # all created exchanges MQ.direct # direct exchange MQ.fanout # fanout exchange MQ.topic # topic exchange MQ.headers # headers exchange
  72. 72. Table of Contents • how Thin works • how EventMachine works • how AMQP works • how Unicorn and Rainbows! works
  73. 73. Unicorn? .
  74. 74. Unicorn? . • is not event-driven!
  75. 75. Unicorn? . • is not event-driven! • except Mongrel HTTP parser, all written in Ruby
  76. 76. Unicorn? . • is not event-driven! • except Mongrel HTTP parser, all written in Ruby • yet *super fast* for fast client
  77. 77. Unicorn? . • is not event-driven! • except Mongrel HTTP parser, all written in Ruby • yet *super fast* for fast client • preforking worker with blocking I/O
  78. 78. Unicorn? . Rainbows!? • is not event-driven! • except Mongrel HTTP parser, all written in Ruby • yet *super fast* for fast client • preforking worker with blocking I/O
  79. 79. Unicorn? . Rainbows!? • is not event-driven! • could be event-driven • except Mongrel HTTP parser, all written in Ruby • yet *super fast* for fast client • preforking worker with blocking I/O
  80. 80. Unicorn? . Rainbows!? • is not event-driven! • could be event-driven • except Mongrel • also pure Ruby, HTTP parser, all except... written in Ruby • yet *super fast* for fast client • preforking worker with blocking I/O
  81. 81. Unicorn? . Rainbows!? • is not event-driven! • could be event-driven • except Mongrel • also pure Ruby, HTTP parser, all except... written in Ruby • yet *super fast* for • *any* concurrency fast client model • preforking worker with blocking I/O
  82. 82. Unicorn? . Rainbows!? • is not event-driven! • could be event-driven • except Mongrel • also pure Ruby, HTTP parser, all except... written in Ruby • yet *super fast* for • *any* concurrency fast client model • preforking worker • provide network with blocking I/O concurrency
  83. 83. Unicorn? . Rainbows!? • is not event-driven! • RevFiberSpawn • except Mongrel HTTP parser, all written in Ruby • yet *super fast* for fast client • preforking worker with blocking I/O
  84. 84. Unicorn? . Rainbows!? • is not event-driven! • RevFiberSpawn • except Mongrel HTTP parser, all • Revactor written in Ruby • yet *super fast* for fast client • preforking worker with blocking I/O
  85. 85. Unicorn? . Rainbows!? • is not event-driven! • RevFiberSpawn • except Mongrel HTTP parser, all • Revactor written in Ruby • ThreadPool • yet *super fast* for fast client • preforking worker with blocking I/O
  86. 86. Unicorn? . Rainbows!? • is not event-driven! • RevFiberSpawn • except Mongrel HTTP parser, all • Revactor written in Ruby • ThreadPool • yet *super fast* for • Rev fast client • preforking worker with blocking I/O
  87. 87. Unicorn? . Rainbows!? • is not event-driven! • RevFiberSpawn • except Mongrel HTTP parser, all • Revactor written in Ruby • ThreadPool • yet *super fast* for • Rev fast client • preforking worker • ThreadSpawn with blocking I/O
  88. 88. Unicorn? . Rainbows!? • is not event-driven! • RevFiberSpawn • except Mongrel HTTP parser, all • Revactor written in Ruby • ThreadPool • yet *super fast* for • Rev fast client • preforking worker • ThreadSpawn with blocking I/O • EventMachine
  89. 89. Unicorn? . Rainbows!? • is not event-driven! • RevFiberSpawn • except Mongrel • RevThreadSpawn HTTP parser, all • Revactor written in Ruby • ThreadPool • yet *super fast* for • Rev fast client • preforking worker • ThreadSpawn with blocking I/O • EventMachine
  90. 90. Unicorn? . Rainbows!? • is not event-driven! • RevFiberSpawn • except Mongrel • RevThreadSpawn HTTP parser, all • Revactor FiberSpawn written in Ruby • • ThreadPool • yet *super fast* for • Rev fast client • preforking worker • ThreadSpawn with blocking I/O • EventMachine
  91. 91. Unicorn? . Rainbows!? • is not event-driven! • RevFiberSpawn • except Mongrel • RevThreadSpawn HTTP parser, all • Revactor FiberSpawn written in Ruby • • ThreadPool FiberPool • yet *super fast* for • fast client • Rev • preforking worker • ThreadSpawn with blocking I/O • EventMachine
  92. 92. Unicorn? . Rainbows!? • is not event-driven! • RevFiberSpawn • except Mongrel • RevThreadSpawn HTTP parser, all • Revactor FiberSpawn written in Ruby • • ThreadPool FiberPool • yet *super fast* for • fast client • Rev • NeverBlock • preforking worker • ThreadSpawn with blocking I/O • EventMachine
  93. 93. Unicorn? . Rainbows!? • is not event-driven! • RevFiberSpawn • except Mongrel • RevThreadSpawn HTTP parser, all • Revactor FiberSpawn written in Ruby • • ThreadPool FiberPool • yet *super fast* for • fast client • Rev • NeverBlock • preforking worker • ThreadSpawn with blocking I/O • RevThreadPool • EventMachine
  94. 94. Unicorn? . Rainbows!? unicorn master _ unicorn worker[0] | _ client[0] _ unicorn worker[1] | _ client[1] _ unicorn worker[2] | _ client[2] ... _ unicorn worker[M] _ client[M]
  95. 95. Unicorn? . Rainbows!? unicorn master rainbows! master _ rainbows! worker[0] _ unicorn worker[0] | _ client[0,0] | _ client[0] | _ client[0,1] | ... _ unicorn worker[1] | _ client[0,N] | _ client[1] _ rainbows! worker[1] | _ client[1,0] _ unicorn worker[2] | ... | _ client[2] | _ client[1,N] ... ... _ rainbows! worker[M] _ unicorn worker[M] _ client[M,0] ... _ client[M] _ client[M,N]
  96. 96. Unicorn? . Rainbows!? unicorn master rainbows! master _ rainbows! worker[0] _ unicorn worker[0] | _ client[0,0]------ | _ client[0,1]------- ___app[0] /___app[1] | _ client[0] | _ client[0,2]-------->--< | ... __/ ... `---app[P] _ unicorn worker[1] | _ client[0,N]----/ _ rainbows! worker[1] | _ client[1] | _ client[1,0]------ | _ client[1,1]------- ___app[0] /___app[1] _ unicorn worker[2] | _ client[1,2]-------->--< | ... __/ ... `---app[P] | _ client[2] | _ client[1,N]----/ _ rainbows! worker[M] ... _ client[M,0]------ ___app[0] _ client[M,1]------- /___app[1] _ unicorn worker[M] _ client[M,2]-------->--< ... ... __/ `---app[P] _ client[M] _ client[M,N]----/
  97. 97. Unicorn? . Rainbows!? static files | nginx |--> slow actions --> Rainbows! | `--> fast actions --> Unicorn
  98. 98. Unicorn? . http://unicorn.bogomips.org/ Rainbows!? http://rainbows.rubyforge.org/ static files | nginx |--> slow actions --> Rainbows! | `--> fast actions --> Unicorn
  99. 99. how Unicorn works unicorn 0.97.0
  100. 100. how Unicorn works unicorn 0.97.0 # in lib/unicorn.rb:270 # in Unicorn::HttpServer#start maintain_worker_count
  101. 101. how Unicorn works unicorn 0.97.0 # in lib/unicorn.rb:602 # in Unicorn::HttpServer#maintain_worker_count (off = WORKER.size - worker_process) == 0 and return off < 0 and return spawn_missing_workers
  102. 102. how Unicorn works unicorn 0.97.0 # in lib/unicorn.rb:591 # in Unicorn::HttpServer#spawn_missing_workers worker = Worker.new(worker_nr, Unicorn::Util.tmpio) before_fork.call(self, worker) WORKERS[fork { ready_pipe.close if ready_pipe self.ready_pipe = nil worker_loop(worker) }] = worker
  103. 103. how Unicorn works unicorn 0.97.0 # in lib/unicorn.rb:705 # in Unicorn::HttpServer#worker_loop ready.each do |sock| begin process_client(sock.accept_nonblock) # workers load balancing here!! ^^
  104. 104. how Unicorn works unicorn 0.97.0 # in lib/unicorn.rb:630 # in Unicorn::HttpServer#process_client # read request, call app, write app response def process_client(client) client.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) response = app.call(env = REQUEST.read(client)) # [...] HttpResponse.write(client, response, HttpRequest::PARSER.headers?)
  105. 105. how Unicorn works unicorn 0.97.0 # in lib/unicorn/http_request.rb:31 # in Unicorn::HttpRequest#read # Does the majority of the IO processing. # It has been written in Ruby using about 8 # different IO processing strategies. # [...] # Anyone who thinks they can make it faster is # more than welcome to take a crack at it.
  106. 106. how Rainbows! works rainbows 0.91.0
  107. 107. how Rainbows! works rainbows 0.91.0 Sorry! To be continued......
  108. 108. how Rainbows! works rainbows 0.91.0 Sorry! To be continued...... ?

×