omq-cli 0.2.0 → 0.3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +158 -0
- data/README.md +14 -9
- data/lib/omq/cli/base_runner.rb +152 -210
- data/lib/omq/cli/cli_parser.rb +470 -0
- data/lib/omq/cli/client_server.rb +32 -59
- data/lib/omq/cli/config.rb +10 -0
- data/lib/omq/cli/expression_evaluator.rb +156 -0
- data/lib/omq/cli/formatter.rb +27 -1
- data/lib/omq/cli/pair.rb +9 -7
- data/lib/omq/cli/parallel_recv_runner.rb +150 -0
- data/lib/omq/cli/pipe.rb +175 -156
- data/lib/omq/cli/pub_sub.rb +5 -1
- data/lib/omq/cli/push_pull.rb +5 -1
- data/lib/omq/cli/radio_dish.rb +5 -1
- data/lib/omq/cli/req_rep.rb +44 -49
- data/lib/omq/cli/router_dealer.rb +13 -46
- data/lib/omq/cli/routing_helper.rb +95 -0
- data/lib/omq/cli/scatter_gather.rb +5 -1
- data/lib/omq/cli/socket_setup.rb +100 -0
- data/lib/omq/cli/transient_monitor.rb +41 -0
- data/lib/omq/cli/version.rb +1 -1
- data/lib/omq/cli.rb +72 -426
- metadata +94 -6
- data/lib/omq/cli/channel.rb +0 -8
- data/lib/omq/cli/peer.rb +0 -8
data/lib/omq/cli/pipe.rb
CHANGED
|
@@ -2,19 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
module OMQ
|
|
4
4
|
module CLI
|
|
5
|
+
# Runner for the virtual "pipe" socket type (PULL -> eval -> PUSH).
|
|
6
|
+
# Supports sequential and parallel (Ractor-based) processing modes.
|
|
5
7
|
class PipeRunner
|
|
8
|
+
# @return [Config] frozen CLI configuration
|
|
6
9
|
attr_reader :config
|
|
7
10
|
|
|
8
11
|
|
|
12
|
+
# @param config [Config] frozen CLI configuration
|
|
9
13
|
def initialize(config)
|
|
10
14
|
@config = config
|
|
11
15
|
@fmt = Formatter.new(config.format, compress: config.compress)
|
|
12
16
|
end
|
|
13
17
|
|
|
14
18
|
|
|
19
|
+
# Runs the pipe in sequential or parallel mode based on config.
|
|
20
|
+
#
|
|
21
|
+
# @param task [Async::Task] the parent async task
|
|
22
|
+
# @return [void]
|
|
15
23
|
def call(task)
|
|
16
24
|
if config.parallel
|
|
17
|
-
run_parallel
|
|
25
|
+
run_parallel(task)
|
|
18
26
|
else
|
|
19
27
|
run_sequential(task)
|
|
20
28
|
end
|
|
@@ -34,41 +42,64 @@ module OMQ
|
|
|
34
42
|
|
|
35
43
|
|
|
36
44
|
def attach_endpoints(sock, endpoints)
|
|
37
|
-
|
|
45
|
+
SocketSetup.attach_endpoints(sock, endpoints)
|
|
38
46
|
end
|
|
39
47
|
|
|
40
48
|
|
|
41
|
-
|
|
42
|
-
in_eps, out_eps = resolve_endpoints
|
|
43
|
-
|
|
44
|
-
@pull = OMQ::PULL.new(linger: config.linger, recv_timeout: config.timeout)
|
|
45
|
-
@push = OMQ::PUSH.new(linger: config.linger, send_timeout: config.timeout)
|
|
46
|
-
@pull.reconnect_interval = config.reconnect_ivl if config.reconnect_ivl
|
|
47
|
-
@push.reconnect_interval = config.reconnect_ivl if config.reconnect_ivl
|
|
48
|
-
@pull.heartbeat_interval = config.heartbeat_ivl if config.heartbeat_ivl
|
|
49
|
-
@push.heartbeat_interval = config.heartbeat_ivl if config.heartbeat_ivl
|
|
49
|
+
# ── Sequential ───────────────────────────────────────────────────
|
|
50
50
|
|
|
51
|
-
attach_endpoints(@pull, in_eps)
|
|
52
|
-
attach_endpoints(@push, out_eps)
|
|
53
51
|
|
|
52
|
+
def run_sequential(task)
|
|
53
|
+
in_eps, out_eps = resolve_endpoints
|
|
54
|
+
@pull, @push = build_pull_push(
|
|
55
|
+
{ linger: config.linger, recv_timeout: config.timeout },
|
|
56
|
+
{ linger: config.linger, send_timeout: config.timeout },
|
|
57
|
+
in_eps, out_eps
|
|
58
|
+
)
|
|
54
59
|
compile_expr
|
|
55
60
|
@sock = @pull # for eval instance_exec
|
|
56
|
-
|
|
57
61
|
with_timeout(config.timeout) do
|
|
58
62
|
@push.peer_connected.wait
|
|
59
63
|
@pull.peer_connected.wait
|
|
60
64
|
end
|
|
65
|
+
setup_sequential_transient(task)
|
|
66
|
+
@sock.instance_exec(&@recv_begin_proc) if @recv_begin_proc
|
|
67
|
+
sequential_message_loop
|
|
68
|
+
@sock.instance_exec(&@recv_end_proc) if @recv_end_proc
|
|
69
|
+
ensure
|
|
70
|
+
@pull&.close
|
|
71
|
+
@push&.close
|
|
72
|
+
end
|
|
61
73
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
74
|
+
|
|
75
|
+
def apply_socket_intervals(sock)
|
|
76
|
+
sock.reconnect_interval = config.reconnect_ivl if config.reconnect_ivl
|
|
77
|
+
sock.heartbeat_interval = config.heartbeat_ivl if config.heartbeat_ivl
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def build_pull_push(pull_opts, push_opts, in_eps, out_eps)
|
|
82
|
+
pull = OMQ::PULL.new(**pull_opts)
|
|
83
|
+
push = OMQ::PUSH.new(**push_opts)
|
|
84
|
+
apply_socket_intervals(pull)
|
|
85
|
+
apply_socket_intervals(push)
|
|
86
|
+
attach_endpoints(pull, in_eps)
|
|
87
|
+
attach_endpoints(push, out_eps)
|
|
88
|
+
[pull, push]
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def setup_sequential_transient(task)
|
|
93
|
+
return unless config.transient
|
|
94
|
+
task.async do
|
|
95
|
+
@pull.all_peers_gone.wait
|
|
96
|
+
@pull.reconnect_enabled = false
|
|
97
|
+
@pull.close_read
|
|
68
98
|
end
|
|
99
|
+
end
|
|
69
100
|
|
|
70
|
-
@sock.instance_exec(&@recv_begin_proc) if @recv_begin_proc
|
|
71
101
|
|
|
102
|
+
def sequential_message_loop
|
|
72
103
|
n = config.count
|
|
73
104
|
i = 0
|
|
74
105
|
loop do
|
|
@@ -76,114 +107,141 @@ module OMQ
|
|
|
76
107
|
break if parts.nil?
|
|
77
108
|
parts = @fmt.decompress(parts)
|
|
78
109
|
parts = eval_recv_expr(parts)
|
|
79
|
-
if parts && !parts.empty?
|
|
80
|
-
@push.send(@fmt.compress(parts))
|
|
81
|
-
end
|
|
110
|
+
@push.send(@fmt.compress(parts)) if parts && !parts.empty?
|
|
82
111
|
i += 1
|
|
83
112
|
break if n && n > 0 && i >= n
|
|
84
113
|
end
|
|
114
|
+
end
|
|
85
115
|
|
|
86
|
-
|
|
116
|
+
|
|
117
|
+
# ── Parallel ─────────────────────────────────────────────────────
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def run_parallel(task)
|
|
121
|
+
in_eps, out_eps = resolve_endpoints
|
|
122
|
+
pairs = build_socket_pairs(config.parallel, in_eps, out_eps)
|
|
123
|
+
wait_for_pairs(pairs)
|
|
124
|
+
setup_parallel_transient(task, pairs)
|
|
125
|
+
workers = spawn_workers(pairs, build_worker_data)
|
|
126
|
+
join_workers(workers)
|
|
87
127
|
ensure
|
|
88
|
-
|
|
89
|
-
|
|
128
|
+
pairs&.each do |pull, push|
|
|
129
|
+
pull&.close
|
|
130
|
+
push&.close
|
|
131
|
+
end
|
|
90
132
|
end
|
|
91
133
|
|
|
92
134
|
|
|
93
|
-
def
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
# Parse BEGIN/END blocks and per-message expression
|
|
101
|
-
begin_proc = end_proc = eval_proc = nil
|
|
102
|
-
if cfg.recv_expr
|
|
103
|
-
extract = ->(src, kw) {
|
|
104
|
-
s = src.index(/#{kw}\s*\{/)
|
|
105
|
-
return [src, nil] unless s
|
|
106
|
-
i = src.index("{", s); d = 1; j = i + 1
|
|
107
|
-
while j < src.length && d > 0
|
|
108
|
-
d += 1 if src[j] == "{"; d -= 1 if src[j] == "}"
|
|
109
|
-
j += 1
|
|
110
|
-
end
|
|
111
|
-
[src[0...s] + src[j..], src[(i + 1)..(j - 2)]]
|
|
112
|
-
}
|
|
113
|
-
expr, begin_body = extract.(cfg.recv_expr, "BEGIN")
|
|
114
|
-
expr, end_body = extract.(expr, "END")
|
|
115
|
-
begin_proc = eval("proc { #{begin_body} }") if begin_body
|
|
116
|
-
end_proc = eval("proc { #{end_body} }") if end_body
|
|
117
|
-
if expr && !expr.strip.empty?
|
|
118
|
-
ractor_expr = expr.gsub(/\$F\b/, "__F")
|
|
119
|
-
eval_proc = eval("proc { |__F| $_ = __F&.first; #{ractor_expr} }")
|
|
120
|
-
end
|
|
121
|
-
end
|
|
135
|
+
def build_socket_pairs(n_workers, in_eps, out_eps)
|
|
136
|
+
pull_opts = { linger: config.linger }
|
|
137
|
+
push_opts = { linger: config.linger }
|
|
138
|
+
pull_opts[:recv_timeout] = config.timeout if config.timeout
|
|
139
|
+
push_opts[:send_timeout] = config.timeout if config.timeout
|
|
140
|
+
n_workers.times.map { build_pull_push(pull_opts, push_opts, in_eps, out_eps) }
|
|
141
|
+
end
|
|
122
142
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
143
|
+
|
|
144
|
+
def wait_for_pairs(pairs)
|
|
145
|
+
with_timeout(config.timeout) do
|
|
146
|
+
pairs.each do |pull, push|
|
|
147
|
+
push.peer_connected.wait
|
|
148
|
+
pull.peer_connected.wait
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def setup_parallel_transient(task, pairs)
|
|
155
|
+
return unless config.transient
|
|
156
|
+
task.async do
|
|
157
|
+
pairs[0][0].all_peers_gone.wait
|
|
158
|
+
pairs.each do |pull, _|
|
|
159
|
+
pull.reconnect_enabled = false
|
|
160
|
+
pull.close_read
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def build_worker_data
|
|
167
|
+
# Pack worker config into a shareable Hash passed via omq.data —
|
|
168
|
+
# Ruby 4.0 forbids Ractor blocks from closing over outer locals.
|
|
169
|
+
::Ractor.make_shareable({
|
|
170
|
+
recv_src: config.recv_expr,
|
|
171
|
+
fmt_format: config.format,
|
|
172
|
+
fmt_compr: config.compress,
|
|
173
|
+
n_count: config.count,
|
|
174
|
+
})
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def spawn_workers(pairs, worker_data)
|
|
179
|
+
pairs.map do |pull, push|
|
|
180
|
+
OMQ::Ractor.new(pull, push, serialize: false, data: worker_data) do |omq|
|
|
181
|
+
pull_p, push_p = omq.sockets
|
|
182
|
+
d = omq.data
|
|
183
|
+
|
|
184
|
+
# Re-compile expression inside Ractor (Procs are not shareable)
|
|
185
|
+
begin_proc, end_proc, eval_proc =
|
|
186
|
+
OMQ::CLI::ExpressionEvaluator.compile_inside_ractor(d[:recv_src])
|
|
187
|
+
|
|
188
|
+
formatter = OMQ::CLI::Formatter.new(d[:fmt_format], compress: d[:fmt_compr])
|
|
189
|
+
# Use a dedicated context object so @ivar expressions in BEGIN/END/eval
|
|
190
|
+
# work inside Ractors (self in a Ractor is shareable; Object.new is not).
|
|
191
|
+
_ctx = Object.new
|
|
192
|
+
_ctx.instance_exec(&begin_proc) if begin_proc
|
|
193
|
+
|
|
194
|
+
n_count = d[:n_count]
|
|
195
|
+
if eval_proc
|
|
196
|
+
if n_count && n_count > 0
|
|
197
|
+
n_count.times do
|
|
198
|
+
parts = pull_p.receive
|
|
199
|
+
break if parts.nil?
|
|
200
|
+
parts = OMQ::CLI::ExpressionEvaluator.normalize_result(
|
|
201
|
+
_ctx.instance_exec(formatter.decompress(parts), &eval_proc)
|
|
202
|
+
)
|
|
203
|
+
next if parts.nil?
|
|
204
|
+
push_p << formatter.compress(parts) unless parts.empty?
|
|
140
205
|
end
|
|
141
206
|
else
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
pull.close_read
|
|
207
|
+
loop do
|
|
208
|
+
parts = pull_p.receive
|
|
209
|
+
break if parts.nil?
|
|
210
|
+
parts = OMQ::CLI::ExpressionEvaluator.normalize_result(
|
|
211
|
+
_ctx.instance_exec(formatter.decompress(parts), &eval_proc)
|
|
212
|
+
)
|
|
213
|
+
next if parts.nil?
|
|
214
|
+
push_p << formatter.compress(parts) unless parts.empty?
|
|
151
215
|
end
|
|
152
216
|
end
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
break if parts.nil?
|
|
160
|
-
parts = formatter.decompress(parts)
|
|
161
|
-
if eval_proc
|
|
162
|
-
result = eval_proc.call(parts)
|
|
163
|
-
parts = case result
|
|
164
|
-
when nil then nil
|
|
165
|
-
when Array then result
|
|
166
|
-
when String then [result]
|
|
167
|
-
else [result.to_s]
|
|
168
|
-
end
|
|
217
|
+
else
|
|
218
|
+
if n_count && n_count > 0
|
|
219
|
+
n_count.times do
|
|
220
|
+
parts = pull_p.receive
|
|
221
|
+
break if parts.nil?
|
|
222
|
+
push_p << formatter.compress(formatter.decompress(parts))
|
|
169
223
|
end
|
|
170
|
-
|
|
171
|
-
|
|
224
|
+
else
|
|
225
|
+
loop do
|
|
226
|
+
parts = pull_p.receive
|
|
227
|
+
break if parts.nil?
|
|
228
|
+
push_p << formatter.compress(formatter.decompress(parts))
|
|
172
229
|
end
|
|
173
|
-
i += 1
|
|
174
|
-
break if cfg.count && cfg.count > 0 && i >= cfg.count
|
|
175
230
|
end
|
|
231
|
+
end
|
|
176
232
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
push&.close
|
|
233
|
+
if end_proc
|
|
234
|
+
out = OMQ::CLI::ExpressionEvaluator.normalize_result(
|
|
235
|
+
_ctx.instance_exec(&end_proc)
|
|
236
|
+
)
|
|
237
|
+
push_p << formatter.compress(out) if out && !out.empty?
|
|
183
238
|
end
|
|
184
239
|
end
|
|
185
240
|
end
|
|
241
|
+
end
|
|
186
242
|
|
|
243
|
+
|
|
244
|
+
def join_workers(workers)
|
|
187
245
|
workers.each do |w|
|
|
188
246
|
w.value
|
|
189
247
|
rescue Ractor::RemoteError => e
|
|
@@ -192,6 +250,9 @@ module OMQ
|
|
|
192
250
|
end
|
|
193
251
|
|
|
194
252
|
|
|
253
|
+
# ── Shared helpers ────────────────────────────────────────────────
|
|
254
|
+
|
|
255
|
+
|
|
195
256
|
def with_timeout(seconds)
|
|
196
257
|
if seconds
|
|
197
258
|
Async::Task.current.with_timeout(seconds) { yield }
|
|
@@ -202,58 +263,16 @@ module OMQ
|
|
|
202
263
|
|
|
203
264
|
|
|
204
265
|
def compile_expr
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
@
|
|
209
|
-
@recv_end_proc = eval("proc { #{end_body} }") if end_body
|
|
210
|
-
@recv_eval_proc = eval("proc { $_ = $F&.first; #{expr} }") if expr && !expr.strip.empty?
|
|
211
|
-
end
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
def extract_blocks(expr)
|
|
215
|
-
begin_body = end_body = nil
|
|
216
|
-
expr, begin_body = extract_block(expr, "BEGIN")
|
|
217
|
-
expr, end_body = extract_block(expr, "END")
|
|
218
|
-
[expr, begin_body, end_body]
|
|
219
|
-
end
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
def extract_block(expr, keyword)
|
|
223
|
-
start = expr.index(/#{keyword}\s*\{/)
|
|
224
|
-
return [expr, nil] unless start
|
|
225
|
-
|
|
226
|
-
i = expr.index("{", start)
|
|
227
|
-
depth = 1
|
|
228
|
-
j = i + 1
|
|
229
|
-
while j < expr.length && depth > 0
|
|
230
|
-
case expr[j]
|
|
231
|
-
when "{" then depth += 1
|
|
232
|
-
when "}" then depth -= 1
|
|
233
|
-
end
|
|
234
|
-
j += 1
|
|
235
|
-
end
|
|
236
|
-
|
|
237
|
-
body = expr[(i + 1)..(j - 2)]
|
|
238
|
-
trimmed = expr[0...start] + expr[j..]
|
|
239
|
-
[trimmed, body]
|
|
266
|
+
@recv_evaluator = ExpressionEvaluator.new(config.recv_expr, format: config.format)
|
|
267
|
+
@recv_begin_proc = @recv_evaluator.begin_proc
|
|
268
|
+
@recv_eval_proc = @recv_evaluator.eval_proc
|
|
269
|
+
@recv_end_proc = @recv_evaluator.end_proc
|
|
240
270
|
end
|
|
241
271
|
|
|
242
272
|
|
|
243
273
|
def eval_recv_expr(parts)
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
result = @sock.instance_exec(&@recv_eval_proc)
|
|
247
|
-
return nil if result.nil? || result.equal?(@sock)
|
|
248
|
-
return [result] if config.format == :marshal
|
|
249
|
-
case result
|
|
250
|
-
when Array then result
|
|
251
|
-
when String then [result]
|
|
252
|
-
else [result.to_str]
|
|
253
|
-
end
|
|
254
|
-
rescue => e
|
|
255
|
-
$stderr.puts "omq: eval error: #{e.message} (#{e.class})"
|
|
256
|
-
exit 3
|
|
274
|
+
result = @recv_evaluator.call(parts, @sock)
|
|
275
|
+
result.equal?(ExpressionEvaluator::SENT) ? nil : result
|
|
257
276
|
end
|
|
258
277
|
|
|
259
278
|
|
data/lib/omq/cli/pub_sub.rb
CHANGED
|
@@ -2,13 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
module OMQ
|
|
4
4
|
module CLI
|
|
5
|
+
# Runner for PUB sockets (publish messages to subscribers).
|
|
5
6
|
class PubRunner < BaseRunner
|
|
6
7
|
def run_loop(task) = run_send_logic
|
|
7
8
|
end
|
|
8
9
|
|
|
9
10
|
|
|
11
|
+
# Runner for SUB sockets (subscribe and receive published messages).
|
|
10
12
|
class SubRunner < BaseRunner
|
|
11
|
-
def run_loop(task)
|
|
13
|
+
def run_loop(task)
|
|
14
|
+
config.parallel ? run_parallel_recv(task) : run_recv_logic
|
|
15
|
+
end
|
|
12
16
|
end
|
|
13
17
|
end
|
|
14
18
|
end
|
data/lib/omq/cli/push_pull.rb
CHANGED
|
@@ -2,13 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
module OMQ
|
|
4
4
|
module CLI
|
|
5
|
+
# Runner for PUSH sockets (send-only pipeline producer).
|
|
5
6
|
class PushRunner < BaseRunner
|
|
6
7
|
def run_loop(task) = run_send_logic
|
|
7
8
|
end
|
|
8
9
|
|
|
9
10
|
|
|
11
|
+
# Runner for PULL sockets (receive-only pipeline consumer).
|
|
10
12
|
class PullRunner < BaseRunner
|
|
11
|
-
def run_loop(task)
|
|
13
|
+
def run_loop(task)
|
|
14
|
+
config.parallel ? run_parallel_recv(task) : run_recv_logic
|
|
15
|
+
end
|
|
12
16
|
end
|
|
13
17
|
end
|
|
14
18
|
end
|
data/lib/omq/cli/radio_dish.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module OMQ
|
|
4
4
|
module CLI
|
|
5
|
+
# Runner for RADIO sockets (draft; group-based publish).
|
|
5
6
|
class RadioRunner < BaseRunner
|
|
6
7
|
def run_loop(task) = run_send_logic
|
|
7
8
|
|
|
@@ -20,8 +21,11 @@ module OMQ
|
|
|
20
21
|
end
|
|
21
22
|
|
|
22
23
|
|
|
24
|
+
# Runner for DISH sockets (draft; group-based subscribe).
|
|
23
25
|
class DishRunner < BaseRunner
|
|
24
|
-
def run_loop(task)
|
|
26
|
+
def run_loop(task)
|
|
27
|
+
config.parallel ? run_parallel_recv(task) : run_recv_logic
|
|
28
|
+
end
|
|
25
29
|
end
|
|
26
30
|
end
|
|
27
31
|
end
|
data/lib/omq/cli/req_rep.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module OMQ
|
|
4
4
|
module CLI
|
|
5
|
+
# Runner for REQ sockets (synchronous request-reply client).
|
|
5
6
|
class ReqRunner < BaseRunner
|
|
6
7
|
private
|
|
7
8
|
|
|
@@ -10,43 +11,31 @@ module OMQ
|
|
|
10
11
|
n = config.count
|
|
11
12
|
i = 0
|
|
12
13
|
sleep(config.delay) if config.delay
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
interval = config.interval
|
|
27
|
-
wait = interval - (Time.now.to_f % interval)
|
|
28
|
-
sleep(wait) if wait > 0
|
|
29
|
-
end
|
|
30
|
-
else
|
|
31
|
-
loop do
|
|
32
|
-
parts = read_next
|
|
33
|
-
break unless parts
|
|
34
|
-
parts = eval_send_expr(parts)
|
|
35
|
-
next unless parts
|
|
36
|
-
send_msg(parts)
|
|
37
|
-
reply = recv_msg
|
|
38
|
-
break if reply.nil?
|
|
39
|
-
reply = eval_recv_expr(reply)
|
|
40
|
-
output(reply)
|
|
41
|
-
i += 1
|
|
42
|
-
break if n && n > 0 && i >= n
|
|
43
|
-
break if config.data || config.file
|
|
44
|
-
end
|
|
14
|
+
loop do
|
|
15
|
+
parts = read_next
|
|
16
|
+
break unless parts
|
|
17
|
+
parts = eval_send_expr(parts)
|
|
18
|
+
next unless parts
|
|
19
|
+
send_msg(parts)
|
|
20
|
+
reply = recv_msg
|
|
21
|
+
break if reply.nil?
|
|
22
|
+
output(eval_recv_expr(reply))
|
|
23
|
+
i += 1
|
|
24
|
+
break if n && n > 0 && i >= n
|
|
25
|
+
break if !config.interval && (config.data || config.file)
|
|
26
|
+
wait_for_interval if config.interval
|
|
45
27
|
end
|
|
46
28
|
end
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def wait_for_interval
|
|
32
|
+
wait = config.interval - (Time.now.to_f % config.interval)
|
|
33
|
+
sleep(wait) if wait > 0
|
|
34
|
+
end
|
|
47
35
|
end
|
|
48
36
|
|
|
49
37
|
|
|
38
|
+
# Runner for REP sockets (synchronous request-reply server).
|
|
50
39
|
class RepRunner < BaseRunner
|
|
51
40
|
private
|
|
52
41
|
|
|
@@ -57,27 +46,33 @@ module OMQ
|
|
|
57
46
|
loop do
|
|
58
47
|
msg = recv_msg
|
|
59
48
|
break if msg.nil?
|
|
60
|
-
|
|
61
|
-
reply = eval_recv_expr(msg)
|
|
62
|
-
unless reply.equal?(SENT)
|
|
63
|
-
output(reply)
|
|
64
|
-
send_msg(reply || [""])
|
|
65
|
-
end
|
|
66
|
-
elsif config.echo
|
|
67
|
-
output(msg)
|
|
68
|
-
send_msg(msg)
|
|
69
|
-
elsif config.data || config.file || !config.stdin_is_tty
|
|
70
|
-
reply = read_next
|
|
71
|
-
break unless reply
|
|
72
|
-
output(msg)
|
|
73
|
-
send_msg(reply)
|
|
74
|
-
else
|
|
75
|
-
abort "REP needs a reply source: --echo, --data, --file, -e, or stdin pipe"
|
|
76
|
-
end
|
|
49
|
+
break unless handle_rep_request(msg)
|
|
77
50
|
i += 1
|
|
78
51
|
break if n && n > 0 && i >= n
|
|
79
52
|
end
|
|
80
53
|
end
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def handle_rep_request(msg)
|
|
57
|
+
if config.recv_expr || @recv_eval_proc
|
|
58
|
+
reply = eval_recv_expr(msg)
|
|
59
|
+
unless reply.equal?(SENT)
|
|
60
|
+
output(reply)
|
|
61
|
+
send_msg(reply || [""])
|
|
62
|
+
end
|
|
63
|
+
elsif config.echo
|
|
64
|
+
output(msg)
|
|
65
|
+
send_msg(msg)
|
|
66
|
+
elsif config.data || config.file || !config.stdin_is_tty
|
|
67
|
+
reply = read_next
|
|
68
|
+
return false unless reply
|
|
69
|
+
output(msg)
|
|
70
|
+
send_msg(reply)
|
|
71
|
+
else
|
|
72
|
+
abort "REP needs a reply source: --echo, --data, --file, -e, or stdin pipe"
|
|
73
|
+
end
|
|
74
|
+
true
|
|
75
|
+
end
|
|
81
76
|
end
|
|
82
77
|
end
|
|
83
78
|
end
|
|
@@ -2,16 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
module OMQ
|
|
4
4
|
module CLI
|
|
5
|
-
|
|
6
|
-
end
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
# Runner for ROUTER sockets (identity-aware async routing).
|
|
9
6
|
class RouterRunner < BaseRunner
|
|
7
|
+
include RoutingHelper
|
|
8
|
+
|
|
10
9
|
private
|
|
11
10
|
|
|
12
11
|
|
|
13
12
|
def run_loop(task)
|
|
14
|
-
receiver = task
|
|
13
|
+
receiver = recv_async(task)
|
|
14
|
+
sender = async_send_loop(task)
|
|
15
|
+
wait_for_loops(receiver, sender)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def recv_async(task)
|
|
20
|
+
task.async do
|
|
15
21
|
n = config.count
|
|
16
22
|
i = 0
|
|
17
23
|
loop do
|
|
@@ -26,50 +32,11 @@ module OMQ
|
|
|
26
32
|
break if n && n > 0 && i >= n
|
|
27
33
|
end
|
|
28
34
|
end
|
|
29
|
-
|
|
30
|
-
sender = task.async do
|
|
31
|
-
n = config.count
|
|
32
|
-
i = 0
|
|
33
|
-
sleep(config.delay) if config.delay
|
|
34
|
-
if config.interval
|
|
35
|
-
Async::Loop.quantized(interval: config.interval) do
|
|
36
|
-
parts = read_next
|
|
37
|
-
break unless parts
|
|
38
|
-
send_targeted_or_eval(parts)
|
|
39
|
-
i += 1
|
|
40
|
-
break if n && n > 0 && i >= n
|
|
41
|
-
end
|
|
42
|
-
elsif config.data || config.file
|
|
43
|
-
parts = read_next
|
|
44
|
-
send_targeted_or_eval(parts) if parts
|
|
45
|
-
else
|
|
46
|
-
loop do
|
|
47
|
-
parts = read_next
|
|
48
|
-
break unless parts
|
|
49
|
-
send_targeted_or_eval(parts)
|
|
50
|
-
i += 1
|
|
51
|
-
break if n && n > 0 && i >= n
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
wait_for_loops(receiver, sender)
|
|
57
35
|
end
|
|
58
36
|
|
|
59
37
|
|
|
60
|
-
def
|
|
61
|
-
|
|
62
|
-
parts = eval_send_expr(parts)
|
|
63
|
-
return unless parts
|
|
64
|
-
identity = resolve_target(parts.shift)
|
|
65
|
-
payload = @fmt.compress(parts)
|
|
66
|
-
@sock.send([identity, "", *payload])
|
|
67
|
-
elsif config.target
|
|
68
|
-
payload = @fmt.compress(parts)
|
|
69
|
-
@sock.send([resolve_target(config.target), "", *payload])
|
|
70
|
-
else
|
|
71
|
-
send_msg(parts)
|
|
72
|
-
end
|
|
38
|
+
def send_to_peer(id, parts)
|
|
39
|
+
@sock.send([id, "", *@fmt.compress(parts)])
|
|
73
40
|
end
|
|
74
41
|
end
|
|
75
42
|
end
|