zactor 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/lib/zactor.rb DELETED
@@ -1,263 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- require 'ruby_interface'
3
- require 'active_support/all'
4
- require 'ffi-rzmq'
5
- require 'em-zeromq'
6
- require 'bson'
7
-
8
- module Zactor
9
- extend ActiveSupport::Autoload
10
-
11
- autoload :Broker
12
- autoload :ActorSub
13
- autoload :ActorPub
14
- autoload :Message
15
- require 'zactor/log_subscriber'
16
-
17
- mattr_accessor :stub
18
- mattr_accessor :zmq, :logger
19
-
20
- class << self
21
- def broker
22
- @broker
23
- end
24
-
25
- def broker_port
26
- @broker_port
27
- end
28
-
29
- def host
30
- @host
31
- end
32
-
33
- def start(broker_port, params = {})
34
- self.zmq ||= EM::ZeroMQ::Context.new(1)
35
- self.logger ||= Logger.new(STDOUT).tap { |o| o.level = params[:debug] ? Logger::DEBUG : Logger::INFO }
36
- @host = "#{params.delete(:host) || '0.0.0.0'}:#{broker_port}"
37
- @broker_port = broker_port
38
- @params = params
39
-
40
- logger.info "Starting Zactor"
41
- @broker = Broker.new :balancer => params[:balancer]
42
- end
43
-
44
- def get_actor(pid, options = {})
45
- h = options[:host] || host
46
- { 'identity' => pid, 'host' => h }
47
- end
48
-
49
- def register(zactor)
50
- @zactors ||= {}
51
- @zactors[zactor] = true
52
- end
53
-
54
- def deregister(zactor)
55
- @zactors.delete zactor
56
- end
57
-
58
- def finish
59
- @zactors.keys.each(&:finish)
60
- @broker.finish
61
- end
62
-
63
- def clear
64
- @zactors = {}
65
- end
66
- end
67
-
68
-
69
- module ZMQMEssages
70
- class ZMQMessage
71
- attr_accessor :res
72
- def initialize(&blk)
73
- @res = []
74
- blk.call self
75
- end
76
-
77
- def str(str)
78
- @res << ZMQ::Message.new.tap { |o| o.copy_in_string str.to_s }
79
- end
80
-
81
- def mes(message)
82
- @res << message
83
- end
84
- end
85
-
86
- def send_messages(messages)
87
- last = messages[-1]
88
- messages[0...-1].each do |mes|
89
- sent = @socket.send mes, ZMQ::NOBLOCK | ZMQ::SNDMORE
90
- break unless sent
91
- end
92
- sent = @socket.send last, ZMQ::NOBLOCK
93
- unless sent
94
- Zactor.logger.info "[Zactor] Error while sending messages"
95
- end
96
- end
97
-
98
- def messages(&blk)
99
- ZMQMessage.new(&blk).res
100
- end
101
- end
102
-
103
-
104
- extend RubyInterface
105
- interface :zactor do
106
- interfaced do
107
- self.zactor do
108
- event(:finish) { |o| o.finish }
109
- event(:link) do |o, msg|
110
- o.zactor.linked msg
111
- # msg.reply :ok
112
- end
113
- event(:link_ping) do |o, msg|
114
- msg.reply :pong
115
- end
116
- end
117
- end
118
-
119
- include ZMQMEssages
120
-
121
- class_attribute :identity_val
122
- class_attribute :events
123
- self.events = {}
124
- class << self
125
- def identity(val)
126
- self.identity_val = val
127
- end
128
-
129
- def event(name, &clb)
130
- self.events = self.events.merge name.to_sym => clb
131
- end
132
- end
133
- attr_accessor :actor, :identity
134
- attr_accessor :pubs
135
-
136
- # Инициализация, подписывается на сообщения для себя, создает сокет для отправки локальных сообщений
137
- def init
138
- @actor = Zactor.get_actor @identity || self.class.identity_val || "actor.#{owner.object_id}-#{Zactor.host}"
139
- Zactor.register self
140
- return if Zactor.stub
141
- @sub = make_sub
142
- @callbacks, @timeouts = {}, {}
143
- @pubs = {}
144
- @pubs["0.0.0.0:#{Zactor.broker_port}"] = make_pub "inproc://zactor_broker_sub"
145
- end
146
-
147
- # Закрываем все соедниения и чистим сисьему. Обязательно нужно делать, когда объект перестает существовать
148
- def finish
149
- Zactor.deregister self
150
- return if Zactor.stub
151
- if @linked
152
- @linked.each do |link|
153
- link.reply :finish
154
- end
155
- end
156
- @finished = true
157
- @sub.close
158
- @pubs.values.each(&:close)
159
- end
160
-
161
- def make_pub(endpoint)
162
- Zactor::ActorPub.new self, endpoint
163
- end
164
-
165
- def make_sub
166
- Zactor::ActorSub.new self
167
- end
168
-
169
- def send_request(actor, event, *args, &clb)
170
- return if @finished || Zactor.stub
171
- @callbacks[clb.object_id.to_s] = clb if clb
172
- send_to actor, messages { |m|
173
- m.str 'request'
174
- m.str "#{clb ? clb.object_id : ''}"
175
- m.str event
176
- m.str BSON.serialize({ 'args' => args })
177
- }
178
- @last_callback = clb.object_id.to_s
179
- self
180
- end
181
-
182
- def timeout(secs, &clb)
183
- raise "Only for requests" unless @last_callback
184
- @timeouts[@last_callback] = EM.add_timer(secs, &clb)
185
- end
186
-
187
- def send_reply(actor, callback_id, *args)
188
- return if @finished || Zactor.stub
189
- Zactor.logger.debug "Zactor: send reply"
190
- send_to actor, messages { |m|
191
- m.str 'reply'
192
- m.str callback_id
193
- m.str BSON.serialize({ 'args' => args })
194
- }
195
- end
196
-
197
- def send_to(actor, mes = [])
198
- pub = @pubs[actor['host']] ||= make_pub("tcp://#{actor['host']}")
199
- pub.send_messages(messages { |m|
200
- m.str actor['identity']
201
- m.str bson_actor
202
- } + mes)
203
- end
204
-
205
- def link(actor, &clb)
206
- Zactor.logger.debug { "Zactor: link #{actor} with #{self.actor}"}
207
- send_request actor, :link do
208
- clb.call
209
- end
210
- link_ping actor, &clb
211
- self
212
- end
213
-
214
- def link_ping(actor, &clb)
215
- EM.add_timer(5) do
216
- unless @finished
217
- send_request(actor, :link_ping) do
218
- link_ping actor, &clb
219
- end.timeout(5) do
220
- clb.call
221
- end
222
- end
223
- end
224
- end
225
-
226
- def linked(msg)
227
- Zactor.logger.debug { "Zactor: linked #{actor} with #{msg.sender}"}
228
- @linked ||= []
229
- @linked << msg
230
- end
231
-
232
- def receive_reply(callback_id, *args)
233
- Zactor.logger.debug "Zactor: receive reply"
234
- if (callback = @callbacks[callback_id])
235
- if timeout = @timeouts[callback_id]
236
- EM.cancel_timer timeout
237
- end
238
- callback.call(*args)
239
- end
240
- end
241
-
242
- def receive_request(sender, event_name, callback_id, *args)
243
- Zactor.logger.debug "Zactor: receive request"
244
- mes = Message.new self, :sender => sender, :callback_id => callback_id, :args => args
245
- if (event = self.class.events[event_name.to_sym])
246
- event.call owner, mes, *args
247
- else
248
- raise "Undefined event #{event_name}"
249
- end
250
- end
251
-
252
- def bson_actor
253
- @bson_actor ||= BSON.serialize(@actor).to_s
254
- end
255
-
256
-
257
- def inspect
258
- "<#Zactor Interface for #{owner.inspect}"
259
- end
260
-
261
- end
262
-
263
- end
@@ -1,136 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- require 'spec_helper'
4
-
5
- describe "Zactor actor" do
6
- class A
7
- include Zactor
8
- end
9
- before do
10
- stub(Zactor::Broker).new
11
- Zactor.start 8000
12
- end
13
- describe "init" do
14
- let(:actor) { A.new.zactor }
15
- let(:sub) { Object.new }
16
- let(:local_pub) { Object.new }
17
- before do
18
- stub(actor).make_sub { sub }
19
- stub(actor).make_pub { local_pub }
20
- end
21
- it "должен зарегистрировать себя" do
22
- mock(Zactor).register actor
23
- actor.init
24
- end
25
-
26
- it "должен создать sub сокет" do
27
- mock(actor).make_sub { sub }
28
- actor.init
29
- actor.instance_eval { @sub }.should eq(sub)
30
- end
31
-
32
- it "должен создать pub сокет для локальных вызовов" do
33
- mock(actor).make_pub("inproc://zactor_broker_sub") { local_pub }
34
- actor.init
35
- actor.instance_eval { @pubs['0.0.0.0:8000'] }.should eq(local_pub)
36
- end
37
-
38
- describe "identity" do
39
- it "по-умолчанию" do
40
- actor.init
41
- actor.actor.should eq({ 'identity' => "actor.#{actor.owner.object_id}-0.0.0.0:8000", 'host' => '0.0.0.0:8000' })
42
- end
43
-
44
- it "с глобальным указанием" do
45
- A.zactor.identity "a"
46
- actor.init
47
- actor.actor.should eq({ 'identity' => "a", 'host' => '0.0.0.0:8000' })
48
- end
49
-
50
- it "с указанием для этого объекта" do
51
- actor.identity = "b"
52
- actor.init
53
- actor.actor.should eq({ 'identity' => "b", 'host' => '0.0.0.0:8000' })
54
- end
55
- end
56
-
57
- describe "after init" do
58
- include Zactor::ZMQMEssages
59
-
60
- def assert_messages(got, expected)
61
- got.map(&:copy_out_string).should eq(expected.map(&:copy_out_string))
62
- end
63
-
64
- before do
65
- actor.init
66
- end
67
- describe "send_to" do
68
- it "должен отправить в локальный pub для локального объекта" do
69
- mes = messages { |m| }
70
- stub(local_pub).send_messages do |mes|
71
- assert_messages mes, messages { |m|
72
- m.str('b')
73
- m.str(actor.bson_actor)
74
- }
75
- end
76
- actor.send_to Zactor.get_actor('b')
77
- end
78
-
79
- it "должен отправить в удаленный pub для удаленного объекта, перед этим создав его" do
80
- remote_pub = Object.new
81
- mock(actor).make_pub("tcp://192.168.1.1:3000") { remote_pub }
82
- stub(remote_pub).send_messages do |mes|
83
- assert_messages mes, messages { |m| m.str('b'); m.str(actor.bson_actor) }
84
- end
85
- actor.send_to Zactor.get_actor('b', :host => '192.168.1.1:3000')
86
- end
87
- end
88
-
89
- describe "send_request" do
90
- it "должен отправлять сообщение типа request" do
91
- stub(local_pub).send_messages do |mes|
92
- assert_messages mes, messages { |m|
93
- m.str('b')
94
- m.str(actor.bson_actor)
95
-
96
- m.str 'request'
97
- m.str ""
98
- m.str "show"
99
- m.str BSON.serialize({ 'args' => ['foo', :bar] })
100
- }
101
- end
102
- actor.send_request Zactor.get_actor('b'), :show, 'foo', :bar
103
- end
104
- end
105
-
106
- describe "send_reply" do
107
- it "должен отправлять сообщение типа reply" do
108
- stub(local_pub).send_messages do |mes|
109
- assert_messages mes, messages { |m|
110
- m.str('b')
111
- m.str(actor.bson_actor)
112
-
113
- m.str 'reply'
114
- m.str 5
115
- m.str BSON.serialize({ 'args' => ['foo', :bar] })
116
- }
117
- end
118
- actor.send_reply Zactor.get_actor('b'), 5, 'foo', :bar
119
- end
120
- end
121
-
122
- describe "receive_reply" do
123
-
124
- end
125
-
126
- describe "receive_request" do
127
-
128
- end
129
-
130
- describe "finish" do
131
-
132
- end
133
- end
134
-
135
- end
136
- end
@@ -1,81 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- require 'spec_helper'
4
- require "em-spec/rspec"
5
-
6
- describe "Zactor" do
7
- include EM::SpecHelper
8
- module Exchange
9
- class A
10
- include Zactor
11
-
12
- def initialize
13
- zactor.init
14
- end
15
-
16
- def ping(actor = Zactor.get_actor("b"))
17
- zactor.send_request actor, :ping do |res|
18
- reply
19
- end
20
- end
21
-
22
- def reply
23
-
24
- end
25
- end
26
-
27
- class B
28
- include Zactor
29
-
30
- zactor do
31
- identity "b"
32
-
33
- event(:ping) do |o, msg|
34
- o.receive
35
- msg.reply "Pong!"
36
- end
37
- end
38
-
39
- def initialize
40
- zactor.init
41
- end
42
-
43
- def receive
44
-
45
- end
46
- end
47
- end
48
-
49
- after do
50
- Zactor.clear
51
- Zactor.finish
52
- end
53
-
54
- it "B должен получить сообщение" do
55
- em do
56
- Zactor.start 8000, :debug => true
57
- Exchange::A.new.ping
58
- b = Exchange::B.new
59
- mock.proxy(b).receive { done }
60
- end
61
- end
62
-
63
- it "A должен получить ответ" do
64
- em do
65
- Zactor.start 8000, :debug => true
66
- a = Exchange::A.new
67
- a.ping
68
- Exchange::B.new
69
- mock.proxy(a).reply { done }
70
- end
71
- end
72
-
73
- it "Если задан таймаут, то он должен срабатывать" do
74
- em(7) do
75
- Zactor.start 8000, :debug => true
76
- a = Exchange::A.new.ping.timeout(5) do
77
- done
78
- end
79
- end
80
- end
81
- end
@@ -1,67 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- require 'spec_helper'
4
- require "em-spec/rspec"
5
-
6
- describe "Zactor" do
7
- include EM::SpecHelper
8
- module Link
9
- class A
10
- include Zactor
11
-
12
- def initialize
13
- zactor.init
14
- end
15
- end
16
-
17
- class B
18
- include Zactor
19
-
20
- def initialize
21
- zactor.init
22
- end
23
- end
24
- end
25
-
26
- after do
27
- puts "FINISH ZACTOR"
28
- Zactor.finish
29
- end
30
-
31
- it "A должен уведомляться о смерти B" do
32
- em do
33
- Zactor.start 8000, :debug => true
34
- a = Link::A.new
35
- b = Link::B.new
36
- a.zactor.link b.zactor.actor do
37
- done
38
- end
39
- EM.add_timer(0.5) do
40
- b.zactor.finish
41
- end
42
- end
43
- end
44
-
45
- # it "A должен уведомляться о смерти B в случае удаления объекта гарбэйдж коллектором" do
46
- #
47
- # end
48
-
49
- it "A должен уведомляться о смерти B, даже если B закончил выполнение неожиданно" do
50
- em(12) do
51
- EM.add_timer(0.5) do #FIXME ZMQ-сокетам нужно время чтобы закрыться. Нужно придумать, что с этим делать
52
- Zactor.start 8000, :debug => true
53
- a = Link::A.new
54
- b = Link::B.new
55
- a.zactor.link b.zactor.actor do
56
- done
57
- end
58
- EM.add_timer(0.5) do
59
- b.zactor.instance_eval do
60
- @linked = []
61
- end
62
- b.zactor.finish
63
- end
64
- end
65
- end
66
- end
67
- end
@@ -1,40 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- require 'spec_helper'
4
-
5
- describe "Zactor" do
6
- describe "start" do
7
- before do
8
- stub(Zactor::Broker).new
9
- end
10
- it "должен выставлять хост по-умолчанию 0.0.0.0" do
11
- Zactor.start 8000
12
- Zactor.host.should eq('0.0.0.0:8000')
13
- end
14
-
15
- it "должен выставлять хост с учетом переданного" do
16
- Zactor.start 8000, :host => '192.168.1.1'
17
- Zactor.host.should eq('192.168.1.1:8000')
18
- end
19
-
20
- it "должен создавать новый брокер с указаным балансером" do
21
- mock(Zactor::Broker).new :balancer => '0.0.0.0:4000'
22
- Zactor.start 8000, :balancer => '0.0.0.0:4000'
23
- end
24
- end
25
-
26
- describe "get_actor" do
27
- before do
28
- stub(Zactor::Broker).new
29
- Zactor.start 8000
30
- end
31
- it "в качестве хоста по-умолчанию ставит себя же" do
32
- Zactor.get_actor("actor1").should eq({ 'identity' => 'actor1', 'host' => '0.0.0.0:8000' })
33
- end
34
-
35
-
36
- it "в качестве хоста ставит переданный" do
37
- Zactor.get_actor("actor1", :host => '192.168.1.1:3000').should eq({ 'identity' => 'actor1', 'host' => '192.168.1.1:3000' })
38
- end
39
- end
40
- end