foreign_actor 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +6 -3
- data/README.md +128 -15
- data/README.md.erb +68 -0
- data/Rakefile +7 -0
- data/TODO.todo +2 -0
- data/bin/{device → router} +1 -1
- data/docs/design.graphml +1 -11
- data/docs/design.png +0 -0
- data/example/Procfile +1 -1
- data/example/client.rb +21 -3
- data/{example2/device.yml → example/router.yml} +0 -0
- data/example/worker.rb +18 -18
- data/example2/Procfile +1 -1
- data/example2/node.rb +1 -2
- data/{example/device.yml → example2/router.yml} +0 -3
- data/foreign_actor.gemspec +3 -3
- data/lib/foreign_actor/client.rb +17 -13
- data/lib/foreign_actor/reactor.rb +4 -1
- data/lib/foreign_actor/reactor_mailbox.rb +1 -1
- data/lib/foreign_actor/version.rb +1 -1
- data/specs/spec_helper.rb +1 -0
- data/specs/unit/reactor_spec.rb +1 -0
- metadata +21 -33
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 55a8cb1bd967272cd69175d48bf7a2750d19fdb0
|
4
|
+
data.tar.gz: d6dc116fde6c0da3ff56db9d5d500de80fce65ac
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 483148ed774ac5a281bc359b84fc5c627f42b2e4064c6a60e0d738efdd65df7bee5d2680ed5327339760ab62a9e6b63e988eac4d57589809085221eb8e4dc1aa
|
7
|
+
data.tar.gz: 66aa30bda494c00cf099d64a4caae9abe7f134eb7e5af8099cf35a973f55d6caf60ecfd30fca7e6782838e1c9bc10b75da42afa4da83f20e7c21036ed73b6a30
|
data/Gemfile
CHANGED
@@ -3,12 +3,15 @@ source 'https://rubygems.org'
|
|
3
3
|
gemspec
|
4
4
|
|
5
5
|
gem 'rake'
|
6
|
-
|
6
|
+
|
7
|
+
group(:doc) do
|
8
|
+
gem 'tilt'
|
9
|
+
end
|
7
10
|
|
8
11
|
group(:test) do
|
9
|
-
gem 'eetee', '
|
12
|
+
gem 'eetee', '~> 0.0.6'
|
10
13
|
gem 'mocha', '~> 0.12.0'
|
11
|
-
gem 'factory_girl'
|
14
|
+
# gem 'factory_girl'
|
12
15
|
|
13
16
|
gem 'simplecov'
|
14
17
|
gem 'guard', '~> 1.5.4'
|
data/README.md
CHANGED
@@ -6,22 +6,19 @@ in multiple processes/machines, the idea is to be able to move an actor to anoth
|
|
6
6
|
|
7
7
|
|
8
8
|
# How ?
|
9
|
-
I used the Crossroads library
|
10
|
-
or without a return value.
|
9
|
+
I used the Crossroads library to achieve my goal using XREQ/XREP sockets, this allows multiple clients with multiple workers each clients being able to make calls with or without a return value.
|
11
10
|
|
12
11
|
Here is what it looks like:
|
13
|
-
![](docs/design.png "Architecture")
|
12
|
+
![](https://raw.github.com/schmurfy/foreign_actor/master/docs/design.png "Architecture")
|
14
13
|
|
15
14
|
In this picture the square boxes are processes while the rounded box are the services (actors).
|
16
|
-
In a standard client/server interaction one client connects to one or more server and to do so
|
17
|
-
|
18
|
-
|
19
|
-
Something to note is that each of these processes can run on a different physical machine
|
20
|
-
allowing effectively to distribute jobs on multiple cores as well as multiple machines.
|
15
|
+
In a standard client/server interaction one client connects to one or more server and to do so needs to know the addresses of all of them, in our case the router process is here to simplify this: both clients and workers connects to it which allows as a direct result things like adding
|
16
|
+
a worker in live, you just start it, it connects to the router and it can starts processing requests immediately.
|
17
|
+
Something to note is that each of these processes can run on a different physical machine allowing effectively to distribute jobs on multiple cores as well as multiple machines.
|
21
18
|
|
22
19
|
|
23
20
|
# Constraints
|
24
|
-
|
21
|
+
The methods arguments are serialized using MessagePack so obviously they need to be serializable by it, which means that you can only send "basic types" to a foreign actor, do not fear though since messagepack allows everything you need:
|
25
22
|
- Integers
|
26
23
|
- Floats (ex: 2.34)
|
27
24
|
- Strings (ex: "a cat")
|
@@ -30,15 +27,131 @@ Since the arguments to the actions are serialized using MessagePack they need to
|
|
30
27
|
- Nil
|
31
28
|
- Array (ex: [1, "a cat", {"a" : nil}])
|
32
29
|
|
33
|
-
Why not going with Marshal ? Because I don't want to close the door to another language, you
|
34
|
-
|
35
|
-
anything else, I just see no reason to close that door.
|
30
|
+
Why not going with Marshal ? Because I don't want to close the door to another language, you can well imagine calling a service which is in fact provided bya C server, a python server or anything else, I just saw no reason to close that door.
|
31
|
+
Another problem I have with Marshal is that the format could well change in a future ruby version which would force users to switch both the server and the client at the same time.
|
36
32
|
|
37
33
|
|
38
34
|
# Examples
|
39
|
-
You can look at the example and example2 folder
|
35
|
+
You can look at the example and example2 folder but here is the code of example1:
|
40
36
|
|
37
|
+
## Client
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
require 'rubygems'
|
41
|
+
require 'bundler/setup'
|
42
|
+
|
43
|
+
require 'foreign_actor'
|
44
|
+
|
45
|
+
# disable buffering to see our logs with foreman
|
46
|
+
$stdout.sync = true
|
47
|
+
|
48
|
+
# Here we create a supervision group to start the reactor which
|
49
|
+
# is an actor specific to foreign_actor and which is required.
|
50
|
+
# The reactor need to be named since the client will access
|
51
|
+
# it by its name which is :xs_reactor by default.
|
52
|
+
class ClientGroup < Celluloid::SupervisionGroup
|
53
|
+
supervise ForeignActor::Reactor, :as => :xs_reactor
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
ClientGroup.run!
|
58
|
+
|
59
|
+
# we create the "client" and give it the address to connect
|
60
|
+
# to (defined in router.yml as the front address)
|
61
|
+
cl = ForeignActor::Client.new('tcp://127.0.0.1:7000')
|
62
|
+
|
63
|
+
# and now we have standard celluloid code which
|
64
|
+
# does what you would expect.
|
65
|
+
|
66
|
+
# run an async task
|
67
|
+
cl.async.do_it(0)
|
68
|
+
|
69
|
+
loop do
|
70
|
+
f = []
|
71
|
+
|
72
|
+
# run a synchronous task and display the result
|
73
|
+
p cl.do_it(2)
|
74
|
+
|
75
|
+
# use future to run 4 tasks in parallel
|
76
|
+
# and display their results
|
77
|
+
started_at = Time.now
|
78
|
+
4.times {|n| f << cl.future.do_it(n) }
|
79
|
+
|
80
|
+
p f.map(&:value)
|
81
|
+
|
82
|
+
elapsed = (Time.now - started_at)
|
83
|
+
puts "time: #{elapsed} seconds"
|
84
|
+
end
|
85
|
+
```
|
86
|
+
|
87
|
+
## Server
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
require 'rubygems'
|
91
|
+
require 'bundler/setup'
|
92
|
+
|
93
|
+
require 'foreign_actor'
|
94
|
+
|
95
|
+
# disable buffering to see our logs with foreman
|
96
|
+
$stdout.sync = true
|
97
|
+
|
98
|
+
class Worker
|
99
|
+
include Celluloid
|
100
|
+
|
101
|
+
def initialize(endpoint)
|
102
|
+
# register this actor as a worker, this allows the actor to
|
103
|
+
# handle requested received on this endpoint (defined in
|
104
|
+
# router.yml as the back address)
|
105
|
+
Actor[:xs_reactor].serve_actor!(endpoint, Actor.current)
|
106
|
+
end
|
107
|
+
|
108
|
+
def do_it(n)
|
109
|
+
# force the process to sleep, we do not wait
|
110
|
+
# to use the celluloid sleep here
|
111
|
+
Kernel.sleep 1
|
112
|
+
|
113
|
+
"response #{$$}"
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
# define our reactor actor and our worker, we pass it the endpoint
|
119
|
+
# to connect to but you may well hardcode above or pass it anyway
|
120
|
+
# you like.
|
121
|
+
class RootGroup < Celluloid::SupervisionGroup
|
122
|
+
supervise ForeignActor::Reactor, :as => :xs_reactor
|
123
|
+
supervise Worker, :as => :worker1, args: ['tcp://127.0.0.1:7001']
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
RootGroup.run!
|
128
|
+
|
129
|
+
puts "Worker started."
|
130
|
+
|
131
|
+
trap("INT") { Celluloid.shutdown; exit }
|
132
|
+
sleep
|
133
|
+
```
|
134
|
+
|
135
|
+
## Router configuration
|
136
|
+
|
137
|
+
```yaml
|
138
|
+
endpoint1:
|
139
|
+
front: 'tcp://*:7000'
|
140
|
+
back: 'tcp://*:7001'
|
141
|
+
|
142
|
+
```
|
143
|
+
|
144
|
+
You then run the client and server as normal:
|
145
|
+
```bash
|
146
|
+
ruby client.rb
|
147
|
+
ruby worker.rb
|
148
|
+
```
|
149
|
+
|
150
|
+
And you need a router:
|
151
|
+
```bash
|
152
|
+
# by default the router.yml file in the current directory will
|
153
|
+
# be used if none specified
|
154
|
+
bundle exec router router.yml
|
155
|
+
```
|
41
156
|
|
42
|
-
# TODO
|
43
|
-
- handle workers crash better, the client should be informed instead of waiting for a timeout.
|
44
157
|
|
data/README.md.erb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
# What is this ?
|
2
|
+
|
3
|
+
I wanted to combine two great libraries for concurrent work: Crossroads (zeromq fork) and Celluloid.
|
4
|
+
Foreign Actor is my attempt at building a simple way to distribute celluloid actors
|
5
|
+
in multiple processes/machines, the idea is to be able to move an actor to another process with no change to the actor itself and minimal changes on the server and client side code.
|
6
|
+
|
7
|
+
|
8
|
+
# How ?
|
9
|
+
I used the Crossroads library to achieve my goal using XREQ/XREP sockets, this allows multiple clients with multiple workers each clients being able to make calls with or without a return value.
|
10
|
+
|
11
|
+
Here is what it looks like:
|
12
|
+
![](https://raw.github.com/schmurfy/foreign_actor/master/docs/design.png "Architecture")
|
13
|
+
|
14
|
+
In this picture the square boxes are processes while the rounded box are the services (actors).
|
15
|
+
In a standard client/server interaction one client connects to one or more server and to do so needs to know the addresses of all of them, in our case the router process is here to simplify this: both clients and workers connects to it which allows as a direct result things like adding
|
16
|
+
a worker in live, you just start it, it connects to the router and it can starts processing requests immediately.
|
17
|
+
Something to note is that each of these processes can run on a different physical machine allowing effectively to distribute jobs on multiple cores as well as multiple machines.
|
18
|
+
|
19
|
+
|
20
|
+
# Constraints
|
21
|
+
The methods arguments are serialized using MessagePack so obviously they need to be serializable by it, which means that you can only send "basic types" to a foreign actor, do not fear though since messagepack allows everything you need:
|
22
|
+
- Integers
|
23
|
+
- Floats (ex: 2.34)
|
24
|
+
- Strings (ex: "a cat")
|
25
|
+
- Hash
|
26
|
+
- Boolean
|
27
|
+
- Nil
|
28
|
+
- Array (ex: [1, "a cat", {"a" : nil}])
|
29
|
+
|
30
|
+
Why not going with Marshal ? Because I don't want to close the door to another language, you can well imagine calling a service which is in fact provided bya C server, a python server or anything else, I just saw no reason to close that door.
|
31
|
+
Another problem I have with Marshal is that the format could well change in a future ruby version which would force users to switch both the server and the client at the same time.
|
32
|
+
|
33
|
+
|
34
|
+
# Examples
|
35
|
+
You can look at the example and example2 folder but here is the code of example1:
|
36
|
+
|
37
|
+
## Client
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
<%= File.read('example/client.rb') %>
|
41
|
+
```
|
42
|
+
|
43
|
+
## Server
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
<%= File.read('example/worker.rb') %>
|
47
|
+
```
|
48
|
+
|
49
|
+
## Router configuration
|
50
|
+
|
51
|
+
```yaml
|
52
|
+
<%= File.read('example/router.yml') %>
|
53
|
+
```
|
54
|
+
|
55
|
+
You then run the client and server as normal:
|
56
|
+
```bash
|
57
|
+
ruby client.rb
|
58
|
+
ruby worker.rb
|
59
|
+
```
|
60
|
+
|
61
|
+
And you need a router:
|
62
|
+
```bash
|
63
|
+
# by default the router.yml file in the current directory will
|
64
|
+
# be used if none specified
|
65
|
+
bundle exec router router.yml
|
66
|
+
```
|
67
|
+
|
68
|
+
|
data/Rakefile
CHANGED
@@ -4,6 +4,13 @@ require "bundler/gem_tasks"
|
|
4
4
|
|
5
5
|
task :default => :test
|
6
6
|
|
7
|
+
task :readme do
|
8
|
+
require 'tilt'
|
9
|
+
tmpl = Tilt::ERBTemplate.new(File.expand_path('../README.md.erb', __FILE__))
|
10
|
+
out = tmpl.render(nil)
|
11
|
+
File.write(File.expand_path('../README.md', __FILE__), out)
|
12
|
+
end
|
13
|
+
|
7
14
|
task :test do
|
8
15
|
|
9
16
|
# do not generate coverage report under travis
|
data/TODO.todo
ADDED
data/bin/{device → router}
RENAMED
data/docs/design.graphml
CHANGED
@@ -15,7 +15,6 @@
|
|
15
15
|
<graph edgedefault="directed" id="G">
|
16
16
|
<data key="d7"/>
|
17
17
|
<node id="n0">
|
18
|
-
<data key="d5"/>
|
19
18
|
<data key="d6">
|
20
19
|
<y:ShapeNode>
|
21
20
|
<y:Geometry height="26.0" width="92.0" x="590.0" y="489.0"/>
|
@@ -27,7 +26,6 @@
|
|
27
26
|
</data>
|
28
27
|
</node>
|
29
28
|
<node id="n1">
|
30
|
-
<data key="d5"/>
|
31
29
|
<data key="d6">
|
32
30
|
<y:ShapeNode>
|
33
31
|
<y:Geometry height="120.0" width="122.0" x="579.0" y="477.0"/>
|
@@ -39,19 +37,17 @@
|
|
39
37
|
</data>
|
40
38
|
</node>
|
41
39
|
<node id="n2">
|
42
|
-
<data key="d5"/>
|
43
40
|
<data key="d6">
|
44
41
|
<y:ShapeNode>
|
45
42
|
<y:Geometry height="41.0" width="109.0" x="495.0" y="390.0"/>
|
46
43
|
<y:Fill hasColor="false" transparent="false"/>
|
47
44
|
<y:BorderStyle color="#000000" type="line" width="2.0"/>
|
48
|
-
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="internal" modelPosition="c" textColor="#000000" visible="true" width="42.
|
45
|
+
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="internal" modelPosition="c" textColor="#000000" visible="true" width="42.490234375" x="33.2548828125" y="11.43359375">Router</y:NodeLabel>
|
49
46
|
<y:Shape type="rectangle"/>
|
50
47
|
</y:ShapeNode>
|
51
48
|
</data>
|
52
49
|
</node>
|
53
50
|
<node id="n3">
|
54
|
-
<data key="d5"/>
|
55
51
|
<data key="d6">
|
56
52
|
<y:ShapeNode>
|
57
53
|
<y:Geometry height="60.0" width="74.0" x="327.0" y="240.0"/>
|
@@ -63,7 +59,6 @@
|
|
63
59
|
</data>
|
64
60
|
</node>
|
65
61
|
<node id="n4">
|
66
|
-
<data key="d5"/>
|
67
62
|
<data key="d6">
|
68
63
|
<y:ShapeNode>
|
69
64
|
<y:Geometry height="60.0" width="74.0" x="452.0" y="240.0"/>
|
@@ -75,7 +70,6 @@
|
|
75
70
|
</data>
|
76
71
|
</node>
|
77
72
|
<node id="n5">
|
78
|
-
<data key="d5"/>
|
79
73
|
<data key="d6">
|
80
74
|
<y:ShapeNode>
|
81
75
|
<y:Geometry height="60.0" width="74.0" x="577.0" y="240.0"/>
|
@@ -87,7 +81,6 @@
|
|
87
81
|
</data>
|
88
82
|
</node>
|
89
83
|
<node id="n6">
|
90
|
-
<data key="d5"/>
|
91
84
|
<data key="d6">
|
92
85
|
<y:ShapeNode>
|
93
86
|
<y:Geometry height="60.0" width="74.0" x="702.0" y="240.0"/>
|
@@ -99,7 +92,6 @@
|
|
99
92
|
</data>
|
100
93
|
</node>
|
101
94
|
<node id="n7">
|
102
|
-
<data key="d5"/>
|
103
95
|
<data key="d6">
|
104
96
|
<y:ShapeNode>
|
105
97
|
<y:Geometry height="26.0" width="92.0" x="416.0" y="529.0"/>
|
@@ -111,7 +103,6 @@
|
|
111
103
|
</data>
|
112
104
|
</node>
|
113
105
|
<node id="n8">
|
114
|
-
<data key="d5"/>
|
115
106
|
<data key="d6">
|
116
107
|
<y:ShapeNode>
|
117
108
|
<y:Geometry height="26.0" width="92.0" x="416.0" y="489.0"/>
|
@@ -123,7 +114,6 @@
|
|
123
114
|
</data>
|
124
115
|
</node>
|
125
116
|
<node id="n9">
|
126
|
-
<data key="d5"/>
|
127
117
|
<data key="d6">
|
128
118
|
<y:ShapeNode>
|
129
119
|
<y:Geometry height="120.0" width="122.0" x="405.0" y="477.0"/>
|
data/docs/design.png
CHANGED
Binary file
|
data/example/Procfile
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
master: ruby ../bin/
|
1
|
+
master: ruby ../bin/router
|
2
2
|
worker: ruby worker.rb
|
data/example/client.rb
CHANGED
@@ -1,5 +1,15 @@
|
|
1
|
-
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
2
3
|
|
4
|
+
require 'foreign_actor'
|
5
|
+
|
6
|
+
# disable buffering to see our logs with foreman
|
7
|
+
$stdout.sync = true
|
8
|
+
|
9
|
+
# Here we create a supervision group to start the reactor which
|
10
|
+
# is an actor specific to foreign_actor and which is required.
|
11
|
+
# The reactor need to be named since the client will access
|
12
|
+
# it by its name which is :xs_reactor by default.
|
3
13
|
class ClientGroup < Celluloid::SupervisionGroup
|
4
14
|
supervise ForeignActor::Reactor, :as => :xs_reactor
|
5
15
|
|
@@ -7,16 +17,24 @@ end
|
|
7
17
|
|
8
18
|
ClientGroup.run!
|
9
19
|
|
10
|
-
#
|
11
|
-
|
20
|
+
# we create the "client" and give it the address to connect
|
21
|
+
# to (defined in router.yml as the front address)
|
22
|
+
cl = ForeignActor::Client.new('tcp://127.0.0.1:7000')
|
23
|
+
|
24
|
+
# and now we have standard celluloid code which
|
25
|
+
# does what you would expect.
|
12
26
|
|
27
|
+
# run an async task
|
13
28
|
cl.async.do_it(0)
|
14
29
|
|
15
30
|
loop do
|
16
31
|
f = []
|
17
32
|
|
33
|
+
# run a synchronous task and display the result
|
18
34
|
p cl.do_it(2)
|
19
35
|
|
36
|
+
# use future to run 4 tasks in parallel
|
37
|
+
# and display their results
|
20
38
|
started_at = Time.now
|
21
39
|
4.times {|n| f << cl.future.do_it(n) }
|
22
40
|
|
File without changes
|
data/example/worker.rb
CHANGED
@@ -1,42 +1,42 @@
|
|
1
|
-
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
|
4
|
+
require 'foreign_actor'
|
5
|
+
|
6
|
+
# disable buffering to see our logs with foreman
|
7
|
+
$stdout.sync = true
|
2
8
|
|
3
9
|
class Worker
|
4
10
|
include Celluloid
|
5
11
|
|
6
12
|
def initialize(endpoint)
|
7
|
-
|
13
|
+
# register this actor as a worker, this allows the actor to
|
14
|
+
# handle requested received on this endpoint (defined in
|
15
|
+
# router.yml as the back address)
|
16
|
+
Actor[:xs_reactor].async.serve_actor(endpoint, Actor.current)
|
8
17
|
end
|
9
18
|
|
10
19
|
def do_it(n)
|
11
|
-
#
|
12
|
-
#
|
13
|
-
# end
|
14
|
-
|
20
|
+
# force the process to sleep, we do not wait
|
21
|
+
# to use the celluloid sleep here
|
15
22
|
Kernel.sleep 1
|
16
23
|
|
17
|
-
#
|
18
|
-
# puts "did it !"
|
19
|
-
# end
|
20
|
-
|
21
|
-
"toto #{$$}"
|
24
|
+
"response #{$$}"
|
22
25
|
end
|
23
26
|
|
24
27
|
end
|
25
28
|
|
29
|
+
# define our reactor actor and our worker, we pass it the endpoint
|
30
|
+
# to connect to but you may well hardcode above or pass it anyway
|
31
|
+
# you like.
|
26
32
|
class RootGroup < Celluloid::SupervisionGroup
|
27
33
|
supervise ForeignActor::Reactor, :as => :xs_reactor
|
28
|
-
|
29
|
-
supervise Worker, :as => :worker1, args: [WORKERS_ENDPOINT]
|
34
|
+
supervise Worker, :as => :worker1, args: ['tcp://127.0.0.1:7001']
|
30
35
|
|
31
36
|
end
|
32
37
|
|
33
38
|
RootGroup.run!
|
34
39
|
|
35
|
-
# Celluloid::Actor[:workers].register_server(:worker1, Worker, WORKERS_ENDPOINT)
|
36
|
-
|
37
|
-
# Celluloid::Actor[:xs_reactor].serve_actor(WORKERS_ENDPOINT, Worker.new)
|
38
|
-
|
39
40
|
puts "Worker started."
|
40
41
|
|
41
|
-
trap("INT") { Celluloid.shutdown; exit }
|
42
42
|
sleep
|
data/example2/Procfile
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
master: ruby ../bin/
|
1
|
+
master: ruby ../bin/router
|
2
2
|
node: ruby node.rb
|
data/example2/node.rb
CHANGED
@@ -14,7 +14,7 @@ class Worker
|
|
14
14
|
include Celluloid
|
15
15
|
|
16
16
|
def initialize(endpoint)
|
17
|
-
Actor[:xs_reactor].serve_actor
|
17
|
+
Actor[:xs_reactor].async.serve_actor(endpoint, Actor.current)
|
18
18
|
end
|
19
19
|
|
20
20
|
def handle_it(token)
|
@@ -36,7 +36,6 @@ end
|
|
36
36
|
RootGroup.run!
|
37
37
|
|
38
38
|
puts "Node #{node_id} started."
|
39
|
-
trap("INT") { Celluloid.shutdown; exit }
|
40
39
|
|
41
40
|
cl = ForeignActor::Client.new(CLIENT_ENDPOINT, 4)
|
42
41
|
|
data/foreign_actor.gemspec
CHANGED
@@ -9,12 +9,12 @@ Gem::Specification.new do |gem|
|
|
9
9
|
gem.homepage = ""
|
10
10
|
|
11
11
|
gem.files = `git ls-files`.split($\)
|
12
|
-
gem.executables = ['
|
12
|
+
gem.executables = ['router']
|
13
13
|
gem.name = "foreign_actor"
|
14
14
|
gem.require_paths = ["lib"]
|
15
15
|
gem.version = ForeignWorker::VERSION
|
16
16
|
|
17
17
|
gem.add_dependency 'ffi-rxs', '~> 1.2.1'
|
18
|
-
gem.add_dependency 'celluloid', '
|
19
|
-
gem.add_dependency 'msgpack', '
|
18
|
+
gem.add_dependency 'celluloid', '~> 0.14.0'
|
19
|
+
gem.add_dependency 'msgpack', '~> 0.5.4'
|
20
20
|
end
|
data/lib/foreign_actor/client.rb
CHANGED
@@ -5,7 +5,7 @@ module ForeignActor
|
|
5
5
|
State = Struct.new(:state) do
|
6
6
|
attr_accessor :state
|
7
7
|
end
|
8
|
-
|
8
|
+
|
9
9
|
class ClientProxy < Celluloid::ActorProxy
|
10
10
|
class MethodMissingRedirector
|
11
11
|
def initialize(&block)
|
@@ -38,23 +38,27 @@ module ForeignActor
|
|
38
38
|
end
|
39
39
|
|
40
40
|
def method_missing(meth, *args, &block)
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
41
|
+
if (meth == :__send__) && (args[0] == :initialize)
|
42
|
+
super
|
43
|
+
else
|
44
|
+
# bang methods are async calls
|
45
|
+
async = false
|
46
|
+
if meth.match(/!$/)
|
47
|
+
meth = meth.to_s
|
48
|
+
meth.slice!(-1, 1)
|
49
|
+
async = true
|
50
|
+
end
|
51
|
+
|
52
|
+
do_call(async, meth, args)
|
47
53
|
end
|
48
|
-
|
49
|
-
do_call(type, meth, args)
|
50
54
|
end
|
51
|
-
|
55
|
+
|
52
56
|
private
|
53
57
|
def do_call(type, meth, args)
|
54
58
|
case type
|
55
|
-
when :async then Celluloid::Actor.async(@mailbox, :async_remote_request, meth, *args)
|
56
|
-
when :sync then Celluloid::Actor.call(@mailbox, :sync_remote_request, meth, *args)
|
57
|
-
when :future then Celluloid::Actor.future(@mailbox, :sync_remote_request, meth, *args)
|
59
|
+
when :async then ::Celluloid::Actor.async(@mailbox, :async_remote_request, meth, *args)
|
60
|
+
when :sync then ::Celluloid::Actor.call(@mailbox, :sync_remote_request, meth, *args)
|
61
|
+
when :future then ::Celluloid::Actor.future(@mailbox, :sync_remote_request, meth, *args)
|
58
62
|
end
|
59
63
|
end
|
60
64
|
|
@@ -25,7 +25,6 @@ module ForeignActor
|
|
25
25
|
|
26
26
|
@poller.register_readable(@control_socket_srv)
|
27
27
|
|
28
|
-
async(:run)
|
29
28
|
end
|
30
29
|
|
31
30
|
|
@@ -269,6 +268,10 @@ module ForeignActor
|
|
269
268
|
include Celluloid
|
270
269
|
mailbox_class ReactorMailbox
|
271
270
|
|
271
|
+
def initialize(*)
|
272
|
+
super
|
273
|
+
async.run()
|
274
|
+
end
|
272
275
|
end
|
273
276
|
|
274
277
|
|
data/specs/spec_helper.rb
CHANGED
data/specs/unit/reactor_spec.rb
CHANGED
metadata
CHANGED
@@ -1,20 +1,18 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: foreign_actor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.0.2
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Julien Ammous
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2013-05-11 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: ffi-rxs
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
17
|
- - ~>
|
20
18
|
- !ruby/object:Gem::Version
|
@@ -22,7 +20,6 @@ dependencies:
|
|
22
20
|
type: :runtime
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
24
|
- - ~>
|
28
25
|
- !ruby/object:Gem::Version
|
@@ -30,40 +27,36 @@ dependencies:
|
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
28
|
name: celluloid
|
32
29
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
30
|
requirements:
|
35
|
-
- -
|
31
|
+
- - ~>
|
36
32
|
- !ruby/object:Gem::Version
|
37
|
-
version: 0.
|
33
|
+
version: 0.14.0
|
38
34
|
type: :runtime
|
39
35
|
prerelease: false
|
40
36
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
37
|
requirements:
|
43
|
-
- -
|
38
|
+
- - ~>
|
44
39
|
- !ruby/object:Gem::Version
|
45
|
-
version: 0.
|
40
|
+
version: 0.14.0
|
46
41
|
- !ruby/object:Gem::Dependency
|
47
42
|
name: msgpack
|
48
43
|
requirement: !ruby/object:Gem::Requirement
|
49
|
-
none: false
|
50
44
|
requirements:
|
51
|
-
- -
|
45
|
+
- - ~>
|
52
46
|
- !ruby/object:Gem::Version
|
53
|
-
version: 0.4
|
47
|
+
version: 0.5.4
|
54
48
|
type: :runtime
|
55
49
|
prerelease: false
|
56
50
|
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
51
|
requirements:
|
59
|
-
- -
|
52
|
+
- - ~>
|
60
53
|
- !ruby/object:Gem::Version
|
61
|
-
version: 0.4
|
54
|
+
version: 0.5.4
|
62
55
|
description: Distributed Actors above Celluloid
|
63
56
|
email:
|
64
57
|
- schmurfy@gmail.com
|
65
58
|
executables:
|
66
|
-
-
|
59
|
+
- router
|
67
60
|
extensions: []
|
68
61
|
extra_rdoc_files: []
|
69
62
|
files:
|
@@ -72,20 +65,22 @@ files:
|
|
72
65
|
- Guardfile
|
73
66
|
- LICENSE
|
74
67
|
- README.md
|
68
|
+
- README.md.erb
|
75
69
|
- Rakefile
|
76
|
-
-
|
70
|
+
- TODO.todo
|
77
71
|
- bin/puppetmaster.rb
|
72
|
+
- bin/router
|
78
73
|
- docs/design.graphml
|
79
74
|
- docs/design.png
|
80
75
|
- example/Procfile
|
81
76
|
- example/client.rb
|
82
77
|
- example/common.rb
|
83
|
-
- example/
|
78
|
+
- example/router.yml
|
84
79
|
- example/worker.rb
|
85
80
|
- example2/Procfile
|
86
81
|
- example2/README.md
|
87
|
-
- example2/device.yml
|
88
82
|
- example2/node.rb
|
83
|
+
- example2/router.yml
|
89
84
|
- foreign_actor.gemspec
|
90
85
|
- lib/foreign_actor.rb
|
91
86
|
- lib/foreign_actor/client.rb
|
@@ -98,32 +93,25 @@ files:
|
|
98
93
|
- specs/unit/serializer_spec.rb
|
99
94
|
homepage: ''
|
100
95
|
licenses: []
|
96
|
+
metadata: {}
|
101
97
|
post_install_message:
|
102
98
|
rdoc_options: []
|
103
99
|
require_paths:
|
104
100
|
- lib
|
105
101
|
required_ruby_version: !ruby/object:Gem::Requirement
|
106
|
-
none: false
|
107
102
|
requirements:
|
108
|
-
- -
|
103
|
+
- - '>='
|
109
104
|
- !ruby/object:Gem::Version
|
110
105
|
version: '0'
|
111
|
-
segments:
|
112
|
-
- 0
|
113
|
-
hash: 2556761358232140633
|
114
106
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
115
|
-
none: false
|
116
107
|
requirements:
|
117
|
-
- -
|
108
|
+
- - '>='
|
118
109
|
- !ruby/object:Gem::Version
|
119
110
|
version: '0'
|
120
|
-
segments:
|
121
|
-
- 0
|
122
|
-
hash: 2556761358232140633
|
123
111
|
requirements: []
|
124
112
|
rubyforge_project:
|
125
|
-
rubygems_version:
|
113
|
+
rubygems_version: 2.0.3
|
126
114
|
signing_key:
|
127
|
-
specification_version:
|
115
|
+
specification_version: 4
|
128
116
|
summary: Distributed Actors
|
129
117
|
test_files: []
|