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

The Celluloid Ecosystem - RubyConf 2012

4131d2f57a0db2a2b4d9a62bd389fd44?s=47 tarcieri
November 03, 2012

The Celluloid Ecosystem - RubyConf 2012

4131d2f57a0db2a2b4d9a62bd389fd44?s=128

tarcieri

November 03, 2012
Tweet

Transcript

  1. The Tony Arcieri RubyConf November 2nd, 2012 Ecosystem

  2. None
  3. We’re hiring!

  4. “Threads on Rails!”

  5. Why?

  6. Multicore

  7. Going Faster (The Old Way) •Clock speed grows exponentially •Faster

    clock speed = faster program! •Threads are expensive!
  8. Going Faster (The New Way) •POWER WALL! Clock speed is

    “stuck” •Number of cores grows exponentially •Threads are increasingly cheap
  9. Multicore Is The Future

  10. “I’ll just throw multiple VMs at it!”

  11. Wastes RAM

  12. Serialization Penalty http://aphyr.com/posts/244-context-switches-and-serialization-in-node

  13. When we have 100 core CPUs...

  14. Will we run 100 VMs? Or one?

  15. Open Source Voice Application Framework Projects Using Celluloid

  16. OOP + Actor Model

  17. “I thought of objects being like biological cells and/or individual

    computers on a network, only able to communicate with messages” - Alan Kay, creator of Smalltalk, on the meaning of "object oriented programming"
  18. OOP Tools Classes Inheritance Messages

  19. Concurrency Tools Threads Locks Queues

  20. OOP Tools Classes Inheritance Messages Concurrency Tools Threads Locks Queues

    +
  21. Active Objects Based on the Actor Model

  22. “Cells”

  23. None
  24. None
  25. Actor Model •Actors are computational entities that can receive messages

    •Each actor has a unique address •If you know an actor’s address, you can send it messages •Actors can create new actors
  26. I’m not the first to do Actor Model + OOP

  27. Pythons did it!

  28. None
  29. 1997

  30. None
  31. Forgotten approach to concurrency?

  32. Example

  33. require 'celluloid' class Launcher include Celluloid def launch 3.downto(1) do

    |count| puts "#{count}..." sleep 1 end puts "BLASTOFF!" end end
  34. require 'celluloid' class Launcher include Celluloid def launch 3.downto(1) do

    |count| puts "#{count}..." sleep 1 end puts "BLASTOFF!" end end
  35. >> launcher = Launcher.new => #<Celluloid::Actor(Launcher:0x3fe8bef4d544)>

  36. Synchronous Calls

  37. >> launcher = Launcher.new => #<Celluloid::Actor(Launcher:0x3fe8bef4d544)> >> launcher.launch

  38. >> launcher = Launcher.new => #<Celluloid::Actor(Launcher:0x3fe8bef4d544)> >> launcher.launch 3...

  39. >> launcher = Launcher.new => #<Celluloid::Actor(Launcher:0x3fe8bef4d544)> >> launcher.launch 3... 2...

  40. >> launcher = Launcher.new => #<Celluloid::Actor(Launcher:0x3fe8bef4d544)> >> launcher.launch 3... 2...

    1...
  41. >> launcher = Launcher.new => #<Celluloid::Actor(Launcher:0x3fe8bef4d544)> >> launcher.launch 3... 2...

    1... BLASTOFF! => nil >>
  42. &DOOHU &HOOXORLG $FWRU3UR[\ &$// 5HFHLYHU &HOOXORLG 0DLOER[ 5(63216( &HOOXORLG 0DLOER[

    &HOOXORLG$FWRU &HOOXORLG&DOO &HOOXORLG5HVSRQVH Synchronous Calls
  43. Asynchronous Calls

  44. >> launcher = Launcher.new => #<Celluloid::Actor(Launcher:0x3fe8bef4d544)> >> launcher.async.launch

  45. >> launcher = Launcher.new => #<Celluloid::Actor(Launcher:0x3fe8bef4d544)> >> launcher.async.launch New syntax!

  46. THEY TOOK ‘ER BANG METHERDS!!!!!

  47. You’ll get them back in Celluloid 1.0

  48. >> launcher = Launcher.new => #<Celluloid::Actor(Launcher:0x3fe8bef4d544)> >> launcher.async.launch

  49. >> launcher = Launcher.new => #<Celluloid::Actor(Launcher:0x3fe8bef4d544)> >> launcher.async.launch => nil

    >> Returns immediately
  50. >> launcher = Launcher.new => #<Celluloid::Actor(Launcher:0x3fe8bef4d544)> >> launcher.async.launch => nil

    >> 3...
  51. >> launcher = Launcher.new => #<Celluloid::Actor(Launcher:0x3fe8bef4d544)> >> launcher.async.launch => nil

    >> 3... 2...
  52. >> launcher = Launcher.new => #<Celluloid::Actor(Launcher:0x3fe8bef4d544)> >> launcher.async.launch => nil

    >> 3... 2... 1...
  53. >> launcher = Launcher.new => #<Celluloid::Actor(Launcher:0x3fe8bef4d544)> >> launcher.async.launch => nil

    >> 3... 2... 1... BLASTOFF!
  54. Asynchronous Calls &DOOHU &HOOXORLG $V\QF3UR[\ &$// 5HFHLYHU &HOOXORLG 0DLOER[ &HOOXORLG$FWRU

    &HOOXORLG&DOO
  55. >> launcher1, launcher2 = Launcher.new, Launcher.new => [#<Celluloid::Actor(Launcher:0x3fc3f1c1c0d8)>, #<Celluloid::Actor(Launcher:0x3fc3f1c1b82c)>] Easy

    Parallelism
  56. >> launcher1, launcher2 = Launcher.new, Launcher.new => [#<Celluloid::Actor(Launcher:0x3fc3f1c1c0d8)>, #<Celluloid::Actor(Launcher:0x3fc3f1c1b82c)>] >>

    launcher1.async.launch; launcher2.async.launch => nil >> Easy Parallelism
  57. >> launcher1, launcher2 = Launcher.new, Launcher.new => [#<Celluloid::Actor(Launcher:0x3fc3f1c1c0d8)>, #<Celluloid::Actor(Launcher:0x3fc3f1c1b82c)>] >>

    launcher1.async.launch; launcher2.async.launch => nil >> 3... 3... Easy Parallelism
  58. >> launcher1, launcher2 = Launcher.new, Launcher.new => [#<Celluloid::Actor(Launcher:0x3fc3f1c1c0d8)>, #<Celluloid::Actor(Launcher:0x3fc3f1c1b82c)>] >>

    launcher1.async.launch; launcher2.async.launch => nil >> 3... 3... 2... 2... Easy Parallelism
  59. >> launcher1, launcher2 = Launcher.new, Launcher.new => [#<Celluloid::Actor(Launcher:0x3fc3f1c1c0d8)>, #<Celluloid::Actor(Launcher:0x3fc3f1c1b82c)>] >>

    launcher1.async.launch; launcher2.async.launch => nil >> 3... 3... 2... 2... 1... 1... Easy Parallelism
  60. >> launcher1, launcher2 = Launcher.new, Launcher.new => [#<Celluloid::Actor(Launcher:0x3fc3f1c1c0d8)>, #<Celluloid::Actor(Launcher:0x3fc3f1c1b82c)>] >>

    launcher1.async.launch; launcher2.async.launch => nil >> 3... 3... 2... 2... 1... 1... BLASTOFF!!! BLASTOFF!!! Easy Parallelism
  61. Futures

  62. class FibonacciWorker include Celluloid def fib(n) n < 2 ?

    n : fib(n-1) + fib(n-2) end end
  63. >> worker = FibonacciWorker.new => #<Celluloid::Actor(FibonacciWorker:0x3ffdcaed65cc)> >> future = worker.future.fib(40)

    => #<Celluloid::Future:0x007ffb9201f0a0>
  64. >> worker = FibonacciWorker.new => #<Celluloid::Actor(FibonacciWorker:0x3ffdcaed65cc)> >> future = worker.future.fib(40)

    => #<Celluloid::Future:0x007ffb9201f0a0> Returns immediately
  65. >> worker = FibonacciWorker.new => #<Celluloid::Actor(FibonacciWorker:0x3ffdcaed65cc)> >> future = worker.future.fib(40)

    => #<Celluloid::Future:0x007ffb9201f0a0> >> future.value
  66. >> worker = FibonacciWorker.new => #<Celluloid::Actor(FibonacciWorker:0x3ffdcaed65cc)> >> future = worker.future.fib(40)

    => #<Celluloid::Future:0x007ffb9201f0a0> >> future.value Blocks until complete
  67. >> worker = FibonacciWorker.new => #<Celluloid::Actor(FibonacciWorker:0x3ffdcaed65cc)> >> future = worker.future.fib(40)

    => #<Celluloid::Future:0x007ffb9201f0a0> >> future.value => 102334155
  68. &DOOHU &HOOXORLG $FWRU3UR[\ 5HFHLYHU &HOOXORLG 0DLOER[ &HOOXORLG$FWRU &HOOXORLG&DOO &$// &HOOXORLG

    0DLOER[ )8785( &HOOXORLG )XWXUH &HOOXORLG5HVSRQVH 9$/8(" 9$/8( Futures
  69. Pools

  70. >> pool = FibonacciWorker.pool(size: 16) => #<Celluloid::Pool(FibonacciWorker:0x3ffdcaed65cc)>

  71. >> pool = FibonacciWorker.pool => #<Celluloid::Pool(FibonacciWorker:0x3ffdcaed65cc)> 1 actor per CPU

    (by default)
  72. >> pool = FibonacciWorker.pool => #<Celluloid::Pool(FibonacciWorker:0x3ffdcaed65cc)> >> (32...40).map { |n|

    pool.future.fib(n) }.map(&:value)
  73. >> pool = FibonacciWorker.pool => #<Celluloid::Pool(FibonacciWorker:0x3ffdcaed65cc)> >> (32...40).map { |n|

    pool.future.fib(n) }.map(&:value) => [2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986]
  74. What if an actor crashes?

  75. None
  76. None
  77. Fault Tolerance •Supervisors & Supervision Trees •“Let it crash!” •Restart

    in a clean state
  78. Ecosystem

  79. None
  80. Distributed Celluloid Evented I/O for Celluloid Celluloid::IO-based web server

  81. Distributed Celluloid

  82. None
  83. Bleeding edge :( 0.12.0 prerelease

  84. Cells are services

  85. DCell exposes them to the network

  86. Built on 0MQ •Fast, brokerless message queue •DCell uses push/pull

    sockets •Built on Celluloid::ZMQ/ffi-rzmq
  87. •Distributed process orchestration (e.g. Chef, Puppet) •Multi-tier Web Applications •Asynchronous

    background jobs Use Cases •Distributed process orchestration (e.g. Chef, Puppet) •Multi-tier Web Applications / SOA •Asynchronous background jobs
  88. Why do this? )URQW(QG )URQW(QG )URQW(QG 5(67 &OLHQW 5(67 &OLHQW

    5(67 &OLHQW 5(67 6HUYLFH 5(67 6HUYLFH 5(67 6HUYLFH 'RPDLQ 2EMHFW 'RPDLQ 2EMHFW 'RPDLQ 2EMHFW 'RPDLQ 2EMHFW 'RPDLQ 2EMHFW 'RPDLQ 2EMHFW 'RPDLQ 2EMHFW 'RPDLQ 2EMHFW 'RPDLQ 2EMHFW
  89. Instead of this? )URQW(QG )URQW(QG )URQW(QG 'RPDLQ 2EMHFW 'RPDLQ 2EMHFW

    'RPDLQ 2EMHFW 'RPDLQ 2EMHFW 'RPDLQ 2EMHFW 'RPDLQ 2EMHFW 'RPDLQ 2EMHFW 'RPDLQ 2EMHFW 'RPDLQ 2EMHFW
  90. "Objects can message objects transparently that live on other machines

    over the network, and you don't have to worry about the networking gunk, and you don't have to worry about finding them, and you don't have to worry about anything. It's just as if you messaged an object that's right next door." --Steve Jobs describing NeXT Portable Distributed Objects
  91. Distributed objects have mostly been a failure

  92. Distributed Object Failures •CORBA •SOAP •PDO •RMI •DRB

  93. Why?

  94. Not asynchronous Huge problem in distributed systems

  95. Not built on the Actor Model

  96. Actor Model is AWESOME!!!

  97. Unifying abstraction for both concurrency and distribution

  98. Success!

  99. Example

  100. You need Zookeeper :(

  101. What is Zookeeper?

  102. Zookeeper provides a total ordering of events in a distributed

    system
  103. Zookeeper = Transactions

  104. What is Zookeeper good for? •Node registry •Global data registry

    •Locks •Leader election
  105. zk gem •Used by the DCell Zookeeper backend •High level

    API to Zookeeper •Abstract leader election API https://github.com/slyphon/zk
  106. It’s not hard! :D

  107. Installing Zookeeper • git clone https://github.com/celluloid/dcell.git • cd dcell •

    rake zookeeper:install • rake zookeeper:start
  108. $ rake zookeeper:start *** Starting Zookeeper cd zookeeper && bin/zkServer.sh

    start JMX enabled by default Using config: bin/../conf/zoo.cfg Starting zookeeper ... STARTED
  109. Two Node Cluster •Node #1 is “itchy” •Node #2 is

    “scratchy”
  110. “itchy” commands >> require 'dcell' => true >> DCell.start :id

    => 'itchy', :addr => 'tcp:// 127.0.0.1:7777' I, [2012-05-09T10:20:46.999000 #52416] INFO -- : Connected to itchy => #<Celluloid::Supervisor(DCell::Group):0x836> Note itchy is on port 7777
  111. “scratchy” commands >> require 'dcell' => true >> DCell.start :id

    => 'scratchy', :addr => 'tcp:// 127.0.0.1:7778' I, [2012-05-09T10:26:42.322000 #52555] INFO -- : Connected to itchy I, [2012-05-09T10:26:42.331000 #52555] INFO -- : Connected to scratchy => #<Celluloid::Supervisor(DCell::Group):0x838> Note scratchy is on port 7778
  112. Finding nodes •DCell::Node[‘itchy’] •DCell::Node.all •DCell.me

  113. Finding Remote Cells DCell::Node[‘itchy’][:info]

  114. >> info_service = DCell::Node['itchy'][:info] => #<Celluloid::Actor(DCell::InfoService:0x83c) @platform="java" @ruby_platform="jruby 1.6.7" @cpu_type="Intel(R)

    Core(TM) i7-2635QM CPU" @ruby_version="1.9.2" @ruby_engine="jruby" @cpu_vendor=:intel @distribution="Mac OS X 10.7.3 (11D50b)" @cpu_count=8 @hostname="wintermute.local" @cpu_speed=2.0 @os="darwin" @cpu_arch="x86_64" @os_version="11.3.0"> >> info_service.uptime => 42 >> info_service.load_averages => [1.08, 0.93, 0.84] >> info_service.distribution => "Mac OS X 10.7.3 (11D50b)" “scratchy” commands
  115. Defining DCell services

  116. require 'celluloid' class Greeter include Celluloid def hello "Hi" end

    end Example Cell
  117. >> Celluloid::Actor[:greeter] = Greeter.new => #<Celluloid::Actor(Greeter:0x7fc)> >> Celluloid::Actor[:greeter].hello => "Hi"

    Naming Cells
  118. >> Greeter.supervise_as :greeter => #<Celluloid::Supervisor:0x1fb> >> Celluloid::Actor[:greeter].hello => "Hi" Naming

    Cells
  119. >> node = DCell::Node['itchy'] => #<DCell::Node[itchy] @addr="tcp://127.0.0.1:7777"> >> node[:greeter].hello =>

    "Hi" Calling Cells
  120. All Celluloid features supported •Synchronous calls •Asynchronous calls •Futures

  121. Evented I/O for Celluloid http://github.com/celluloid/celluloid-io

  122. Now that we have threads licked... How about I/O?

  123. USE BLOCKING I/O

  124. Blocking IO is OK!* No central event loop to block

  125. *But be careful Locks in external services = Deadlocks in

    Celluloid
  126. Evented IO •Large numbers of connections (>10,000) •Mostly idle connections

    •Mostly IO-bound problems •Websockets are an ideal case
  127. Actors are event loops

  128. &HOOXORLG $FWRU &HOOXORLG 0DLOER[ &RQGLWLRQ 9DULDEOH Normal Actors

  129. Celluloid::IO Actors &HOOXORLG $FWRU &HOOXORLG ,20DLOER[ &HOOXORLG ,25HDFWRU

  130. nio4r-powered reactor

  131. nio4r •Quasi-inspired by Java NIO •Smallest API possible •libev C

    extension for CRuby/rbx •Java extension for JRuby •Pure Ruby version too! http://github.com/tarcieri/nio4r/
  132. Celluloid::IO::TCPSocket •Uses fibered I/O •“Duck-type” of ::TCPSocket •Evented inside Celluloid::IO

    actors •Blocking IO elsewhere (Ruby Threads, normal Celluloid actors)
  133. Evented IO AND Threaded IO You don’t have to choose!

  134. class EchoServer include Celluloid::IO def initialize(host, port) puts "*** Starting

    echo server on #{host}:#{port}" # Since we included Celluloid::IO, we're actually making a # Celluloid::IO::TCPServer here @server = TCPServer.new(host, port) run! end def finalize @server.close if @server end def run loop { handle_connection! @server.accept } end def handle_connection(socket) _, port, host = socket.peeraddr puts "*** Received connection from #{host}:#{port}" loop { socket.write socket.readpartial(4096) } rescue EOFError puts "*** #{host}:#{port} disconnected" socket.close end end supervisor = EchoServer.supervise("127.0.0.1", 1234) trap("INT") { supervisor.terminate; exit } sleep
  135. Celluloid::IO-powered web server http://github.com/celluloid/reel

  136. Hello World Benchmark # httperf --num-conns=50 --num-calls=1000 Ruby Version Throughput

    Latency ------------ ---------- ------- JRuby HEAD 5650 reqs/s (0.2 ms/req) Ruby 1.9.3 5263 reqs/s (0.2 ms/req) JRuby 1.6.7 4303 reqs/s (0.2 ms/req) rbx HEAD 2288 reqs/s (0.4 ms/req)
  137. Hello World Comparison Web Server Throughput Latency ---------- ---------- -------

    Goliath (0.9.4) 2058 reqs/s (0.5 ms/req) Thin (1.2.11) 7502 reqs/s (0.1 ms/req) Node.js (0.6.5) 11735 reqs/s (0.1 ms/req) Ruby servers on 1.9.3
  138. It’s got Websockets!

  139. Celluloid-powered Web Framework http://github.com/celluloid/lattice

  140. Vaporware!

  141. Goals •Built out of parts of Rails •Webmachine for resources

    •Multithreaded development mode •Easy scatter/gather for SOA
  142. That’s all, folks!

  143. Twitter: @bascule Celluloid: celluloid.io Blog: unlimitednovelty.com