only start a master process • The master process does not handle requests, but controls one or more child processes that do • It starts these processes by forking itself
I'm the original process" if fork puts "#{Process.pid}: I’m the master" else puts "#{Process.pid}: I'm the child” @winner = ‘Argentina’ end puts "#{Process.pid}: The winner is #{@winner}" 351: I'm the original process 351: I’m the master 351: The winner is The Netherlands 372: I'm the child 372: The winner is Argentina
worker processes that handle requests • If you expect your workers to break it’s easy to kill them without affecting other workers • Concurrency is limited by the number of processes • Every process uses the full amount of memory.
|i| Thread.new do sleep rand(5) puts "I'm thread #{i} and the winner is #{@winner}" end end sleep 2 @winner = ‘Argentina’ sleep 30 I'm thread 2 and the winner is The Netherlands I'm thread 4 and the winner is The Netherlands I'm thread 0 and the winner is The Netherlands I'm thread 1 and the winner is Argentina I'm thread 3 and the winner is Argentina
100.times do |i| Thread.new do sleep rand(0.5) @lock.synchronize do snapshot_of_total = @total sleep rand(0.5) @total = snapshot_of_total + 1 end end end sleep 60 puts @total 100
• They wait for work to come in and process it outside of the server’s main lock • Concurrency is limited by size of the thread pool • Worker threads use little memory compared to processes
EM.run do 5.times do |i| EM.add_timer(rand(5)) do puts "I'm callback #{i} and” the winner is #{@winner}" end end EM.add_timer(2) do @winner = ‘Argentina’ end end I'm callback 1 and the winner is The Netherlands I'm callback 3 and the winner is The Netherlands I'm callback 0 and the winner is Argentina I'm callback 2 and the winner is Argentina I'm callback 4 and the winner is Argentina
EM.run do 100.times do |i| EM.add_timer(rand(0.5)) do snapshot_of_total = @total EM.add_timer(rand(0.5)) do @total = snapshot_of_total + 1 end end end EM.add_timer(5) do puts @total end end 4
def process EventMachine.defer( method(:pre_process), method(:post_process) ) end def pre_process @app.call(@request.env) end def post_process send_data(@response) end end end @port, Connection )
of operations and callbacks • Whenever an operation has to wait for something it stops, a callback gets called when the wait is over • Hardly any memory is used by the callbacks, they’re just Ruby blocks • Concurrency is an order of magnitude bigger than the other two models • All code running in the loop has to be event-driven
If you run highly concurrent apps with long-running streams event-driven allows you to scale • If you don’t have a high-volume problem or you expect your processes to get out of control go for multi-process