tribe 0.2.3 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +88 -40
- data/lib/tribe/actable.rb +21 -16
- data/lib/tribe/version.rb +1 -1
- metadata +7 -11
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7425172d0fe26f65ff963c4f0b2742c3e21373fd
|
4
|
+
data.tar.gz: c117f5e53b430eb17cd75b804cbabb804028cbdc
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5e233c30e21178435eb07e4b4fcf687d9dabb4b1dfe3832f056c2e25b120d26b25f4f3e835b76be4e1cfad13ad1402626900cc0b96855a9bca2e698eb43a3847
|
7
|
+
data.tar.gz: da9e75d8da2798be63f340ca80d2231d08f94be59618b4a5d8edc9597a76e22e5d60d678016faa366fe08e4ec2c7476794877e34cea2d43aa0ecb381da78985c
|
data/README.md
CHANGED
@@ -25,13 +25,13 @@ Or install it yourself as:
|
|
25
25
|
|
26
26
|
## Actors
|
27
27
|
|
28
|
-
Actors are light-weight objects
|
28
|
+
Actors are light-weight objects that use asynchronous message passing for communcation.
|
29
29
|
There are two types of methods that you create in your actors:
|
30
30
|
|
31
31
|
1. *Command handlers* are prefixed with "on_" and define the types of commands your actor will process.
|
32
32
|
2. *System handlers* are postfixed with "_handler" and are built into the actor system. These are used for exception, shutdown, and cleanup handling. It is important that you call the super method since their default behavior is used by the actor system.
|
33
33
|
|
34
|
-
To send a message you use the
|
34
|
+
To send a message you use the Actable#message! method and specify a command with an optional data parameter.
|
35
35
|
The return value will always be nil since messaging is asynchronous.
|
36
36
|
|
37
37
|
# Create your custom actor class.
|
@@ -42,7 +42,7 @@ The return value will always be nil since messaging is asynchronous.
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def on_my_custom(event)
|
45
|
-
puts "Received a custom event (#{event.inspect})"
|
45
|
+
puts "Received a custom event (#{event.inspect})."
|
46
46
|
end
|
47
47
|
|
48
48
|
def exception_handler(e)
|
@@ -64,13 +64,13 @@ The return value will always be nil since messaging is asynchronous.
|
|
64
64
|
# Send an event to each actor.
|
65
65
|
100.times do |i|
|
66
66
|
actor = Tribe.registry["my_actor_#{i}"]
|
67
|
-
actor.
|
67
|
+
actor.message!(:my_custom, 'hello world')
|
68
68
|
end
|
69
69
|
|
70
70
|
# Shutdown the actors.
|
71
71
|
100.times do |i|
|
72
72
|
actor = Tribe.registry["my_actor_#{i}"]
|
73
|
-
actor.
|
73
|
+
actor.shutdown!
|
74
74
|
end
|
75
75
|
|
76
76
|
#### Implementation
|
@@ -133,27 +133,20 @@ Both one-shot and periodic timers are provided.
|
|
133
133
|
# Shutdown the actors.
|
134
134
|
10.times do |i|
|
135
135
|
actor = Tribe.registry["my_actor_#{i}"]
|
136
|
-
actor.
|
136
|
+
actor.shutdown!
|
137
137
|
end
|
138
138
|
|
139
139
|
## Futures
|
140
140
|
|
141
|
-
As mentioned above, message passing with the
|
141
|
+
As mentioned above, message passing with the Actable#message! method is asynchronous and always returns nil.
|
142
142
|
This can be a pain since in many cases you will be interested in the result.
|
143
|
-
|
144
|
-
|
145
|
-
You can then use this object to obtain the result when it becomes available sometime in the future.
|
146
|
-
|
147
|
-
Tribe includes both blocking and non-blocking futures.
|
148
|
-
You should prefer to use non-blocking futures for performance reasons (see details below).
|
149
|
-
|
150
|
-
In situations where an actor dies, your future will receive the raised exception as the result.
|
143
|
+
The Actable#future! method solves this problem by returning a Tribe::Future object instead of nil.
|
144
|
+
You can then use this object to obtain the result when it becomes available.
|
151
145
|
|
152
146
|
#### Non-blocking
|
153
147
|
|
154
148
|
Non-blocking futures are asynchronous and use callbacks.
|
155
|
-
No waiting for a result is involved.
|
156
|
-
The actor will continue to process other events.
|
149
|
+
No waiting for a result is involved and the actor will continue to process other events.
|
157
150
|
|
158
151
|
class ActorA < Tribe::Actor
|
159
152
|
private
|
@@ -165,16 +158,16 @@ The actor will continue to process other events.
|
|
165
158
|
def on_start(event)
|
166
159
|
friend = registry['actor_b']
|
167
160
|
|
168
|
-
future = friend.
|
161
|
+
future = friend.future!(:compute, 10)
|
169
162
|
|
170
163
|
future.success do |result|
|
171
|
-
perform do
|
164
|
+
perform! do
|
172
165
|
puts "ActorA (#{identifier}) future result: #{result}"
|
173
166
|
end
|
174
167
|
end
|
175
168
|
|
176
169
|
future.failure do |exception|
|
177
|
-
perform do
|
170
|
+
perform! do
|
178
171
|
puts "ActorA (#{identifier}) future failure: #{exception}"
|
179
172
|
end
|
180
173
|
end
|
@@ -200,14 +193,14 @@ The actor will continue to process other events.
|
|
200
193
|
actor_a = ActorA.new(:name => 'actor_a')
|
201
194
|
actor_b = ActorB.new(:name => 'actor_b')
|
202
195
|
|
203
|
-
actor_a.
|
196
|
+
actor_a.message!(:start)
|
204
197
|
|
205
|
-
actor_a.
|
206
|
-
actor_b.
|
198
|
+
actor_a.shutdown!
|
199
|
+
actor_b.shutdown!
|
207
200
|
|
208
|
-
*Important*: You must use
|
201
|
+
*Important*: You must use Actable#perform! inside the above callbacks.
|
209
202
|
This ensures that your code executes within the context of the correct actor.
|
210
|
-
Failure to do so will result in
|
203
|
+
Failure to do so will result in unexpected behavior (thread safety will be lost)!
|
211
204
|
|
212
205
|
#### Blocking
|
213
206
|
|
@@ -224,7 +217,7 @@ The actor won't process any other events until the future has a result.
|
|
224
217
|
def on_start(event)
|
225
218
|
friend = registry['actor_b']
|
226
219
|
|
227
|
-
future = friend.
|
220
|
+
future = friend.future!(:compute, 10)
|
228
221
|
|
229
222
|
future.wait # The current thread will sleep until a result is available.
|
230
223
|
|
@@ -255,32 +248,87 @@ The actor won't process any other events until the future has a result.
|
|
255
248
|
actor_a = ActorA.new(:name => 'actor_a')
|
256
249
|
actor_b = ActorB.new(:name => 'actor_b')
|
257
250
|
|
258
|
-
actor_a.
|
251
|
+
actor_a.message!(:start)
|
252
|
+
|
253
|
+
actor_a.shutdown!
|
254
|
+
actor_b.shutdown!
|
255
|
+
|
256
|
+
#### Performance Summary
|
257
|
+
|
258
|
+
Below you will find a summary of performance recommendations regarding the use of futures:
|
259
|
+
|
260
|
+
- Use Actable#message! unless you really need Actable#future! since futures have overhead.
|
261
|
+
- If you use Actable#future!, prefer the non-blocking API over the blocking one.
|
262
|
+
- If you use the blocking API, the actor calling Future#wait should use a dedicated worker thread.
|
263
|
+
|
264
|
+
## Forwarding
|
265
|
+
|
266
|
+
Messages and futures can be forwarded to other actors.
|
267
|
+
This lets you build actors that delegate work to other actors.
|
268
|
+
|
269
|
+
# Create your router class.
|
270
|
+
class MyRouter < Tribe::Actor
|
271
|
+
private
|
272
|
+
def initialize(options = {})
|
273
|
+
super
|
274
|
+
@processors = 100.times.map { MyProcessor.new }
|
275
|
+
end
|
276
|
+
|
277
|
+
def on_process(event)
|
278
|
+
@processors[rand(100)].forward!(event)
|
279
|
+
end
|
280
|
+
|
281
|
+
def exception_handler(e)
|
282
|
+
super
|
283
|
+
puts concat_e("MyRouter (#{identifier}) died.", e)
|
284
|
+
end
|
285
|
+
|
286
|
+
def shutdown_handler(event)
|
287
|
+
super
|
288
|
+
puts "MyRouter (#{identifier}) is shutting down. Put cleanup code here."
|
289
|
+
@processors.each { |p| p.shutdown! }
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
# Create your processor class.
|
294
|
+
class MyProcessor < Tribe::Actor
|
295
|
+
private
|
296
|
+
def initialize(options = {})
|
297
|
+
super
|
298
|
+
end
|
259
299
|
|
260
|
-
|
261
|
-
|
300
|
+
def on_process(event)
|
301
|
+
puts "MyProcessor (#{identifier}) received a process event (#{event.inspect})."
|
302
|
+
end
|
262
303
|
|
263
|
-
|
304
|
+
def exception_handler(e)
|
305
|
+
super
|
306
|
+
puts concat_e("MyProcessor (#{identifier}) died.", e)
|
307
|
+
end
|
264
308
|
|
265
|
-
|
266
|
-
|
309
|
+
def shutdown_handler(event)
|
310
|
+
super
|
311
|
+
puts "MyProcessor (#{identifier}) is shutting down. Put cleanup code here."
|
312
|
+
end
|
313
|
+
end
|
267
314
|
|
268
|
-
|
269
|
-
|
315
|
+
# Create the router.
|
316
|
+
router = MyRouter.new(:name => 'router')
|
270
317
|
|
271
|
-
|
272
|
-
|
318
|
+
# Send an event to the router and it will forward it to a random processor.
|
319
|
+
100.times do |i|
|
320
|
+
router.message!(:process, i)
|
321
|
+
end
|
273
322
|
|
274
|
-
|
275
|
-
|
276
|
-
The downside to using dedicated actors is that they consume more resources and you can't have as many of them.
|
323
|
+
# Shutdown the router.
|
324
|
+
router.shutdown!
|
277
325
|
|
278
326
|
## TODO - missing features
|
279
327
|
|
280
328
|
- Supervisors.
|
281
329
|
- Linking.
|
282
330
|
- Future timeouts.
|
283
|
-
-
|
331
|
+
- Remote actors (Tribe clustering).
|
284
332
|
|
285
333
|
## Contributing
|
286
334
|
|
data/lib/tribe/actable.rb
CHANGED
@@ -32,27 +32,32 @@ module Tribe
|
|
32
32
|
# Thread safe public methods.
|
33
33
|
# Notes: These are the methods that actors use to communicate with each other.
|
34
34
|
# Actors should avoid sharing mutable state in order to remain thread safe.
|
35
|
+
# Methods with a ! are designed for asynchronous communication.
|
35
36
|
#
|
36
37
|
|
37
38
|
public
|
38
39
|
|
39
|
-
def
|
40
|
-
return
|
40
|
+
def message!(command, data = nil)
|
41
|
+
return forward!(Workers::Event.new(command, data))
|
42
|
+
end
|
43
|
+
|
44
|
+
def forward!(event)
|
45
|
+
return nil unless alive?
|
41
46
|
|
42
|
-
@_as.mailbox.push(
|
47
|
+
@_as.mailbox.push(event) do
|
43
48
|
@_as.pool.perform { process_events }
|
44
49
|
end
|
45
50
|
|
46
|
-
return
|
51
|
+
return nil
|
47
52
|
end
|
48
53
|
|
49
|
-
def
|
54
|
+
def future!(command, data = nil)
|
50
55
|
@_as.futures ||= Tribe::SafeSet.new # Lazy instantiation for performance.
|
51
56
|
|
52
57
|
future = Tribe::Future.new
|
53
58
|
@_as.futures.add(future)
|
54
59
|
|
55
|
-
perform do
|
60
|
+
perform! do
|
56
61
|
begin
|
57
62
|
result = event_handler(Workers::Event.new(command, data))
|
58
63
|
future.result = result
|
@@ -67,6 +72,14 @@ module Tribe
|
|
67
72
|
return future
|
68
73
|
end
|
69
74
|
|
75
|
+
def shutdown!
|
76
|
+
return message!(:shutdown)
|
77
|
+
end
|
78
|
+
|
79
|
+
def perform!(&block)
|
80
|
+
return message!(:perform, block)
|
81
|
+
end
|
82
|
+
|
70
83
|
def alive?
|
71
84
|
@_as.mailbox.synchronize { return @_as.alive }
|
72
85
|
end
|
@@ -79,14 +92,6 @@ module Tribe
|
|
79
92
|
return @_as.name ? "#{object_id}:#{@_as.name}" : object_id
|
80
93
|
end
|
81
94
|
|
82
|
-
def shutdown
|
83
|
-
return enqueue(:shutdown)
|
84
|
-
end
|
85
|
-
|
86
|
-
def perform(&block)
|
87
|
-
return enqueue(:perform, block)
|
88
|
-
end
|
89
|
-
|
90
95
|
#
|
91
96
|
# Private event handlers.
|
92
97
|
# Notes: These methods are designed to be overriden (make sure you call super).
|
@@ -145,7 +150,7 @@ module Tribe
|
|
145
150
|
|
146
151
|
timer = Workers::Timer.new(delay, :scheduler => @_as.scheduler) do
|
147
152
|
@_as.timers.delete(timer)
|
148
|
-
|
153
|
+
message!(command, data)
|
149
154
|
end
|
150
155
|
|
151
156
|
@_as.timers.add(timer)
|
@@ -159,7 +164,7 @@ module Tribe
|
|
159
164
|
@_as.timers ||= Tribe::SafeSet.new
|
160
165
|
|
161
166
|
timer = Workers::PeriodicTimer.new(delay, :scheduler => @_as.scheduler) do
|
162
|
-
|
167
|
+
message!(command, data)
|
163
168
|
unless alive?
|
164
169
|
@_as.timers.delete(timer)
|
165
170
|
timer.cancel
|
data/lib/tribe/version.rb
CHANGED
metadata
CHANGED
@@ -1,20 +1,18 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tribe
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.3.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Chad Remesch
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2013-06-
|
11
|
+
date: 2013-06-08 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: workers
|
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
|
@@ -54,27 +51,26 @@ files:
|
|
54
51
|
- tribe.gemspec
|
55
52
|
homepage: https://github.com/chadrem/tribe
|
56
53
|
licenses: []
|
54
|
+
metadata: {}
|
57
55
|
post_install_message:
|
58
56
|
rdoc_options: []
|
59
57
|
require_paths:
|
60
58
|
- lib
|
61
59
|
required_ruby_version: !ruby/object:Gem::Requirement
|
62
|
-
none: false
|
63
60
|
requirements:
|
64
|
-
- -
|
61
|
+
- - '>='
|
65
62
|
- !ruby/object:Gem::Version
|
66
63
|
version: '0'
|
67
64
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
68
|
-
none: false
|
69
65
|
requirements:
|
70
|
-
- -
|
66
|
+
- - '>='
|
71
67
|
- !ruby/object:Gem::Version
|
72
68
|
version: '0'
|
73
69
|
requirements: []
|
74
70
|
rubyforge_project:
|
75
|
-
rubygems_version:
|
71
|
+
rubygems_version: 2.0.3
|
76
72
|
signing_key:
|
77
|
-
specification_version:
|
73
|
+
specification_version: 4
|
78
74
|
summary: Actors are lightweight concurrent objects that use asynchronous message passing
|
79
75
|
for communication. Tribe focuses on high performance, low latency, an easy to use
|
80
76
|
API, and flexibility.
|