asir 0.2.0
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/.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
|
+
|