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,34 @@
|
|
1
|
+
require 'asir/transport/composite'
|
2
|
+
|
3
|
+
module ASIR
|
4
|
+
class Transport
|
5
|
+
# !SLIDE
|
6
|
+
# Broadcast Transport
|
7
|
+
#
|
8
|
+
# Broadcast to multiple Transports.
|
9
|
+
class Broadcast < self
|
10
|
+
include Composite
|
11
|
+
|
12
|
+
def _send_message message, message_payload
|
13
|
+
result = first_exception = nil
|
14
|
+
transports.each do | transport |
|
15
|
+
begin
|
16
|
+
result = transport.send_message(message)
|
17
|
+
rescue ::Exception => exc
|
18
|
+
first_exception ||= exc
|
19
|
+
_handle_send_message_exception! transport, message, exc
|
20
|
+
raise unless @continue_on_exception
|
21
|
+
end
|
22
|
+
end
|
23
|
+
if first_exception && @reraise_first_exception
|
24
|
+
$! = first_exception
|
25
|
+
raise
|
26
|
+
end
|
27
|
+
result
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
# !SLIDE END
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'asir/transport'
|
2
|
+
require 'thread'
|
3
|
+
|
4
|
+
module ASIR
|
5
|
+
class Transport
|
6
|
+
# !SLIDE
|
7
|
+
# Buffer Transport
|
8
|
+
#
|
9
|
+
# Buffers Messages until #flush!
|
10
|
+
# Assumes One-way Messages.
|
11
|
+
class Buffer < self
|
12
|
+
include Delegation
|
13
|
+
|
14
|
+
# Transport to send_message.
|
15
|
+
attr_accessor :transport
|
16
|
+
|
17
|
+
def initialize *args
|
18
|
+
super
|
19
|
+
@messages = Queue.new
|
20
|
+
@messages_mutex = Mutex.new
|
21
|
+
@paused = 0
|
22
|
+
@paused_mutex = Mutex.new
|
23
|
+
end
|
24
|
+
|
25
|
+
# If paused, queue messages,
|
26
|
+
# Otherwise delegate immediately to #transport.
|
27
|
+
def _send_message message, message_payload
|
28
|
+
return nil if @ignore
|
29
|
+
if paused?
|
30
|
+
@messages_mutex.synchronize do
|
31
|
+
@messages << message
|
32
|
+
end
|
33
|
+
nil
|
34
|
+
else
|
35
|
+
@transport.send_message(message)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns true if currently paused.
|
40
|
+
# Messages are queued until #resume!.
|
41
|
+
def paused?
|
42
|
+
@paused > 0
|
43
|
+
end
|
44
|
+
|
45
|
+
# Pauses all messages until resume!.
|
46
|
+
# May be called multiple times.
|
47
|
+
def pause!
|
48
|
+
@paused_mutex.synchronize do
|
49
|
+
@paused += 1
|
50
|
+
end
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
54
|
+
# Will automatically call #flush! when not #paused?.
|
55
|
+
def resume!
|
56
|
+
should_flush = @paused_mutex.synchronize do
|
57
|
+
@paused -= 1 if @paused > 0
|
58
|
+
@paused == 0
|
59
|
+
end
|
60
|
+
flush! if should_flush
|
61
|
+
self
|
62
|
+
end
|
63
|
+
|
64
|
+
def size
|
65
|
+
@messages_mutex.synchronize do
|
66
|
+
@messages.size
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Will flush pending Messages even if ! #paused?.
|
71
|
+
def flush!
|
72
|
+
clear!.each do | message |
|
73
|
+
@transport.send_message(message)
|
74
|
+
end
|
75
|
+
self
|
76
|
+
end
|
77
|
+
|
78
|
+
# Clear all pending Messages without sending them.
|
79
|
+
# Returns Array of Messages that would have been sent.
|
80
|
+
def clear!
|
81
|
+
messages = [ ]
|
82
|
+
@messages_mutex.synchronize do
|
83
|
+
@messages.size.times do
|
84
|
+
messages << @messages.shift(true)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
messages
|
88
|
+
end
|
89
|
+
|
90
|
+
# Take Message from head of Queue.
|
91
|
+
def shift non_block=false
|
92
|
+
@messages.shift(non_block)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Processes queue.
|
96
|
+
# Usually used in worker Thread.
|
97
|
+
def process! non_block=false
|
98
|
+
@running = true
|
99
|
+
while @running && message = shift(non_block)
|
100
|
+
@transport.send_message(message)
|
101
|
+
end
|
102
|
+
message
|
103
|
+
end
|
104
|
+
|
105
|
+
# Stop processing queue.
|
106
|
+
def stop!
|
107
|
+
@messages_mutex.synchronize do
|
108
|
+
@ignore = true; @running = false
|
109
|
+
end
|
110
|
+
self
|
111
|
+
end
|
112
|
+
end
|
113
|
+
# !SLIDE END
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'asir/transport/delegation'
|
2
|
+
module ASIR
|
3
|
+
class Transport
|
4
|
+
# !SLIDE
|
5
|
+
# A Transport composed of other Transports.
|
6
|
+
#
|
7
|
+
# Classes that mix this in should define #_send_message(message, message_payload).
|
8
|
+
module Composite
|
9
|
+
include Delegation
|
10
|
+
|
11
|
+
# Enumerable of Transport objects.
|
12
|
+
attr_accessor :transports
|
13
|
+
# If true, continue with other Transports when Transport#send_message raises an Exception.
|
14
|
+
attr_accessor :continue_on_exception
|
15
|
+
end
|
16
|
+
# !SLIDE END
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
@@ -0,0 +1,180 @@
|
|
1
|
+
require 'asir/transport/stream'
|
2
|
+
require 'asir/transport/payload_io'
|
3
|
+
require 'uri'
|
4
|
+
|
5
|
+
module ASIR
|
6
|
+
class Transport
|
7
|
+
# !SLIDE
|
8
|
+
# Connection-Oriented Transport
|
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
|
49
|
+
|
50
|
+
# !SLIDE
|
51
|
+
# Returns a connected Channel.
|
52
|
+
def stream
|
53
|
+
@stream ||=
|
54
|
+
connect!
|
55
|
+
end
|
56
|
+
|
57
|
+
# Yields Channel after _connect!.
|
58
|
+
def connect!(opts = nil, &blk)
|
59
|
+
base_opts = {
|
60
|
+
:on_connect => lambda { | channel |
|
61
|
+
connection = _connect!
|
62
|
+
blk.call(connection) if blk
|
63
|
+
connection
|
64
|
+
}
|
65
|
+
}
|
66
|
+
base_opts.update(opts) if opts
|
67
|
+
Channel.new(base_opts)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Returns raw client stream.
|
71
|
+
def _connect!
|
72
|
+
_log { "_connect! #{uri}" } if @verbose >= 1
|
73
|
+
sock = _client_connect!
|
74
|
+
_log { "_connect! socket=#{sock}" } if @verbose >= 1
|
75
|
+
_after_connect! sock
|
76
|
+
sock
|
77
|
+
rescue ::Exception => err
|
78
|
+
raise Error, "Cannot connect to #{self.class} #{uri}: #{err.inspect}", err.backtrace
|
79
|
+
end
|
80
|
+
|
81
|
+
# Subclasses can override.
|
82
|
+
def _after_connect! stream
|
83
|
+
self
|
84
|
+
end
|
85
|
+
|
86
|
+
# Subclasses can override.
|
87
|
+
def _before_close! stream
|
88
|
+
self
|
89
|
+
end
|
90
|
+
|
91
|
+
# !SLIDE
|
92
|
+
# Sends the encoded Message payload String.
|
93
|
+
def _send_message message, message_payload
|
94
|
+
stream.with_stream! do | io |
|
95
|
+
_write message_payload, io
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# !SLIDE
|
100
|
+
# Receives the encoded Message payload String.
|
101
|
+
def _receive_message stream, additional_data
|
102
|
+
[ _read(stream), nil ]
|
103
|
+
end
|
104
|
+
|
105
|
+
# !SLIDE
|
106
|
+
# Sends the encoded Result payload String.
|
107
|
+
def _send_result message, result, result_payload, stream, message_state
|
108
|
+
unless @one_way || message.one_way
|
109
|
+
_write result_payload, stream
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# !SLIDE
|
114
|
+
# Receives the encoded Result payload String.
|
115
|
+
def _receive_result message, opaque_result
|
116
|
+
unless @one_way || message.one_way
|
117
|
+
stream.with_stream! do | io |
|
118
|
+
_read io
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# !SLIDE
|
124
|
+
# Server
|
125
|
+
|
126
|
+
def prepare_server!
|
127
|
+
_log { "prepare_server! #{uri}" } if @verbose >= 1
|
128
|
+
_server!
|
129
|
+
rescue ::Exception => err
|
130
|
+
_log [ "prepare_server! #{uri}", :exception, err ]
|
131
|
+
raise Error, "Cannot prepare server on #{self.class} #{uri}: #{err.inspect}", err.backtrace
|
132
|
+
end
|
133
|
+
|
134
|
+
def run_server!
|
135
|
+
_log { "run_server! #{uri}" } if @verbose >= 1
|
136
|
+
with_server_signals! do
|
137
|
+
@running = true
|
138
|
+
while @running
|
139
|
+
stream = _server_accept_connection! @server
|
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
|
148
|
+
end
|
149
|
+
end
|
150
|
+
self
|
151
|
+
ensure
|
152
|
+
_server_close!
|
153
|
+
end
|
154
|
+
|
155
|
+
def _server!
|
156
|
+
raise Error::SubclassResponsibility, "_server!"
|
157
|
+
end
|
158
|
+
|
159
|
+
def _server_close!
|
160
|
+
if @server
|
161
|
+
@server.close rescue nil
|
162
|
+
end
|
163
|
+
@server = nil
|
164
|
+
self
|
165
|
+
end
|
166
|
+
|
167
|
+
# Accept a client connection.
|
168
|
+
def _server_accept_connection! server
|
169
|
+
raise Error::SubclassResponsibility, "_server_accept_connection!"
|
170
|
+
end
|
171
|
+
|
172
|
+
# Close a client connection.
|
173
|
+
def _server_close_connection! stream
|
174
|
+
raise Error::SubclassResponsibility, "_server_close_connection!"
|
175
|
+
end
|
176
|
+
end
|
177
|
+
# !SLIDE END
|
178
|
+
end # class
|
179
|
+
end # module
|
180
|
+
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module ASIR
|
2
|
+
class Message
|
3
|
+
# !SLIDE
|
4
|
+
# Message Delay Support
|
5
|
+
#
|
6
|
+
module Delay
|
7
|
+
# Returns the number of seconds from now, that the message should be delayed.
|
8
|
+
# If message.delay is Numeric, sets message.delay to the Time to delay til.
|
9
|
+
# If message.delay is Time, returns (now - message.delay).to_f
|
10
|
+
# Returns Float if message.delay was set, or nil.
|
11
|
+
# Returns 0 if delay has already expired.
|
12
|
+
def relative_message_delay! message, now = nil
|
13
|
+
case delay = message.delay
|
14
|
+
when nil
|
15
|
+
when Numeric
|
16
|
+
now ||= Time.now
|
17
|
+
delay = delay.to_f
|
18
|
+
message.delay = (now + delay).utc
|
19
|
+
when Time
|
20
|
+
now ||= Time.now
|
21
|
+
delay = (delay - now).to_f
|
22
|
+
delay = 0 if delay < 0
|
23
|
+
else
|
24
|
+
raise TypeError, "Expected message.delay to be Numeric or Time, given #{delay.class}"
|
25
|
+
end
|
26
|
+
delay
|
27
|
+
end
|
28
|
+
|
29
|
+
def wait_for_delay! message
|
30
|
+
while (delay = relative_message_delay!(message)) && delay > 0
|
31
|
+
sleep delay
|
32
|
+
end
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module ASIR
|
2
|
+
class Transport
|
3
|
+
# !SLIDE
|
4
|
+
# A Transport that delgated to one or more other Transports.
|
5
|
+
#
|
6
|
+
# Classes that include this must define #_send_message(message, message_payload).
|
7
|
+
module Delegation
|
8
|
+
# If true, reraise the first Exception that occurred during Transport#send_message.
|
9
|
+
attr_accessor :reraise_first_exception
|
10
|
+
|
11
|
+
# Proc to call(transport, message, exc) when a delegated #send_message fails.
|
12
|
+
attr_accessor :on_send_message_exception
|
13
|
+
|
14
|
+
# Proc to call(transport, message) when #send_message fails with no recourse.
|
15
|
+
attr_accessor :on_failed_message
|
16
|
+
|
17
|
+
# Return the subTransports#send_message result unmodified from #_send_message.
|
18
|
+
def _receive_result message, opaque_result
|
19
|
+
opaque_result
|
20
|
+
end
|
21
|
+
|
22
|
+
# Return the subTransports#send_message result unmodified from #_send_message.
|
23
|
+
def receive_result message, opaque_result
|
24
|
+
opaque_result
|
25
|
+
end
|
26
|
+
|
27
|
+
def needs_message_identifier?
|
28
|
+
@needs_message_identifier ||
|
29
|
+
transports.any? { | t | t.needs_message_identifier? }
|
30
|
+
end
|
31
|
+
|
32
|
+
def needs_message_timestamp?
|
33
|
+
@needs_message_timestamp ||
|
34
|
+
transports.any? { | t | t.needs_message_timestamp? }
|
35
|
+
end
|
36
|
+
|
37
|
+
# Subclasses with multiple transport should override this method.
|
38
|
+
def transports
|
39
|
+
@transports ||= [ transport ]
|
40
|
+
end
|
41
|
+
|
42
|
+
# Called from within _send_message rescue.
|
43
|
+
def _handle_send_message_exception! transport, message, exc
|
44
|
+
_log { [ :send_message, :transport_failed, exc ] }
|
45
|
+
(message[:transport_exceptions] ||= [ ]) << "#{exc.inspect}"
|
46
|
+
@on_send_message_exception.call(self, message, exc) if @on_send_message_exception
|
47
|
+
self
|
48
|
+
end
|
49
|
+
end
|
50
|
+
# !SLIDE END
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'asir/transport/composite'
|
2
|
+
|
3
|
+
module ASIR
|
4
|
+
class Transport
|
5
|
+
# !SLIDE
|
6
|
+
# Fallback Transport
|
7
|
+
class Fallback < self
|
8
|
+
include Composite
|
9
|
+
|
10
|
+
def _send_message message, message_payload
|
11
|
+
result = sent = first_exception = nil
|
12
|
+
transports.each do | transport |
|
13
|
+
begin
|
14
|
+
result = transport.send_message(message)
|
15
|
+
sent = true
|
16
|
+
break
|
17
|
+
rescue ::Exception => exc
|
18
|
+
first_exception ||= exc
|
19
|
+
_handle_send_message_exception! transport, message, exc
|
20
|
+
end
|
21
|
+
end
|
22
|
+
unless sent
|
23
|
+
if first_exception && @reraise_first_exception
|
24
|
+
$! = first_exception
|
25
|
+
raise
|
26
|
+
end
|
27
|
+
raise FallbackError, "fallback failed"
|
28
|
+
end
|
29
|
+
result
|
30
|
+
end
|
31
|
+
class FallbackError < Error; end
|
32
|
+
end
|
33
|
+
# !SLIDE END
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|