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

The Celluloid Ecosystem - RubyConf 2012

tarcieri
November 03, 2012

The Celluloid Ecosystem - RubyConf 2012

tarcieri

November 03, 2012
Tweet

More Decks by tarcieri

Other Decks in Programming

Transcript

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

    View Slide

  2. View Slide

  3. We’re hiring!

    View Slide

  4. “Threads on Rails!”

    View Slide

  5. Why?

    View Slide

  6. Multicore

    View Slide

  7. Going Faster
    (The Old Way)
    •Clock speed grows exponentially
    •Faster clock speed = faster program!
    •Threads are expensive!

    View Slide

  8. Going Faster
    (The New Way)
    •POWER WALL! Clock speed is “stuck”
    •Number of cores grows exponentially
    •Threads are increasingly cheap

    View Slide

  9. Multicore Is The Future

    View Slide

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

    View Slide

  11. Wastes RAM

    View Slide

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

    View Slide

  13. When we have 100
    core CPUs...

    View Slide

  14. Will we run 100 VMs?
    Or one?

    View Slide

  15. Open Source Voice Application Framework
    Projects Using Celluloid

    View Slide

  16. OOP + Actor Model

    View Slide

  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"

    View Slide

  18. OOP
    Tools
    Classes
    Inheritance
    Messages

    View Slide

  19. Concurrency
    Tools
    Threads
    Locks
    Queues

    View Slide

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

    View Slide

  21. Active Objects
    Based on the Actor Model

    View Slide

  22. “Cells”

    View Slide

  23. View Slide

  24. View Slide

  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

    View Slide

  26. I’m not the first
    to do Actor Model + OOP

    View Slide

  27. Pythons did it!

    View Slide

  28. View Slide

  29. 1997

    View Slide

  30. View Slide

  31. Forgotten approach
    to concurrency?

    View Slide

  32. Example

    View Slide

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

    View Slide

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

    View Slide

  35. >> launcher = Launcher.new
    => #

    View Slide

  36. Synchronous Calls

    View Slide

  37. >> launcher = Launcher.new
    => #
    >> launcher.launch

    View Slide

  38. >> launcher = Launcher.new
    => #
    >> launcher.launch
    3...

    View Slide

  39. >> launcher = Launcher.new
    => #
    >> launcher.launch
    3...
    2...

    View Slide

  40. >> launcher = Launcher.new
    => #
    >> launcher.launch
    3...
    2...
    1...

    View Slide

  41. >> launcher = Launcher.new
    => #
    >> launcher.launch
    3...
    2...
    1...
    BLASTOFF!
    => nil
    >>

    View Slide

  42. &DOOHU
    &HOOXORLG
    $FWRU3UR[\
    &$//
    5HFHLYHU
    &HOOXORLG
    0DLOER[
    5(63216( &HOOXORLG
    0DLOER[
    &HOOXORLG$FWRU
    &HOOXORLG&DOO
    &HOOXORLG5HVSRQVH
    Synchronous Calls

    View Slide

  43. Asynchronous Calls

    View Slide

  44. >> launcher = Launcher.new
    => #
    >> launcher.async.launch

    View Slide

  45. >> launcher = Launcher.new
    => #
    >> launcher.async.launch
    New syntax!

    View Slide

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

    View Slide

  47. You’ll get them back
    in Celluloid 1.0

    View Slide

  48. >> launcher = Launcher.new
    => #
    >> launcher.async.launch

    View Slide

  49. >> launcher = Launcher.new
    => #
    >> launcher.async.launch
    => nil
    >>
    Returns immediately

    View Slide

  50. >> launcher = Launcher.new
    => #
    >> launcher.async.launch
    => nil
    >>
    3...

    View Slide

  51. >> launcher = Launcher.new
    => #
    >> launcher.async.launch
    => nil
    >>
    3...
    2...

    View Slide

  52. >> launcher = Launcher.new
    => #
    >> launcher.async.launch
    => nil
    >>
    3...
    2...
    1...

    View Slide

  53. >> launcher = Launcher.new
    => #
    >> launcher.async.launch
    => nil
    >>
    3...
    2...
    1...
    BLASTOFF!

    View Slide

  54. Asynchronous Calls
    &DOOHU
    &HOOXORLG
    $V\QF3UR[\
    &$// 5HFHLYHU
    &HOOXORLG
    0DLOER[
    &HOOXORLG$FWRU
    &HOOXORLG&DOO

    View Slide

  55. >> launcher1, launcher2 = Launcher.new, Launcher.new
    => [#,
    #]
    Easy Parallelism

    View Slide

  56. >> launcher1, launcher2 = Launcher.new, Launcher.new
    => [#,
    #]
    >> launcher1.async.launch; launcher2.async.launch
    => nil
    >>
    Easy Parallelism

    View Slide

  57. >> launcher1, launcher2 = Launcher.new, Launcher.new
    => [#,
    #]
    >> launcher1.async.launch; launcher2.async.launch
    => nil
    >>
    3...
    3...
    Easy Parallelism

    View Slide

  58. >> launcher1, launcher2 = Launcher.new, Launcher.new
    => [#,
    #]
    >> launcher1.async.launch; launcher2.async.launch
    => nil
    >>
    3...
    3...
    2...
    2...
    Easy Parallelism

    View Slide

  59. >> launcher1, launcher2 = Launcher.new, Launcher.new
    => [#,
    #]
    >> launcher1.async.launch; launcher2.async.launch
    => nil
    >>
    3...
    3...
    2...
    2...
    1...
    1...
    Easy Parallelism

    View Slide

  60. >> launcher1, launcher2 = Launcher.new, Launcher.new
    => [#,
    #]
    >> launcher1.async.launch; launcher2.async.launch
    => nil
    >>
    3...
    3...
    2...
    2...
    1...
    1...
    BLASTOFF!!!
    BLASTOFF!!!
    Easy Parallelism

    View Slide

  61. Futures

    View Slide

  62. class FibonacciWorker
    include Celluloid
    def fib(n)
    n < 2 ? n : fib(n-1) + fib(n-2)
    end
    end

    View Slide

  63. >> worker = FibonacciWorker.new
    => #
    >> future = worker.future.fib(40)
    => #

    View Slide

  64. >> worker = FibonacciWorker.new
    => #
    >> future = worker.future.fib(40)
    => #
    Returns immediately

    View Slide

  65. >> worker = FibonacciWorker.new
    => #
    >> future = worker.future.fib(40)
    => #
    >> future.value

    View Slide

  66. >> worker = FibonacciWorker.new
    => #
    >> future = worker.future.fib(40)
    => #
    >> future.value
    Blocks until complete

    View Slide

  67. >> worker = FibonacciWorker.new
    => #
    >> future = worker.future.fib(40)
    => #
    >> future.value
    => 102334155

    View Slide

  68. &DOOHU
    &HOOXORLG
    $FWRU3UR[\
    5HFHLYHU
    &HOOXORLG
    0DLOER[
    &HOOXORLG$FWRU
    &HOOXORLG&DOO
    &$//
    &HOOXORLG
    0DLOER[
    )8785(
    &HOOXORLG
    )XWXUH
    &HOOXORLG5HVSRQVH
    9$/8("
    9$/8(
    Futures

    View Slide

  69. Pools

    View Slide

  70. >> pool = FibonacciWorker.pool(size: 16)
    => #

    View Slide

  71. >> pool = FibonacciWorker.pool
    => #
    1 actor per CPU
    (by default)

    View Slide

  72. >> pool = FibonacciWorker.pool
    => #
    >> (32...40).map { |n| pool.future.fib(n) }.map(&:value)

    View Slide

  73. >> pool = FibonacciWorker.pool
    => #
    >> (32...40).map { |n| pool.future.fib(n) }.map(&:value)
    => [2178309, 3524578, 5702887, 9227465, 14930352,
    24157817, 39088169, 63245986]

    View Slide

  74. What if an actor
    crashes?

    View Slide

  75. View Slide

  76. View Slide

  77. Fault Tolerance
    •Supervisors & Supervision Trees
    •“Let it crash!”
    •Restart in a clean state

    View Slide

  78. Ecosystem

    View Slide

  79. View Slide

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

    View Slide

  81. Distributed Celluloid

    View Slide

  82. View Slide

  83. Bleeding edge :(
    0.12.0 prerelease

    View Slide

  84. Cells are services

    View Slide

  85. DCell exposes them
    to the network

    View Slide

  86. Built on 0MQ
    •Fast, brokerless message queue
    •DCell uses push/pull sockets
    •Built on Celluloid::ZMQ/ffi-rzmq

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  91. Distributed objects
    have mostly been a
    failure

    View Slide

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

    View Slide

  93. Why?

    View Slide

  94. Not asynchronous
    Huge problem in distributed systems

    View Slide

  95. Not built on the Actor
    Model

    View Slide

  96. Actor Model is
    AWESOME!!!

    View Slide

  97. Unifying abstraction
    for both concurrency and distribution

    View Slide

  98. Success!

    View Slide

  99. Example

    View Slide

  100. You need
    Zookeeper :(

    View Slide

  101. What is Zookeeper?

    View Slide

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

    View Slide

  103. Zookeeper =
    Transactions

    View Slide

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

    View Slide

  105. zk gem
    •Used by the DCell Zookeeper backend
    •High level API to Zookeeper
    •Abstract leader election API
    https://github.com/slyphon/zk

    View Slide

  106. It’s not hard! :D

    View Slide

  107. Installing Zookeeper
    • git clone https://github.com/celluloid/dcell.git
    • cd dcell
    • rake zookeeper:install
    • rake zookeeper:start

    View Slide

  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

    View Slide

  109. Two Node Cluster
    •Node #1 is “itchy”
    •Node #2 is “scratchy”

    View Slide

  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
    => #
    Note itchy is on port 7777

    View Slide

  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
    => #
    Note scratchy is on port 7778

    View Slide

  112. Finding nodes
    •DCell::Node[‘itchy’]
    •DCell::Node.all
    •DCell.me

    View Slide

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

    View Slide

  114. >> info_service = DCell::Node['itchy'][:info]
    => #@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

    View Slide

  115. Defining DCell
    services

    View Slide

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

    View Slide

  117. >> Celluloid::Actor[:greeter] = Greeter.new
    => #
    >> Celluloid::Actor[:greeter].hello
    => "Hi"
    Naming Cells

    View Slide

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

    View Slide

  119. >> node = DCell::Node['itchy']
    => #
    >> node[:greeter].hello
    => "Hi"
    Calling Cells

    View Slide

  120. All Celluloid features
    supported
    •Synchronous calls
    •Asynchronous calls
    •Futures

    View Slide

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

    View Slide

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

    View Slide

  123. USE BLOCKING I/O

    View Slide

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

    View Slide

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

    View Slide

  126. Evented IO
    •Large numbers of connections (>10,000)
    •Mostly idle connections
    •Mostly IO-bound problems
    •Websockets are an ideal case

    View Slide

  127. Actors are
    event loops

    View Slide

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

    View Slide

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

    View Slide

  130. nio4r-powered
    reactor

    View Slide

  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/

    View Slide

  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)

    View Slide

  133. Evented IO
    AND
    Threaded IO
    You don’t have to choose!

    View Slide

  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

    View Slide

  135. Celluloid::IO-powered web server
    http://github.com/celluloid/reel

    View Slide

  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)

    View Slide

  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

    View Slide

  138. It’s got Websockets!

    View Slide

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

    View Slide

  140. Vaporware!

    View Slide

  141. Goals
    •Built out of parts of Rails
    •Webmachine for resources
    •Multithreaded development mode
    •Easy scatter/gather for SOA

    View Slide

  142. That’s all, folks!

    View Slide

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

    View Slide