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 full-size slide

  2. We’re hiring!

    View full-size slide

  3. “Threads on Rails!”

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  6. Multicore Is The Future

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  9. When we have 100
    core CPUs...

    View full-size slide

  10. Will we run 100 VMs?
    Or one?

    View full-size slide

  11. Open Source Voice Application Framework
    Projects Using Celluloid

    View full-size slide

  12. OOP + Actor Model

    View full-size slide

  13. “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 full-size slide

  14. OOP
    Tools
    Classes
    Inheritance
    Messages

    View full-size slide

  15. Concurrency
    Tools
    Threads
    Locks
    Queues

    View full-size slide

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

    View full-size slide

  17. Active Objects
    Based on the Actor Model

    View full-size slide

  18. 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 full-size slide

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

    View full-size slide

  20. Pythons did it!

    View full-size slide

  21. Forgotten approach
    to concurrency?

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  24. >> launcher = Launcher.new
    => #

    View full-size slide

  25. Synchronous Calls

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  32. Asynchronous Calls

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  36. You’ll get them back
    in Celluloid 1.0

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  61. What if an actor
    crashes?

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  64. Distributed Celluloid

    View full-size slide

  65. Bleeding edge :(
    0.12.0 prerelease

    View full-size slide

  66. Cells are services

    View full-size slide

  67. DCell exposes them
    to the network

    View full-size slide

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

    View full-size slide

  69. •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 full-size slide

  70. 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 full-size slide

  71. 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 full-size slide

  72. "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 full-size slide

  73. Distributed objects
    have mostly been a
    failure

    View full-size slide

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

    View full-size slide

  75. Not asynchronous
    Huge problem in distributed systems

    View full-size slide

  76. Not built on the Actor
    Model

    View full-size slide

  77. Actor Model is
    AWESOME!!!

    View full-size slide

  78. Unifying abstraction
    for both concurrency and distribution

    View full-size slide

  79. You need
    Zookeeper :(

    View full-size slide

  80. What is Zookeeper?

    View full-size slide

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

    View full-size slide

  82. Zookeeper =
    Transactions

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  85. It’s not hard! :D

    View full-size slide

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

    View full-size slide

  87. $ 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 full-size slide

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

    View full-size slide

  89. “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 full-size slide

  90. “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 full-size slide

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

    View full-size slide

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

    View full-size slide

  93. >> 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 full-size slide

  94. Defining DCell
    services

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  102. USE BLOCKING I/O

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  106. Actors are
    event loops

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  109. nio4r-powered
    reactor

    View full-size slide

  110. 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 full-size slide

  111. 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 full-size slide

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

    View full-size slide

  113. 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 full-size slide

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

    View full-size slide

  115. 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 full-size slide

  116. 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 full-size slide

  117. It’s got Websockets!

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  120. That’s all, folks!

    View full-size slide

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

    View full-size slide