asir 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +11 -0
- data/Gemfile +16 -0
- data/README.textile +50 -0
- data/Rakefile +83 -0
- data/VERSION +1 -0
- data/asir.gemspec +36 -0
- data/asir.riterate.yml +114 -0
- data/bin/asir +6 -0
- data/doc/Rakefile +8 -0
- data/doc/asir-sequence.pic +84 -0
- data/doc/asir-sequence.svg +1559 -0
- data/doc/sequence.pic +430 -0
- data/example/asir_control.sh +24 -0
- data/example/asir_control_client_http.rb +14 -0
- data/example/asir_control_client_zmq.rb +15 -0
- data/example/config/asir_config.rb +63 -0
- data/example/delayed_service.rb +15 -0
- data/example/ex01.rb +12 -0
- data/example/ex02.rb +12 -0
- data/example/ex03.rb +19 -0
- data/example/ex04.rb +33 -0
- data/example/ex05.rb +16 -0
- data/example/ex06.rb +26 -0
- data/example/ex07.rb +28 -0
- data/example/ex08.rb +30 -0
- data/example/ex09.rb +25 -0
- data/example/ex10.rb +24 -0
- data/example/ex11.rb +48 -0
- data/example/ex12.rb +34 -0
- data/example/ex13.rb +35 -0
- data/example/ex14.rb +30 -0
- data/example/ex15.rb +13 -0
- data/example/ex16.rb +33 -0
- data/example/ex17.rb +41 -0
- data/example/ex18.rb +62 -0
- data/example/ex19.rb +32 -0
- data/example/ex20.rb +28 -0
- data/example/ex21.rb +28 -0
- data/example/ex22.rb +15 -0
- data/example/ex23.rb +20 -0
- data/example/ex24.rb +35 -0
- data/example/example_helper.rb +51 -0
- data/example/sample_service.rb +162 -0
- data/example/unsafe_service.rb +12 -0
- data/hack_night/README.txt +18 -0
- data/hack_night/exercise/prob-1.rb +18 -0
- data/hack_night/exercise/prob-2.rb +21 -0
- data/hack_night/exercise/prob-3.rb +16 -0
- data/hack_night/exercise/prob-4.rb +36 -0
- data/hack_night/exercise/prob-5.rb +36 -0
- data/hack_night/exercise/prob-6.rb +95 -0
- data/hack_night/exercise/prob-7.rb +34 -0
- data/hack_night/solution/math_service.rb +11 -0
- data/hack_night/solution/prob-1.rb +12 -0
- data/hack_night/solution/prob-2.rb +15 -0
- data/hack_night/solution/prob-3.rb +17 -0
- data/hack_night/solution/prob-4.rb +37 -0
- data/hack_night/solution/prob-5.rb +21 -0
- data/hack_night/solution/prob-6.rb +33 -0
- data/hack_night/solution/prob-7.rb +36 -0
- data/lab/phony_proc.rb +31 -0
- data/lib/asir.rb +253 -0
- data/lib/asir/additional_data.rb +25 -0
- data/lib/asir/channel.rb +130 -0
- data/lib/asir/client.rb +111 -0
- data/lib/asir/code_block.rb +57 -0
- data/lib/asir/code_more.rb +50 -0
- data/lib/asir/coder.rb +26 -0
- data/lib/asir/coder/base64.rb +19 -0
- data/lib/asir/coder/chain.rb +30 -0
- data/lib/asir/coder/identity.rb +23 -0
- data/lib/asir/coder/json.rb +30 -0
- data/lib/asir/coder/marshal.rb +17 -0
- data/lib/asir/coder/null.rb +23 -0
- data/lib/asir/coder/proc.rb +22 -0
- data/lib/asir/coder/sign.rb +48 -0
- data/lib/asir/coder/xml.rb +213 -0
- data/lib/asir/coder/yaml.rb +33 -0
- data/lib/asir/coder/zlib.rb +21 -0
- data/lib/asir/configuration.rb +32 -0
- data/lib/asir/error.rb +34 -0
- data/lib/asir/identity.rb +36 -0
- data/lib/asir/initialization.rb +23 -0
- data/lib/asir/log.rb +82 -0
- data/lib/asir/main.rb +396 -0
- data/lib/asir/message.rb +31 -0
- data/lib/asir/message/delay.rb +35 -0
- data/lib/asir/object_resolving.rb +15 -0
- data/lib/asir/result.rb +39 -0
- data/lib/asir/retry_behavior.rb +54 -0
- data/lib/asir/transport.rb +241 -0
- data/lib/asir/transport/beanstalk.rb +217 -0
- data/lib/asir/transport/broadcast.rb +34 -0
- data/lib/asir/transport/buffer.rb +115 -0
- data/lib/asir/transport/composite.rb +19 -0
- data/lib/asir/transport/connection_oriented.rb +180 -0
- data/lib/asir/transport/delay.rb +38 -0
- data/lib/asir/transport/delegation.rb +53 -0
- data/lib/asir/transport/fallback.rb +36 -0
- data/lib/asir/transport/file.rb +88 -0
- data/lib/asir/transport/http.rb +54 -0
- data/lib/asir/transport/local.rb +21 -0
- data/lib/asir/transport/null.rb +14 -0
- data/lib/asir/transport/payload_io.rb +52 -0
- data/lib/asir/transport/rack.rb +73 -0
- data/lib/asir/transport/retry.rb +41 -0
- data/lib/asir/transport/stream.rb +35 -0
- data/lib/asir/transport/subprocess.rb +30 -0
- data/lib/asir/transport/tcp_socket.rb +34 -0
- data/lib/asir/transport/webrick.rb +50 -0
- data/lib/asir/transport/zmq.rb +110 -0
- data/lib/asir/uuid.rb +32 -0
- data/lib/asir/version.rb +3 -0
- data/spec/const_get_speed_spec.rb +33 -0
- data/spec/debug_helper.rb +20 -0
- data/spec/example_spec.rb +88 -0
- data/spec/json_spec.rb +128 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/xml_spec.rb +144 -0
- data/stylesheets/slides.css +105 -0
- metadata +173 -0
@@ -0,0 +1,15 @@
|
|
1
|
+
module ASIR
|
2
|
+
# !SLIDE
|
3
|
+
# Object Resolving
|
4
|
+
#
|
5
|
+
module ObjectResolving
|
6
|
+
class ResolveError < Error; end
|
7
|
+
def resolve_object name
|
8
|
+
name.to_s.split(MODULE_SEP).inject(Object){|m, n| m.const_get(n)}
|
9
|
+
rescue ::Exception => err
|
10
|
+
raise ResolveError, "cannot resolve #{name.inspect}: #{err.inspect}", err.backtrace
|
11
|
+
end
|
12
|
+
end
|
13
|
+
# !SLIDE END
|
14
|
+
end
|
15
|
+
|
data/lib/asir/result.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
module ASIR
|
2
|
+
# !SLIDE
|
3
|
+
# Result
|
4
|
+
#
|
5
|
+
# Encapsulate the result returned to the Client.
|
6
|
+
class Result
|
7
|
+
include AdditionalData, Identity, CodeMore::Result
|
8
|
+
attr_accessor :message, :result, :exception
|
9
|
+
# Optional: Opaque data about the server that processed the Message.
|
10
|
+
attr_accessor :server
|
11
|
+
|
12
|
+
def initialize msg, res = nil, exc = nil
|
13
|
+
@message = msg; @result = res
|
14
|
+
@exception = exc && EncapsulatedException.new(exc)
|
15
|
+
@identifier = @message.identifier
|
16
|
+
end
|
17
|
+
end
|
18
|
+
# !SLIDE END
|
19
|
+
|
20
|
+
# !SLIDE
|
21
|
+
# Encapsulated Exception
|
22
|
+
#
|
23
|
+
# Encapsulates exceptions raised in the Service.
|
24
|
+
class EncapsulatedException
|
25
|
+
include ObjectResolving, AdditionalData
|
26
|
+
attr_accessor :exception_class, :exception_message, :exception_backtrace
|
27
|
+
|
28
|
+
def initialize exc
|
29
|
+
@exception_class = exc.class.name
|
30
|
+
@exception_message = exc.message
|
31
|
+
@exception_backtrace = exc.backtrace
|
32
|
+
end
|
33
|
+
|
34
|
+
def invoke!
|
35
|
+
raise resolve_object(@exception_class), @exception_message, @exception_backtrace
|
36
|
+
end
|
37
|
+
end
|
38
|
+
# !SLIDE END
|
39
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module ASIR
|
2
|
+
# !SLIDE
|
3
|
+
# Generic retry behavior
|
4
|
+
module RetryBehavior
|
5
|
+
# Maximum trys.
|
6
|
+
attr_accessor :try_max
|
7
|
+
# Initial amount of seconds to sleep between each try.
|
8
|
+
attr_accessor :try_sleep
|
9
|
+
# Amount of seconds to increment sleep between each try.
|
10
|
+
attr_accessor :try_sleep_increment
|
11
|
+
# Maxinum amount of seconds to sleep between each try.
|
12
|
+
attr_accessor :try_sleep_max
|
13
|
+
|
14
|
+
# Yields:
|
15
|
+
# :try, n_try
|
16
|
+
# :rescue, exc
|
17
|
+
# :retry, exc
|
18
|
+
# :failed, nil
|
19
|
+
def with_retry
|
20
|
+
n_try = 0
|
21
|
+
sleep_secs = try_sleep
|
22
|
+
result = done = last_exception = nil
|
23
|
+
begin
|
24
|
+
n_try += 1
|
25
|
+
result = yield :try, n_try
|
26
|
+
done = true
|
27
|
+
rescue *Error::Unforwardable.unforwardable => exc
|
28
|
+
raise
|
29
|
+
rescue ::Exception => exc
|
30
|
+
last_exception = exc
|
31
|
+
yield :rescue, exc
|
32
|
+
if ! try_max || try_max > n_try
|
33
|
+
yield :retry, exc
|
34
|
+
if sleep_secs
|
35
|
+
sleep sleep_secs
|
36
|
+
sleep_secs += try_sleep_increment if try_sleep_increment
|
37
|
+
sleep_secs = try_sleep_max if try_sleep_max && sleep_secs > try_sleep_max
|
38
|
+
end
|
39
|
+
retry
|
40
|
+
end
|
41
|
+
end
|
42
|
+
unless done
|
43
|
+
unless yield :failed, last_exception
|
44
|
+
exc = last_exception
|
45
|
+
raise RetryError, "Retry failed: #{exc.inspect}", exc.backtrace
|
46
|
+
end
|
47
|
+
end
|
48
|
+
result
|
49
|
+
end
|
50
|
+
class RetryError < Error; end
|
51
|
+
end # module
|
52
|
+
# !SLIDE END
|
53
|
+
end # module ASIR
|
54
|
+
|
@@ -0,0 +1,241 @@
|
|
1
|
+
require 'time'
|
2
|
+
require 'asir/message/delay'
|
3
|
+
|
4
|
+
module ASIR
|
5
|
+
# !SLIDE
|
6
|
+
# Transport
|
7
|
+
#
|
8
|
+
# Client: Send the Message to the Service.
|
9
|
+
# Service: Receive the Message from the Client.
|
10
|
+
# Service: Invoke the Message.
|
11
|
+
# Service: Send the Result to the Client.
|
12
|
+
# Client: Receive the Result from the Service.
|
13
|
+
class Transport
|
14
|
+
include Log, Initialization, AdditionalData, Message::Delay
|
15
|
+
|
16
|
+
attr_accessor :encoder, :decoder, :one_way
|
17
|
+
|
18
|
+
# !SLIDE
|
19
|
+
# Transport#send_message
|
20
|
+
# * Encode Message.
|
21
|
+
# * Send encoded Message.
|
22
|
+
# * Receive decoded Result.
|
23
|
+
def send_message message
|
24
|
+
@message_count ||= 0; @message_count += 1
|
25
|
+
message.create_timestamp! if needs_message_timestamp?
|
26
|
+
message.create_identifier! if needs_message_identifier?
|
27
|
+
@before_send_message.call(self, message) if @before_send_message
|
28
|
+
relative_message_delay! message
|
29
|
+
message_payload = encoder.dup.encode(message)
|
30
|
+
opaque_result = _send_message(message, message_payload)
|
31
|
+
receive_result(message, opaque_result)
|
32
|
+
end
|
33
|
+
|
34
|
+
# !SLIDE
|
35
|
+
# Transport#receive_message
|
36
|
+
# Receive Message payload from stream.
|
37
|
+
def receive_message stream
|
38
|
+
@message_count ||= 0; @message_count += 1
|
39
|
+
additional_data = { }
|
40
|
+
if req_and_state = _receive_message(stream, additional_data)
|
41
|
+
message = req_and_state[0] = encoder.dup.decode(req_and_state.first)
|
42
|
+
message.additional_data!.update(additional_data) if message
|
43
|
+
if @after_receive_message
|
44
|
+
begin
|
45
|
+
@after_receive_message.call(self, message)
|
46
|
+
rescue ::Exception => exc
|
47
|
+
_log { [ :receive_message, :after_receive_message, :exception, exc ] }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
req_and_state
|
52
|
+
end
|
53
|
+
# !SLIDE END
|
54
|
+
|
55
|
+
# !SLIDE
|
56
|
+
# Transport#send_result
|
57
|
+
# Send Result to stream.
|
58
|
+
def send_result result, stream, message_state
|
59
|
+
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
|
+
if @one_way && message.block
|
68
|
+
message.block.call(result)
|
69
|
+
else
|
70
|
+
result.message = nil # avoid sending back entire Message.
|
71
|
+
result_payload = decoder.dup.encode(result)
|
72
|
+
_send_result(message, result, result_payload, stream, message_state)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
# !SLIDE END
|
76
|
+
|
77
|
+
# !SLIDE
|
78
|
+
# Transport#receive_result
|
79
|
+
# Receieve Result from stream:
|
80
|
+
# * Receive Result payload
|
81
|
+
# * Decode Result.
|
82
|
+
# * Extract Result result or exception.
|
83
|
+
def receive_result message, opaque_result
|
84
|
+
result_payload = _receive_result(message, opaque_result)
|
85
|
+
result = decoder.dup.decode(result_payload)
|
86
|
+
if result && ! message.one_way
|
87
|
+
if exc = result.exception
|
88
|
+
exc.invoke!
|
89
|
+
else
|
90
|
+
if ! @one_way && message.block
|
91
|
+
message.block.call(result)
|
92
|
+
end
|
93
|
+
result.result
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
# !SLIDE END
|
98
|
+
|
99
|
+
def initialize *args
|
100
|
+
@verbose = 0
|
101
|
+
super
|
102
|
+
end
|
103
|
+
|
104
|
+
# Incremented for each message sent or received.
|
105
|
+
attr_accessor :message_count
|
106
|
+
|
107
|
+
# A Proc to call within #receive_message, after #_receive_message.
|
108
|
+
# trans.after_receiver_message(trans, message)
|
109
|
+
attr_accessor :after_receive_message
|
110
|
+
|
111
|
+
# A Proc to call within #send_message, before #_send_message.
|
112
|
+
# trans.before_send_message(trans, message)
|
113
|
+
attr_accessor :before_send_message
|
114
|
+
|
115
|
+
# Proc to call after #_send_result if result.exception.
|
116
|
+
# trans.on_result_exception.call(trans, result)
|
117
|
+
attr_accessor :on_result_exception
|
118
|
+
|
119
|
+
# Proc to call with exception, if exception occurs within #serve_message!, but outside
|
120
|
+
# Message#invoke!.
|
121
|
+
#
|
122
|
+
# trans.on_exception.call(trans, exception, :message, Message_instance)
|
123
|
+
# trans.on_exception.call(trans, exception, :result, Message_instance, Result_instance)
|
124
|
+
attr_accessor :on_exception
|
125
|
+
|
126
|
+
attr_accessor :needs_message_identifier, :needs_message_timestamp
|
127
|
+
alias :needs_message_identifier? :needs_message_identifier
|
128
|
+
alias :needs_message_timestamp? :needs_message_timestamp
|
129
|
+
|
130
|
+
attr_accessor :verbose
|
131
|
+
|
132
|
+
def _subclass_responsibility *args
|
133
|
+
raise Error::SubclassResponsibility "subclass responsibility"
|
134
|
+
end
|
135
|
+
alias :_send_message :_subclass_responsibility
|
136
|
+
alias :_receive_message :_subclass_responsibility
|
137
|
+
alias :_send_result :_subclass_responsibility
|
138
|
+
alias :_receive_result :_subclass_responsibility
|
139
|
+
|
140
|
+
# !SLIDE
|
141
|
+
# Serve a Message.
|
142
|
+
def serve_message! in_stream, out_stream
|
143
|
+
message = message_state = message_ok = result = result_ok = nil
|
144
|
+
exception = original_exception = unforwardable_exception = nil
|
145
|
+
message, message_state = receive_message(in_stream)
|
146
|
+
if message
|
147
|
+
message_ok = true
|
148
|
+
result = invoke_message!(message)
|
149
|
+
result_ok = true
|
150
|
+
self
|
151
|
+
else
|
152
|
+
nil
|
153
|
+
end
|
154
|
+
rescue ::Exception => exc
|
155
|
+
exception = original_exception = exc
|
156
|
+
_log [ :message_error, exc ]
|
157
|
+
@on_exception.call(self, exc, :message, message) if @on_exception
|
158
|
+
ensure
|
159
|
+
begin
|
160
|
+
if message_ok
|
161
|
+
if exception && ! result_ok
|
162
|
+
case exception
|
163
|
+
when *Error::Unforwardable.unforwardable
|
164
|
+
unforwardable_exception = exception = Error::Unforwardable.new(exception)
|
165
|
+
end
|
166
|
+
result = Result.new(message, nil, exception)
|
167
|
+
end
|
168
|
+
if out_stream
|
169
|
+
send_result(result, out_stream, message_state)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
rescue ::Exception => exc
|
173
|
+
_log [ :result_error, exc ]
|
174
|
+
@on_exception.call(self, exc, :result, message, result) if @on_exception
|
175
|
+
end
|
176
|
+
raise original_exception if unforwardable_exception
|
177
|
+
end
|
178
|
+
|
179
|
+
# !SLIDE pause
|
180
|
+
|
181
|
+
# !SLIDE
|
182
|
+
# Transport Server Support
|
183
|
+
|
184
|
+
def stop! force = false
|
185
|
+
@running = false
|
186
|
+
stop_server! if respond_to?(:stop_server!)
|
187
|
+
raise Error::Terminate if force
|
188
|
+
self
|
189
|
+
end
|
190
|
+
|
191
|
+
def with_server_signals!
|
192
|
+
old_trap = { }
|
193
|
+
[ "TERM", "HUP" ].each do | sig |
|
194
|
+
trap = proc do | *args |
|
195
|
+
stop!
|
196
|
+
@signal_exception = ::ASIR::Error::Terminate.new("#{self} by SIG#{sig} #{args.inspect} in #{__FILE__}:#{__LINE__}")
|
197
|
+
end
|
198
|
+
old_trap[sig] = Signal.trap(sig, trap)
|
199
|
+
end
|
200
|
+
yield
|
201
|
+
if exc = @signal_exception
|
202
|
+
@signal_exception = nil
|
203
|
+
raise exc
|
204
|
+
end
|
205
|
+
ensure
|
206
|
+
# $stderr.puts "old_trap = #{old_trap.inspect}"
|
207
|
+
old_trap.each do | sig, trap |
|
208
|
+
Signal.trap(sig, trap) rescue nil
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
# !SLIDE
|
213
|
+
# Transport Support
|
214
|
+
# ...
|
215
|
+
|
216
|
+
def encoder
|
217
|
+
@encoder ||=
|
218
|
+
Coder::Identity.new
|
219
|
+
end
|
220
|
+
|
221
|
+
def decoder
|
222
|
+
@decoder ||=
|
223
|
+
encoder
|
224
|
+
end
|
225
|
+
|
226
|
+
# Invokes the Message object, returns a Result object.
|
227
|
+
def invoke_message! message
|
228
|
+
_processing_message = @processing_message
|
229
|
+
@processing_message = true
|
230
|
+
wait_for_delay! message
|
231
|
+
message.invoke!
|
232
|
+
ensure
|
233
|
+
@processing_message = _processing_message
|
234
|
+
end
|
235
|
+
|
236
|
+
# !SLIDE END
|
237
|
+
# !SLIDE resume
|
238
|
+
|
239
|
+
end
|
240
|
+
# !SLIDE END
|
241
|
+
end
|
@@ -0,0 +1,217 @@
|
|
1
|
+
require 'asir/transport/tcp_socket'
|
2
|
+
|
3
|
+
module ASIR
|
4
|
+
class Transport
|
5
|
+
# !SLIDE
|
6
|
+
# Beanstalk Transport
|
7
|
+
class Beanstalk < TcpSocket
|
8
|
+
LINE_TERMINATOR = "\r\n".freeze
|
9
|
+
|
10
|
+
attr_accessor :tube, :priority, :delay, :ttr
|
11
|
+
|
12
|
+
def initialize *args
|
13
|
+
@port ||= 11300
|
14
|
+
@tube ||= 'asir'
|
15
|
+
@priority ||= 0
|
16
|
+
@delay ||= 0
|
17
|
+
@ttr ||= 600
|
18
|
+
super
|
19
|
+
end
|
20
|
+
|
21
|
+
# !SLIDE
|
22
|
+
# Sends the encoded Message payload String.
|
23
|
+
def _send_message message, message_payload
|
24
|
+
stream.with_stream! do | s |
|
25
|
+
begin
|
26
|
+
match =
|
27
|
+
_beanstalk(s,
|
28
|
+
"put #{message[:beanstalk_priority] || @priority} #{message[:beanstalk_delay] || @delay} #{message[:beanstalk_ttr] || @ttr} #{message_payload.size}\r\n",
|
29
|
+
/\AINSERTED (\d+)\r\n\Z/,
|
30
|
+
message_payload)
|
31
|
+
job_id = message[:beanstalk_job_id] = match[1].to_i
|
32
|
+
_log { "beanstalk_job_id = #{job_id.inspect}" } if @verbose >= 2
|
33
|
+
rescue ::Exception => exc
|
34
|
+
message[:beanstalk_error] = exc
|
35
|
+
close
|
36
|
+
raise exc
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
RESERVE = "reserve\r\n".freeze
|
42
|
+
|
43
|
+
# !SLIDE
|
44
|
+
# Receives the encoded Message payload String.
|
45
|
+
def _receive_message channel, additional_data
|
46
|
+
channel.with_stream! do | stream |
|
47
|
+
begin
|
48
|
+
match =
|
49
|
+
_beanstalk(stream,
|
50
|
+
RESERVE,
|
51
|
+
/\ARESERVED (\d+) (\d+)\r\n\Z/)
|
52
|
+
additional_data[:beanstalk_job_id] = match[1].to_i
|
53
|
+
additional_data[:beanstalk_message_size] =
|
54
|
+
size = match[2].to_i
|
55
|
+
message_payload = stream.read(size)
|
56
|
+
_read_line_and_expect! stream, /\A\r\n\Z/
|
57
|
+
# Pass the original stream used to #_send_result below.
|
58
|
+
[ message_payload, stream ]
|
59
|
+
rescue ::Exception => exc
|
60
|
+
_log { [ :_receive_message, :exception, exc ] }
|
61
|
+
additional_data[:beanstalk_error] = exc
|
62
|
+
channel.close
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# !SLIDE
|
68
|
+
# Sends the encoded Result payload String.
|
69
|
+
def _send_result message, result, result_payload, channel, stream
|
70
|
+
#
|
71
|
+
# There is a possibility here the following could happen:
|
72
|
+
#
|
73
|
+
# _receive_message
|
74
|
+
# channel == #<Channel:1>
|
75
|
+
# channel.stream == #<TCPSocket:1234>
|
76
|
+
# end
|
77
|
+
# ...
|
78
|
+
# ERROR OCCURES:
|
79
|
+
# channel.stream.close
|
80
|
+
# channel.stream = nil
|
81
|
+
# ...
|
82
|
+
# _send_result
|
83
|
+
# channel == #<Channel:1>
|
84
|
+
# channel.stream == #<TCPSocket:5678> # NEW CONNECTION
|
85
|
+
# stream.write "delete #{job_id}"
|
86
|
+
# ...
|
87
|
+
#
|
88
|
+
# Therefore: _receiver_message passes the original message stream to us.
|
89
|
+
# We insure that the same stream is still the active one and use it.
|
90
|
+
channel.with_stream! do | maybe_other_stream |
|
91
|
+
_log [ :_send_result, "stream lost" ] if maybe_other_stream != stream
|
92
|
+
job_id = message[:beanstalk_job_id] or raise "no beanstalk_job_id"
|
93
|
+
_beanstalk(stream,
|
94
|
+
"delete #{job_id}\r\n",
|
95
|
+
/\ADELETED\r\n\Z/)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# !SLIDE
|
100
|
+
# Receives the encoded Result payload String.
|
101
|
+
def _receive_result message, opaque_result
|
102
|
+
nil
|
103
|
+
end
|
104
|
+
|
105
|
+
# !SLIDE
|
106
|
+
# Sets beanstalk_delay if message.delay was specified.
|
107
|
+
def relative_message_delay! message, now = nil
|
108
|
+
if delay = super
|
109
|
+
message[:beanstalk_delay] = delay.to_i
|
110
|
+
end
|
111
|
+
delay
|
112
|
+
end
|
113
|
+
|
114
|
+
# !SLIDE
|
115
|
+
# Beanstalk protocol support
|
116
|
+
|
117
|
+
# Send "something ...\r\n".
|
118
|
+
# Expect /\ASOMETHING (\d+)...\r\n".
|
119
|
+
def _beanstalk stream, message, expect, payload = nil
|
120
|
+
_log { [ :_beanstalk, :message, message ] } if @verbose >= 3
|
121
|
+
stream.write message
|
122
|
+
if payload
|
123
|
+
stream.write payload
|
124
|
+
stream.write LINE_TERMINATOR
|
125
|
+
end
|
126
|
+
stream.flush
|
127
|
+
if match = _read_line_and_expect!(stream, expect)
|
128
|
+
_log { [ :_beanstalk, :result, match[0] ] } if @verbose >= 3
|
129
|
+
end
|
130
|
+
match
|
131
|
+
end
|
132
|
+
|
133
|
+
def _after_connect! stream
|
134
|
+
if @tube
|
135
|
+
_beanstalk(stream,
|
136
|
+
"use #{@tube}\r\n",
|
137
|
+
/\AUSING #{@tube}\r\n\Z/)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# !SLIDE
|
142
|
+
# Beanstalk Server
|
143
|
+
|
144
|
+
def prepare_beanstalk_server!
|
145
|
+
_log { "prepare_beanstalk_server! #{uri}" } if @verbose >= 1
|
146
|
+
@server = connect!(:try_max => nil,
|
147
|
+
:try_sleep => 1,
|
148
|
+
:try_sleep_increment => 0.1,
|
149
|
+
:try_sleep_max => 10) do | stream |
|
150
|
+
if @tube
|
151
|
+
_beanstalk(stream,
|
152
|
+
"watch #{@tube}\r\n",
|
153
|
+
/\AWATCHING (\d+)\r\n\Z/)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
self
|
157
|
+
end
|
158
|
+
alias :prepare_server! :prepare_beanstalk_server!
|
159
|
+
|
160
|
+
def run_beanstalk_server!
|
161
|
+
_log :run_beanstalk_server! if @verbose >= 1
|
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!
|
188
|
+
end
|
189
|
+
|
190
|
+
def start_beanstalkd!
|
191
|
+
_log { "run_beanstalkd! #{uri}" } if @verbose >= 1
|
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
|
202
|
+
end
|
203
|
+
|
204
|
+
def stop_beanstalkd!
|
205
|
+
_log { "stop_beanstalkd! #{uri} pid=#{@beanstalkd_pid.inspect}" } if @verbose >= 1
|
206
|
+
Process.kill 'TERM', @beanstalkd_pid
|
207
|
+
Process.waitpid @beanstalkd_pid
|
208
|
+
self
|
209
|
+
ensure
|
210
|
+
@beanstalkd_pid = nil
|
211
|
+
end
|
212
|
+
|
213
|
+
end
|
214
|
+
# !SLIDE END
|
215
|
+
end # class
|
216
|
+
end # module
|
217
|
+
|