asir 0.2.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +1 -2
- data/README.textile +4 -2
- data/VERSION +1 -1
- data/asir.gemspec +1 -4
- data/asir.riterate.yml +1 -0
- data/bin/asir +2 -1
- data/example/asir_control.sh +63 -1
- data/example/asir_control_client_http.rb +2 -2
- data/example/asir_control_client_resque.rb +16 -0
- data/example/asir_control_client_zmq.rb +3 -3
- data/example/config/asir_config.rb +20 -8
- data/example/ex02.rb +1 -1
- data/example/ex03.rb +2 -2
- data/example/ex04.rb +2 -2
- data/example/ex05.rb +1 -1
- data/example/ex06.rb +6 -5
- data/example/ex07.rb +2 -2
- data/example/ex08.rb +2 -2
- data/example/ex09.rb +2 -2
- data/example/ex10.rb +2 -2
- data/example/ex11.rb +5 -5
- data/example/ex12.rb +6 -6
- data/example/ex13.rb +4 -4
- data/example/ex14.rb +4 -4
- data/example/ex15.rb +2 -2
- data/example/ex16.rb +8 -8
- data/example/ex17.rb +12 -11
- data/example/ex18.rb +5 -5
- data/example/ex19.rb +3 -3
- data/example/ex20.rb +3 -3
- data/example/ex21.rb +3 -3
- data/example/ex22.rb +1 -1
- data/example/ex23.rb +2 -2
- data/example/ex24.rb +4 -4
- data/example/ex25.rb +41 -0
- data/example/example_helper.rb +38 -3
- data/example/sample_service.rb +4 -4
- data/hack_night/exercise/prob-3.rb +3 -3
- data/hack_night/exercise/prob-6.rb +2 -2
- data/hack_night/exercise/prob-7.rb +2 -2
- data/hack_night/solution/prob-2.rb +2 -2
- data/hack_night/solution/prob-3.rb +3 -3
- data/hack_night/solution/prob-6.rb +7 -6
- data/hack_night/solution/prob-7.rb +6 -6
- data/{spec → lab}/const_get_speed_spec.rb +0 -0
- data/lib/asir.rb +29 -7
- data/lib/asir/additional_data.rb +25 -0
- data/lib/asir/channel.rb +4 -5
- data/lib/asir/client.rb +29 -13
- data/lib/asir/config.rb +8 -0
- data/lib/asir/description.rb +34 -0
- data/lib/asir/environment.rb +96 -0
- data/lib/asir/error.rb +4 -1
- data/lib/asir/invoker.rb +14 -0
- data/lib/asir/main.rb +84 -103
- data/lib/asir/message.rb +1 -1
- data/lib/asir/poll_throttle.rb +53 -0
- data/lib/asir/retry_behavior.rb +1 -1
- data/lib/asir/thread_variable.rb +183 -0
- data/lib/asir/transport.rb +36 -23
- data/lib/asir/transport/beanstalk.rb +18 -52
- data/lib/asir/transport/conduit.rb +42 -0
- data/lib/asir/transport/connection_oriented.rb +32 -56
- data/lib/asir/transport/delegation.rb +5 -5
- data/lib/asir/transport/demux.rb +33 -0
- data/lib/asir/transport/file.rb +5 -3
- data/lib/asir/transport/payload_io.rb +8 -4
- data/lib/asir/transport/resque.rb +212 -0
- data/lib/asir/transport/stream.rb +19 -9
- data/lib/asir/transport/tcp_socket.rb +3 -2
- data/lib/asir/transport/zmq.rb +14 -17
- data/lib/asir/uri_config.rb +51 -0
- data/lib/asir/version.rb +1 -1
- data/spec/client_spec.rb +48 -0
- data/spec/demux_spec.rb +38 -0
- data/spec/json_spec.rb +0 -2
- data/spec/message_spec.rb +68 -0
- data/spec/performance_spec.rb +66 -0
- data/spec/spec_helper.rb +34 -0
- data/spec/thread_variable_spec.rb +135 -0
- data/spec/transport_spec.rb +82 -0
- metadata +28 -4
data/lib/asir/transport.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
require 'time'
|
2
|
+
require 'asir/thread_variable'
|
2
3
|
require 'asir/message/delay'
|
4
|
+
require 'asir/transport/conduit'
|
3
5
|
|
4
6
|
module ASIR
|
5
7
|
# !SLIDE
|
@@ -11,7 +13,7 @@ module ASIR
|
|
11
13
|
# Service: Send the Result to the Client.
|
12
14
|
# Client: Receive the Result from the Service.
|
13
15
|
class Transport
|
14
|
-
include Log, Initialization, AdditionalData, Message::Delay
|
16
|
+
include Log, Initialization, AdditionalData, Message::Delay, ThreadVariable, Conduit
|
15
17
|
|
16
18
|
attr_accessor :encoder, :decoder, :one_way
|
17
19
|
|
@@ -22,8 +24,8 @@ module ASIR
|
|
22
24
|
# * Receive decoded Result.
|
23
25
|
def send_message message
|
24
26
|
@message_count ||= 0; @message_count += 1
|
25
|
-
message.create_timestamp! if needs_message_timestamp?
|
26
|
-
message.create_identifier! if needs_message_identifier?
|
27
|
+
message.create_timestamp! if needs_message_timestamp? message
|
28
|
+
message.create_identifier! if needs_message_identifier? message
|
27
29
|
@before_send_message.call(self, message) if @before_send_message
|
28
30
|
relative_message_delay! message
|
29
31
|
message_payload = encoder.dup.encode(message)
|
@@ -57,13 +59,6 @@ module ASIR
|
|
57
59
|
# Send Result to stream.
|
58
60
|
def send_result result, stream, message_state
|
59
61
|
message = result.message
|
60
|
-
if @on_result_exception && result.exception
|
61
|
-
begin
|
62
|
-
@on_result_exception.call(self, result)
|
63
|
-
rescue ::Exception => exc
|
64
|
-
_log { [ :send_result, :result, result, :on_result_exception, exc ] }
|
65
|
-
end
|
66
|
-
end
|
67
62
|
if @one_way && message.block
|
68
63
|
message.block.call(result)
|
69
64
|
else
|
@@ -85,7 +80,7 @@ module ASIR
|
|
85
80
|
result = decoder.dup.decode(result_payload)
|
86
81
|
if result && ! message.one_way
|
87
82
|
if exc = result.exception
|
88
|
-
|
83
|
+
invoker.invoke!(exc, self)
|
89
84
|
else
|
90
85
|
if ! @one_way && message.block
|
91
86
|
message.block.call(result)
|
@@ -105,27 +100,27 @@ module ASIR
|
|
105
100
|
attr_accessor :message_count
|
106
101
|
|
107
102
|
# A Proc to call within #receive_message, after #_receive_message.
|
108
|
-
# trans.
|
103
|
+
# trans.after_receive_message(trans, message)
|
109
104
|
attr_accessor :after_receive_message
|
110
105
|
|
111
106
|
# A Proc to call within #send_message, before #_send_message.
|
112
107
|
# trans.before_send_message(trans, message)
|
113
108
|
attr_accessor :before_send_message
|
114
109
|
|
115
|
-
# Proc to call
|
110
|
+
# Proc to call with #invoke_message! if result.exception.
|
116
111
|
# trans.on_result_exception.call(trans, result)
|
117
112
|
attr_accessor :on_result_exception
|
118
113
|
|
119
114
|
# Proc to call with exception, if exception occurs within #serve_message!, but outside
|
120
115
|
# Message#invoke!.
|
121
116
|
#
|
122
|
-
# trans.on_exception.call(trans, exception, :message, Message_instance)
|
117
|
+
# trans.on_exception.call(trans, exception, :message, Message_instance, nil)
|
123
118
|
# trans.on_exception.call(trans, exception, :result, Message_instance, Result_instance)
|
124
119
|
attr_accessor :on_exception
|
125
120
|
|
126
121
|
attr_accessor :needs_message_identifier, :needs_message_timestamp
|
127
|
-
|
128
|
-
|
122
|
+
def needs_message_identifier? m; @needs_message_identifier; end
|
123
|
+
def needs_message_timestamp? m; @needs_message_timestamp; end
|
129
124
|
|
130
125
|
attr_accessor :verbose
|
131
126
|
|
@@ -154,7 +149,7 @@ module ASIR
|
|
154
149
|
rescue ::Exception => exc
|
155
150
|
exception = original_exception = exc
|
156
151
|
_log [ :message_error, exc ]
|
157
|
-
@on_exception.call(self, exc, :message, message) if @on_exception
|
152
|
+
@on_exception.call(self, exc, :message, message, nil) if @on_exception
|
158
153
|
ensure
|
159
154
|
begin
|
160
155
|
if message_ok
|
@@ -180,6 +175,7 @@ module ASIR
|
|
180
175
|
|
181
176
|
# !SLIDE
|
182
177
|
# Transport Server Support
|
178
|
+
attr_accessor :running
|
183
179
|
|
184
180
|
def stop! force = false
|
185
181
|
@running = false
|
@@ -225,12 +221,29 @@ module ASIR
|
|
225
221
|
|
226
222
|
# Invokes the Message object, returns a Result object.
|
227
223
|
def invoke_message! message
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
224
|
+
result = nil
|
225
|
+
Transport.with_attr! :current, self do
|
226
|
+
with_attr! :message, message do
|
227
|
+
wait_for_delay! message
|
228
|
+
result = invoker.invoke!(message, self)
|
229
|
+
# Hook for Exceptions.
|
230
|
+
if @on_result_exception && result.exception
|
231
|
+
@on_result_exception.call(self, result)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
result
|
236
|
+
end
|
237
|
+
# The current Message being handled.
|
238
|
+
attr_accessor_thread :message
|
239
|
+
|
240
|
+
# The current active Transport.
|
241
|
+
cattr_accessor_thread :current
|
242
|
+
|
243
|
+
# The Invoker responsible for invoking the Message.
|
244
|
+
attr_accessor :invoker
|
245
|
+
def invoker
|
246
|
+
@invoker ||= Invoker.new
|
234
247
|
end
|
235
248
|
|
236
249
|
# !SLIDE END
|
@@ -140,9 +140,8 @@ module ASIR
|
|
140
140
|
|
141
141
|
# !SLIDE
|
142
142
|
# Beanstalk Server
|
143
|
-
|
144
|
-
|
145
|
-
_log { "prepare_beanstalk_server! #{uri}" } if @verbose >= 1
|
143
|
+
def _server!
|
144
|
+
_log { "_server! #{uri}" } if @verbose >= 1
|
146
145
|
@server = connect!(:try_max => nil,
|
147
146
|
:try_sleep => 1,
|
148
147
|
:try_sleep_increment => 0.1,
|
@@ -155,61 +154,28 @@ module ASIR
|
|
155
154
|
end
|
156
155
|
self
|
157
156
|
end
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
with_server_signals! do
|
163
|
-
@running = true
|
164
|
-
while @running
|
165
|
-
prepare_beanstalk_server! unless @server
|
166
|
-
# Same socket for both in and out stream.
|
167
|
-
serve_stream! @server, @server
|
168
|
-
end
|
169
|
-
end
|
170
|
-
self
|
171
|
-
ensure
|
172
|
-
_server_close!
|
173
|
-
end
|
174
|
-
alias :run_server! :run_beanstalk_server!
|
175
|
-
|
176
|
-
def serve_stream! in_stream, out_stream
|
177
|
-
while @running
|
178
|
-
begin
|
179
|
-
serve_stream_message! in_stream, out_stream
|
180
|
-
rescue ::Exception => exc
|
181
|
-
_log [ :serve_stream_error, exc ]
|
182
|
-
@running = false
|
183
|
-
end
|
184
|
-
end
|
185
|
-
self
|
186
|
-
ensure
|
187
|
-
_server_close!
|
157
|
+
|
158
|
+
def _server_accept_connection! server
|
159
|
+
prepare_server! unless @server
|
160
|
+
[ @server, @server ]
|
188
161
|
end
|
189
162
|
|
190
|
-
def
|
191
|
-
|
192
|
-
raise "already running #{@beanstalkd_pid}" if @beanstalkd_pid
|
193
|
-
addr = @address ? "-l #{@address} " : ""
|
194
|
-
cmd = "beanstalkd #{addr}-p #{port}"
|
195
|
-
@beanstalkd_pid = Process.fork do
|
196
|
-
_log { "Start beanstalkd: #{cmd} ..." } if @verbose >= 1
|
197
|
-
exec(cmd)
|
198
|
-
raise "exec #{cmd.inspect} failed"
|
199
|
-
end
|
200
|
-
_log { "Start beanstalkd: #{cmd} pid=#{@beanstalkd_pid.inspect}" } if @verbose >= 2
|
201
|
-
self
|
163
|
+
def _server_close_connection! in_stream, out_stream
|
164
|
+
# NOTHING
|
202
165
|
end
|
203
166
|
|
204
|
-
def
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
ensure
|
210
|
-
@beanstalkd_pid = nil
|
167
|
+
def stream_eof? stream
|
168
|
+
# Note: stream.eof? on a beanstalkd connection,
|
169
|
+
# will cause blocking read *forever* because
|
170
|
+
# beanstalk connections are long lived.
|
171
|
+
false
|
211
172
|
end
|
212
173
|
|
174
|
+
def _start_conduit!
|
175
|
+
addr = address ? "-l #{address} " : ""
|
176
|
+
cmd = "beanstalkd #{addr}-p #{port}"
|
177
|
+
exec(cmd)
|
178
|
+
end
|
213
179
|
end
|
214
180
|
# !SLIDE END
|
215
181
|
end # class
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'asir/transport'
|
2
|
+
|
3
|
+
module Asir
|
4
|
+
class Transport
|
5
|
+
# Conduit service support.
|
6
|
+
module Conduit
|
7
|
+
def start_conduit! options = nil
|
8
|
+
opts = { :fork => true }
|
9
|
+
opts.update(options) if options
|
10
|
+
_log { "start_conduit! #{self}" } if @verbose >= 1
|
11
|
+
in_fork = opts[:fork]
|
12
|
+
raise "already running #{@conduit_pid} #{@conduit_cmd}" if @conduit_pid
|
13
|
+
if in_fork
|
14
|
+
@conduit_pid = ::Process.fork do
|
15
|
+
_log { "start_conduit! #{self} starting pid=#{$$.inspect}" } if @verbose >= 2
|
16
|
+
_start_conduit!
|
17
|
+
raise "Could not exec"
|
18
|
+
end
|
19
|
+
_log { "start_conduit! #{self} started pid=#{@conduit_pid.inspect}" } if @verbose >= 2
|
20
|
+
else
|
21
|
+
_start_conduit!
|
22
|
+
end
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def stop_conduit!
|
27
|
+
if @conduit_pid
|
28
|
+
::Process.kill 'TERM', @conduit_pid
|
29
|
+
::Process.waitpid @conduit_pid
|
30
|
+
# File.unlink @redis_conf
|
31
|
+
end
|
32
|
+
self
|
33
|
+
ensure
|
34
|
+
@conduit_pid = nil
|
35
|
+
end
|
36
|
+
|
37
|
+
def _start_conduit!
|
38
|
+
raise Error::SubclassResponsibility
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -1,51 +1,13 @@
|
|
1
1
|
require 'asir/transport/stream'
|
2
2
|
require 'asir/transport/payload_io'
|
3
|
-
require '
|
3
|
+
require 'asir/uri_config'
|
4
4
|
|
5
5
|
module ASIR
|
6
6
|
class Transport
|
7
7
|
# !SLIDE
|
8
8
|
# Connection-Oriented Transport
|
9
9
|
class ConnectionOriented < Stream
|
10
|
-
include PayloadIO
|
11
|
-
|
12
|
-
attr_accessor :uri, :scheme, :port, :address
|
13
|
-
alias :protocol :scheme
|
14
|
-
alias :protocol= :scheme=
|
15
|
-
|
16
|
-
def uri
|
17
|
-
@uri ||= "#{scheme}://#{address}:#{port}"
|
18
|
-
end
|
19
|
-
|
20
|
-
def scheme
|
21
|
-
@scheme ||=
|
22
|
-
case
|
23
|
-
when @uri
|
24
|
-
URI.parse(@uri).scheme
|
25
|
-
else
|
26
|
-
'tcp'.freeze
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def address
|
31
|
-
@address ||=
|
32
|
-
case
|
33
|
-
when @uri
|
34
|
-
URI.parse(@uri).host
|
35
|
-
else
|
36
|
-
'127.0.0.1'.freeze
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def port
|
41
|
-
@port ||=
|
42
|
-
case
|
43
|
-
when @uri
|
44
|
-
URI.parse(@uri).port
|
45
|
-
else
|
46
|
-
raise Error, "#{self.class}: port not set."
|
47
|
-
end
|
48
|
-
end
|
10
|
+
include PayloadIO, UriConfig
|
49
11
|
|
50
12
|
# !SLIDE
|
51
13
|
# Returns a connected Channel.
|
@@ -70,12 +32,12 @@ module ASIR
|
|
70
32
|
# Returns raw client stream.
|
71
33
|
def _connect!
|
72
34
|
_log { "_connect! #{uri}" } if @verbose >= 1
|
73
|
-
|
74
|
-
_log { "_connect!
|
75
|
-
_after_connect!
|
76
|
-
|
35
|
+
stream = _client_connect!
|
36
|
+
_log { "_connect! stream=#{stream}" } if @verbose >= 1
|
37
|
+
_after_connect! stream
|
38
|
+
stream
|
77
39
|
rescue ::Exception => err
|
78
|
-
raise
|
40
|
+
raise err.class, "Cannot connect to #{self.class} #{uri}: #{err.inspect}", err.backtrace
|
79
41
|
end
|
80
42
|
|
81
43
|
# Subclasses can override.
|
@@ -128,30 +90,43 @@ module ASIR
|
|
128
90
|
_server!
|
129
91
|
rescue ::Exception => err
|
130
92
|
_log [ "prepare_server! #{uri}", :exception, err ]
|
131
|
-
raise
|
93
|
+
raise err.class, "Cannot prepare server on #{self.class} #{uri}: #{err.inspect}", err.backtrace
|
132
94
|
end
|
133
95
|
|
134
96
|
def run_server!
|
135
97
|
_log { "run_server! #{uri}" } if @verbose >= 1
|
136
98
|
with_server_signals! do
|
137
99
|
@running = true
|
100
|
+
server_on_start!
|
138
101
|
while @running
|
139
|
-
|
140
|
-
_log { "run_server!: connected" } if @verbose >= 1
|
141
|
-
begin
|
142
|
-
# Same socket for both in and out stream.
|
143
|
-
serve_stream! stream, stream
|
144
|
-
ensure
|
145
|
-
_server_close_connection!(stream)
|
146
|
-
end
|
147
|
-
_log { "run_server!: disconnected" } if @verbose >= 1
|
102
|
+
serve_connection!
|
148
103
|
end
|
149
104
|
end
|
150
105
|
self
|
151
106
|
ensure
|
107
|
+
server_on_stop!
|
152
108
|
_server_close!
|
153
109
|
end
|
154
110
|
|
111
|
+
def server_on_start!
|
112
|
+
end
|
113
|
+
|
114
|
+
def server_on_stop!
|
115
|
+
end
|
116
|
+
|
117
|
+
def serve_connection!
|
118
|
+
in_stream, out_stream = _server_accept_connection! @server
|
119
|
+
_log { "serve_connection!: connected #{in_stream} #{out_stream}" } if @verbose >= 1
|
120
|
+
_server_serve_stream! in_stream, out_stream
|
121
|
+
rescue Error::Terminate => err
|
122
|
+
@running = false
|
123
|
+
_log [ :serve_connection_terminate, err ]
|
124
|
+
ensure
|
125
|
+
_server_close_connection!(in_stream, out_stream)
|
126
|
+
_log { "serve_connection!: disconnected" } if @verbose >= 1
|
127
|
+
end
|
128
|
+
alias :_server_serve_stream! :_serve_stream!
|
129
|
+
|
155
130
|
def _server!
|
156
131
|
raise Error::SubclassResponsibility, "_server!"
|
157
132
|
end
|
@@ -165,12 +140,13 @@ module ASIR
|
|
165
140
|
end
|
166
141
|
|
167
142
|
# Accept a client connection.
|
143
|
+
# Returns [ in_stream, out_stream ].
|
168
144
|
def _server_accept_connection! server
|
169
145
|
raise Error::SubclassResponsibility, "_server_accept_connection!"
|
170
146
|
end
|
171
147
|
|
172
148
|
# Close a client connection.
|
173
|
-
def _server_close_connection!
|
149
|
+
def _server_close_connection! in_stream, out_stream
|
174
150
|
raise Error::SubclassResponsibility, "_server_close_connection!"
|
175
151
|
end
|
176
152
|
end
|
@@ -24,17 +24,17 @@ module ASIR
|
|
24
24
|
opaque_result
|
25
25
|
end
|
26
26
|
|
27
|
-
def needs_message_identifier?
|
27
|
+
def needs_message_identifier? message
|
28
28
|
@needs_message_identifier ||
|
29
|
-
transports.any? { | t | t.needs_message_identifier? }
|
29
|
+
transports.any? { | t | t.needs_message_identifier?(message) }
|
30
30
|
end
|
31
31
|
|
32
|
-
def needs_message_timestamp?
|
32
|
+
def needs_message_timestamp? message
|
33
33
|
@needs_message_timestamp ||
|
34
|
-
transports.any? { | t | t.needs_message_timestamp? }
|
34
|
+
transports.any? { | t | t.needs_message_timestamp?(message) }
|
35
35
|
end
|
36
36
|
|
37
|
-
# Subclasses with multiple
|
37
|
+
# Subclasses with multiple transports should override this method.
|
38
38
|
def transports
|
39
39
|
@transports ||= [ transport ]
|
40
40
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'asir/transport/delegation'
|
2
|
+
|
3
|
+
module ASIR
|
4
|
+
class Transport
|
5
|
+
# !SLIDE
|
6
|
+
# Select a Transport based on a Proc.
|
7
|
+
class Demux < self
|
8
|
+
include Delegation
|
9
|
+
|
10
|
+
# Proc returning actual transport to use.
|
11
|
+
# transport_proc.call(transport, message)
|
12
|
+
attr_accessor :transport_proc
|
13
|
+
|
14
|
+
# Only active during #send_message.
|
15
|
+
attr_accessor_thread :transport
|
16
|
+
|
17
|
+
# Support for Delegation mixin.
|
18
|
+
def transports; [ transport ]; end
|
19
|
+
|
20
|
+
def send_message message
|
21
|
+
with_attr! :transport, transport_proc.call(self, message) do
|
22
|
+
super
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def _send_message message, message_payload
|
27
|
+
transport.send_message(message)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
# !SLIDE END
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|