DCell

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. 1995
DCell is a simple and easy way to build distributed applications in Ruby. Somewhat similar to DRb, DCell lets you easily expose Ruby objects as network services, and call them remotely just like you would any other Ruby object. However, unlike DRb all objects in the system are concurrent. You can create and register several available services on a given node, obtain handles to them, and easily pass these handles around the network just like any other objects.

DCell is a distributed extension to Celluloid, which provides concurrent objects for Ruby with many of the features of Erlang, such as the ability to supervise objects and restart them when they crash, and also link to other objects and receive event notifications of when they crash. This makes it easier to build robust, fault-tolerant distributed systems.

DCell uses the 0MQ messaging protocol which provides a robust, fault-tolerant brokerless transport for asynchronous messages sent between nodes. DCell is built on top of the Celluloid::ZMQ library, which provides a Celluloid-oriented wrapper around the underlying ffi-rzmq library.

[~/ruby/celluloid]$ cat itchy.rb 
#!/usr/bin/env ruby
require 'dcell'

DCell.start :id => "itchy", :addr => "tcp://127.0.0.1:9001"

class Itchy
  include Celluloid

  def initialize
    puts "Ready for mayhem!"
    @n = 0
  end

  def fight
    @n = (@n % 6) + 1
    if @n <= 3
      puts "Bite!"
    else
      puts "Fight!"
    end
  end
end

Itchy.supervise_as :itchy
sleep
To configure an individual DCell node, we need to give it a node ID and a 0MQ address to bind to. Node IDs look like domain names, and 0MQ addresses look like URLs that start with tcp:

DCell.start :id => "itchy", :addr => "tcp://127.0.0.1:9001"

[~/ruby/celluloid]$ cat scratchy.rb 
#!/usr/bin/env ruby
require 'dcell'

DCell.start :id => "scratchy", :addr => "tcp://127.0.0.1:9002"
itchy_node = DCell::Node["itchy"]

puts "Fighting itchy! (check itchy's output)"

6.times do
  itchy_node[:itchy].fight
  sleep 1
end
To create a cluster, we need to start another Ruby VM and connect it to the first VM.

DCell.start :id => "scratchy", :addr => "tcp://127.0.0.1:9002"
itchy_node = DCell::Node["itchy"]

Once you have a cluster of multiple nodes, you can bootstrap additional nodes into the cluster by pointing them at any node, and all nodes will gossip about the newly added node.

[~/sinatra/sinatra-streaming/chat_with_streaming(master)]$ redis-server 
[17773] 24 Nov 22:59:48.546 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
[17773] 24 Nov 22:59:48.549 * Max number of open files set to 10032
                _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis 2.6.14 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._                                   
 (    '      ,       .-`  | `,    )     Running in stand alone mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379
 |    `-._   `._    /     _.-'    |     PID: 17773
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           http://redis.io        
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                                               

[17773] 24 Nov 22:59:48.557 # Server started, Redis version 2.6.14
[17773] 24 Nov 22:59:48.558 * The server is now ready to accept connections on port 6379

[~/ruby/celluloid]$ ruby itchy.rb 
Ready for mayhem!
I, [2013-11-24T23:02:37.512066 #17781]  INFO -- : Connected to scratchy
Bite!
Bite!
Bite!
Fight!
Fight!
Fight!
W, [2013-11-24T23:02:52.511423 #17781]  WARN -- : Communication with scratchy interrupted

[~/ruby/celluloid]$ ruby scratchy.rb 
Fighting itchy! (check itchy's output)
I, [2013-11-24T23:02:37.506941 #17813]  INFO -- : Connected to itchy
D, [2013-11-24T23:02:43.580602 #17813] DEBUG -- : Terminating 10 actors...
W, [2013-11-24T23:02:43.584986 #17813]  WARN -- : Terminating task: type=:call, meta={:method_name=>:run}, status=:zmqwait
[~/ruby/celluloid]$

Casiano Rodriguez León 2015-01-07