omq 0.5.1 → 0.6.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +184 -0
- data/README.md +21 -19
- data/exe/omq +6 -0
- data/lib/omq/cli/base_runner.rb +423 -0
- data/lib/omq/cli/channel.rb +8 -0
- data/lib/omq/cli/client_server.rb +106 -0
- data/lib/omq/cli/config.rb +51 -0
- data/lib/omq/cli/formatter.rb +75 -0
- data/lib/omq/cli/pair.rb +31 -0
- data/lib/omq/cli/peer.rb +8 -0
- data/lib/omq/cli/pipe.rb +249 -0
- data/lib/omq/cli/pub_sub.rb +14 -0
- data/lib/omq/cli/push_pull.rb +14 -0
- data/lib/omq/cli/radio_dish.rb +27 -0
- data/lib/omq/cli/req_rep.rb +77 -0
- data/lib/omq/cli/router_dealer.rb +70 -0
- data/lib/omq/cli/scatter_gather.rb +14 -0
- data/lib/omq/cli.rb +468 -0
- data/lib/omq/pub_sub.rb +2 -2
- data/lib/omq/radio_dish.rb +2 -2
- data/lib/omq/socket.rb +74 -27
- data/lib/omq/version.rb +1 -1
- data/lib/omq/zmtp/connection.rb +24 -3
- data/lib/omq/zmtp/engine.rb +179 -17
- data/lib/omq/zmtp/options.rb +4 -3
- data/lib/omq/zmtp/reactor.rb +10 -5
- data/lib/omq/zmtp/routing/channel.rb +8 -2
- data/lib/omq/zmtp/routing/fan_out.rb +38 -8
- data/lib/omq/zmtp/routing/pair.rb +8 -2
- data/lib/omq/zmtp/routing/peer.rb +7 -1
- data/lib/omq/zmtp/routing/push.rb +14 -7
- data/lib/omq/zmtp/routing/radio.rb +32 -11
- data/lib/omq/zmtp/routing/rep.rb +11 -7
- data/lib/omq/zmtp/routing/req.rb +1 -2
- data/lib/omq/zmtp/routing/round_robin.rb +35 -1
- data/lib/omq/zmtp/routing/router.rb +7 -1
- data/lib/omq/zmtp/routing/scatter.rb +16 -3
- data/lib/omq/zmtp/routing/server.rb +7 -1
- data/lib/omq/zmtp/routing/xsub.rb +7 -1
- data/lib/omq/zmtp/transport/inproc.rb +40 -5
- data/lib/omq/zmtp/transport/ipc.rb +9 -7
- data/lib/omq/zmtp/transport/tcp.rb +14 -7
- data/lib/omq/zmtp/writable.rb +21 -4
- data/lib/omq.rb +7 -0
- metadata +18 -3
- data/exe/omqcat +0 -532
|
@@ -16,37 +16,44 @@ module OMQ
|
|
|
16
16
|
init_round_robin(engine)
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
+
|
|
19
20
|
# @return [Async::LimitedQueue]
|
|
20
21
|
#
|
|
21
22
|
attr_reader :send_queue
|
|
22
23
|
|
|
24
|
+
|
|
23
25
|
# PUSH is write-only.
|
|
24
26
|
#
|
|
25
27
|
def recv_queue
|
|
26
28
|
raise "PUSH sockets cannot receive"
|
|
27
29
|
end
|
|
28
30
|
|
|
31
|
+
|
|
29
32
|
# @param connection [Connection]
|
|
30
33
|
#
|
|
31
34
|
def connection_added(connection)
|
|
32
35
|
@connections << connection
|
|
33
36
|
signal_connection_available
|
|
34
37
|
start_send_pump unless @send_pump_started
|
|
35
|
-
|
|
38
|
+
start_reaper(connection)
|
|
36
39
|
end
|
|
37
40
|
|
|
41
|
+
|
|
38
42
|
# @param connection [Connection]
|
|
39
43
|
#
|
|
40
44
|
def connection_removed(connection)
|
|
41
45
|
@connections.delete(connection)
|
|
42
46
|
end
|
|
43
47
|
|
|
48
|
+
|
|
44
49
|
# @param parts [Array<String>]
|
|
45
50
|
#
|
|
46
51
|
def enqueue(parts)
|
|
47
52
|
@send_queue.enqueue(parts)
|
|
48
53
|
end
|
|
49
54
|
|
|
55
|
+
|
|
56
|
+
# Stops all background tasks (send pump, reapers).
|
|
50
57
|
#
|
|
51
58
|
def stop
|
|
52
59
|
@tasks.each(&:stop)
|
|
@@ -55,13 +62,13 @@ module OMQ
|
|
|
55
62
|
|
|
56
63
|
private
|
|
57
64
|
|
|
58
|
-
|
|
59
|
-
#
|
|
60
|
-
# a dead peer is only
|
|
61
|
-
# succeed if the kernel send buffer absorbs the data.
|
|
65
|
+
|
|
66
|
+
# Detects peer disconnection on write-only sockets. Without
|
|
67
|
+
# this, a dead peer is only noticed on the next send — which
|
|
68
|
+
# may succeed if the kernel send buffer absorbs the data.
|
|
62
69
|
#
|
|
63
|
-
def
|
|
64
|
-
@tasks << Reactor.spawn_pump do
|
|
70
|
+
def start_reaper(conn)
|
|
71
|
+
@tasks << Reactor.spawn_pump(annotation: "reaper") do
|
|
65
72
|
conn.receive_message # blocks until peer disconnects
|
|
66
73
|
rescue *ZMTP::CONNECTION_LOST
|
|
67
74
|
@engine.connection_lost(conn)
|
|
@@ -21,6 +21,7 @@ module OMQ
|
|
|
21
21
|
@groups = {} # connection => Set of joined groups
|
|
22
22
|
@send_queue = Async::LimitedQueue.new(engine.options.send_hwm)
|
|
23
23
|
@send_pump_started = false
|
|
24
|
+
@conflate = engine.options.conflate
|
|
24
25
|
@tasks = []
|
|
25
26
|
end
|
|
26
27
|
|
|
@@ -67,23 +68,44 @@ module OMQ
|
|
|
67
68
|
|
|
68
69
|
def start_send_pump
|
|
69
70
|
@send_pump_started = true
|
|
70
|
-
@tasks <<
|
|
71
|
+
@tasks << @engine.spawn_pump_task(annotation: "send pump") do
|
|
71
72
|
loop do
|
|
73
|
+
@send_pump_idle = true
|
|
72
74
|
batch = [@send_queue.dequeue]
|
|
75
|
+
@send_pump_idle = false
|
|
73
76
|
Routing.drain_send_queue(@send_queue, batch)
|
|
74
77
|
|
|
75
78
|
written = Set.new
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
|
|
80
|
+
if @conflate
|
|
81
|
+
# Keep only the last matching message per connection.
|
|
82
|
+
latest = {} # conn => [group, body]
|
|
83
|
+
batch.each do |parts|
|
|
84
|
+
group = parts[0]
|
|
85
|
+
body = parts[1] || "".b
|
|
86
|
+
@connections.each do |conn|
|
|
87
|
+
next unless @groups[conn]&.include?(group)
|
|
88
|
+
latest[conn] = [group, body]
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
latest.each do |conn, msg|
|
|
81
92
|
begin
|
|
82
|
-
|
|
83
|
-
conn.write_message([group, body])
|
|
93
|
+
conn.write_message(msg)
|
|
84
94
|
written << conn
|
|
85
95
|
rescue *ZMTP::CONNECTION_LOST
|
|
86
|
-
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
else
|
|
99
|
+
batch.each do |parts|
|
|
100
|
+
group = parts[0]
|
|
101
|
+
body = parts[1] || "".b
|
|
102
|
+
@connections.each do |conn|
|
|
103
|
+
next unless @groups[conn]&.include?(group)
|
|
104
|
+
begin
|
|
105
|
+
conn.write_message([group, body])
|
|
106
|
+
written << conn
|
|
107
|
+
rescue *ZMTP::CONNECTION_LOST
|
|
108
|
+
end
|
|
87
109
|
end
|
|
88
110
|
end
|
|
89
111
|
end
|
|
@@ -91,14 +113,13 @@ module OMQ
|
|
|
91
113
|
written.each do |conn|
|
|
92
114
|
conn.flush
|
|
93
115
|
rescue *ZMTP::CONNECTION_LOST
|
|
94
|
-
# connection dead — will be cleaned up
|
|
95
116
|
end
|
|
96
117
|
end
|
|
97
118
|
end
|
|
98
119
|
end
|
|
99
120
|
|
|
100
121
|
def start_group_listener(conn)
|
|
101
|
-
@tasks << Reactor.spawn_pump do
|
|
122
|
+
@tasks << Reactor.spawn_pump(annotation: "recv pump") do
|
|
102
123
|
loop do
|
|
103
124
|
frame = conn.read_frame
|
|
104
125
|
next unless frame.command?
|
data/lib/omq/zmtp/routing/rep.rb
CHANGED
|
@@ -19,6 +19,7 @@ module OMQ
|
|
|
19
19
|
@pending_replies = []
|
|
20
20
|
@tasks = []
|
|
21
21
|
@send_pump_started = false
|
|
22
|
+
@send_pump_idle = true
|
|
22
23
|
end
|
|
23
24
|
|
|
24
25
|
# @return [Async::LimitedQueue]
|
|
@@ -29,13 +30,11 @@ module OMQ
|
|
|
29
30
|
#
|
|
30
31
|
def connection_added(connection)
|
|
31
32
|
transform = ->(msg) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
end
|
|
36
|
-
msg.shift # remove empty delimiter
|
|
33
|
+
delimiter = msg.index(&:empty?) || msg.size
|
|
34
|
+
envelope = msg[0, delimiter]
|
|
35
|
+
body = msg[(delimiter + 1)..] || []
|
|
37
36
|
@pending_replies << { conn: connection, envelope: envelope }
|
|
38
|
-
|
|
37
|
+
body
|
|
39
38
|
}
|
|
40
39
|
task = @engine.start_recv_pump(connection, @recv_queue, transform: transform)
|
|
41
40
|
@tasks << task if task
|
|
@@ -64,11 +63,16 @@ module OMQ
|
|
|
64
63
|
|
|
65
64
|
private
|
|
66
65
|
|
|
66
|
+
def send_pump_idle? = @send_pump_idle
|
|
67
|
+
|
|
68
|
+
|
|
67
69
|
def start_send_pump
|
|
68
70
|
@send_pump_started = true
|
|
69
|
-
@tasks <<
|
|
71
|
+
@tasks << @engine.spawn_pump_task(annotation: "send pump") do
|
|
70
72
|
loop do
|
|
73
|
+
@send_pump_idle = true
|
|
71
74
|
batch = [@send_queue.dequeue]
|
|
75
|
+
@send_pump_idle = false
|
|
72
76
|
Routing.drain_send_queue(@send_queue, batch)
|
|
73
77
|
|
|
74
78
|
written = Set.new
|
data/lib/omq/zmtp/routing/req.rb
CHANGED
|
@@ -15,20 +15,31 @@ module OMQ
|
|
|
15
15
|
module RoundRobin
|
|
16
16
|
private
|
|
17
17
|
|
|
18
|
+
|
|
19
|
+
# Initializes round-robin state for the including class.
|
|
20
|
+
#
|
|
21
|
+
# @param engine [Engine]
|
|
22
|
+
#
|
|
18
23
|
def init_round_robin(engine)
|
|
19
24
|
@connections = []
|
|
20
25
|
@cycle = @connections.cycle
|
|
21
26
|
@connection_available = Async::Promise.new
|
|
22
27
|
@send_queue = Async::LimitedQueue.new(engine.options.send_hwm)
|
|
23
28
|
@send_pump_started = false
|
|
29
|
+
@send_pump_idle = true
|
|
24
30
|
end
|
|
25
31
|
|
|
32
|
+
|
|
33
|
+
# Resolves the connection-available promise so blocked
|
|
34
|
+
# senders can proceed.
|
|
35
|
+
#
|
|
26
36
|
def signal_connection_available
|
|
27
37
|
unless @connection_available.resolved?
|
|
28
38
|
@connection_available.resolve(true)
|
|
29
39
|
end
|
|
30
40
|
end
|
|
31
41
|
|
|
42
|
+
|
|
32
43
|
# Blocks until a connection is available, then returns
|
|
33
44
|
# the next one in round-robin order.
|
|
34
45
|
#
|
|
@@ -43,6 +54,7 @@ module OMQ
|
|
|
43
54
|
retry
|
|
44
55
|
end
|
|
45
56
|
|
|
57
|
+
|
|
46
58
|
# Transforms parts before sending. Override in subclasses
|
|
47
59
|
# (e.g. REQ prepends an empty delimiter frame).
|
|
48
60
|
#
|
|
@@ -51,11 +63,21 @@ module OMQ
|
|
|
51
63
|
#
|
|
52
64
|
def transform_send(parts) = parts
|
|
53
65
|
|
|
66
|
+
|
|
67
|
+
# Starts the background send pump that dequeues messages
|
|
68
|
+
# and dispatches them round-robin across connections.
|
|
69
|
+
#
|
|
70
|
+
# @return [Boolean] true when the send pump is idle (not sending a batch)
|
|
71
|
+
def send_pump_idle? = @send_pump_idle
|
|
72
|
+
|
|
73
|
+
|
|
54
74
|
def start_send_pump
|
|
55
75
|
@send_pump_started = true
|
|
56
|
-
@tasks <<
|
|
76
|
+
@tasks << @engine.spawn_pump_task(annotation: "send pump") do
|
|
57
77
|
loop do
|
|
78
|
+
@send_pump_idle = true
|
|
58
79
|
batch = [@send_queue.dequeue]
|
|
80
|
+
@send_pump_idle = false
|
|
59
81
|
Routing.drain_send_queue(@send_queue, batch)
|
|
60
82
|
|
|
61
83
|
if batch.size == 1
|
|
@@ -67,6 +89,12 @@ module OMQ
|
|
|
67
89
|
end
|
|
68
90
|
end
|
|
69
91
|
|
|
92
|
+
|
|
93
|
+
# Sends a single message, retrying on a new connection if
|
|
94
|
+
# the current one is lost.
|
|
95
|
+
#
|
|
96
|
+
# @param parts [Array<String>]
|
|
97
|
+
#
|
|
70
98
|
def send_with_retry(parts)
|
|
71
99
|
conn = next_connection
|
|
72
100
|
conn.send_message(transform_send(parts))
|
|
@@ -75,6 +103,12 @@ module OMQ
|
|
|
75
103
|
retry
|
|
76
104
|
end
|
|
77
105
|
|
|
106
|
+
|
|
107
|
+
# Sends a batch of messages, writing without flushing for
|
|
108
|
+
# throughput. Falls back to #send_with_retry on failure.
|
|
109
|
+
#
|
|
110
|
+
# @param batch [Array<Array<String>>]
|
|
111
|
+
#
|
|
78
112
|
def send_batch(batch)
|
|
79
113
|
written = Set.new
|
|
80
114
|
batch.each_with_index do |parts, i|
|
|
@@ -21,6 +21,7 @@ module OMQ
|
|
|
21
21
|
@connections_by_identity = {}
|
|
22
22
|
@tasks = []
|
|
23
23
|
@send_pump_started = false
|
|
24
|
+
@send_pump_idle = true
|
|
24
25
|
end
|
|
25
26
|
|
|
26
27
|
# @return [Async::LimitedQueue]
|
|
@@ -68,11 +69,16 @@ module OMQ
|
|
|
68
69
|
|
|
69
70
|
private
|
|
70
71
|
|
|
72
|
+
def send_pump_idle? = @send_pump_idle
|
|
73
|
+
|
|
74
|
+
|
|
71
75
|
def start_send_pump
|
|
72
76
|
@send_pump_started = true
|
|
73
|
-
@tasks <<
|
|
77
|
+
@tasks << @engine.spawn_pump_task(annotation: "send pump") do
|
|
74
78
|
loop do
|
|
79
|
+
@send_pump_idle = true
|
|
75
80
|
batch = [@send_queue.dequeue]
|
|
81
|
+
@send_pump_idle = false
|
|
76
82
|
Routing.drain_send_queue(@send_queue, batch)
|
|
77
83
|
|
|
78
84
|
written = Set.new
|
|
@@ -16,37 +16,44 @@ module OMQ
|
|
|
16
16
|
init_round_robin(engine)
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
+
|
|
19
20
|
# @return [Async::LimitedQueue]
|
|
20
21
|
#
|
|
21
22
|
attr_reader :send_queue
|
|
22
23
|
|
|
24
|
+
|
|
23
25
|
# SCATTER is write-only.
|
|
24
26
|
#
|
|
25
27
|
def recv_queue
|
|
26
28
|
raise "SCATTER sockets cannot receive"
|
|
27
29
|
end
|
|
28
30
|
|
|
31
|
+
|
|
29
32
|
# @param connection [Connection]
|
|
30
33
|
#
|
|
31
34
|
def connection_added(connection)
|
|
32
35
|
@connections << connection
|
|
33
36
|
signal_connection_available
|
|
34
37
|
start_send_pump unless @send_pump_started
|
|
35
|
-
|
|
38
|
+
start_reaper(connection)
|
|
36
39
|
end
|
|
37
40
|
|
|
41
|
+
|
|
38
42
|
# @param connection [Connection]
|
|
39
43
|
#
|
|
40
44
|
def connection_removed(connection)
|
|
41
45
|
@connections.delete(connection)
|
|
42
46
|
end
|
|
43
47
|
|
|
48
|
+
|
|
44
49
|
# @param parts [Array<String>]
|
|
45
50
|
#
|
|
46
51
|
def enqueue(parts)
|
|
47
52
|
@send_queue.enqueue(parts)
|
|
48
53
|
end
|
|
49
54
|
|
|
55
|
+
|
|
56
|
+
# Stops all background tasks (send pump, reapers).
|
|
50
57
|
#
|
|
51
58
|
def stop
|
|
52
59
|
@tasks.each(&:stop)
|
|
@@ -55,8 +62,14 @@ module OMQ
|
|
|
55
62
|
|
|
56
63
|
private
|
|
57
64
|
|
|
58
|
-
|
|
59
|
-
|
|
65
|
+
|
|
66
|
+
# Detects peer disconnection on write-only sockets by
|
|
67
|
+
# blocking on a receive that only returns on disconnect.
|
|
68
|
+
#
|
|
69
|
+
# @param conn [Connection]
|
|
70
|
+
#
|
|
71
|
+
def start_reaper(conn)
|
|
72
|
+
@tasks << Reactor.spawn_pump(annotation: "reaper") do
|
|
60
73
|
conn.receive_message
|
|
61
74
|
rescue *ZMTP::CONNECTION_LOST
|
|
62
75
|
@engine.connection_lost(conn)
|
|
@@ -21,6 +21,7 @@ module OMQ
|
|
|
21
21
|
@connections_by_routing_id = {}
|
|
22
22
|
@tasks = []
|
|
23
23
|
@send_pump_started = false
|
|
24
|
+
@send_pump_idle = true
|
|
24
25
|
end
|
|
25
26
|
|
|
26
27
|
# @return [Async::LimitedQueue]
|
|
@@ -59,11 +60,16 @@ module OMQ
|
|
|
59
60
|
|
|
60
61
|
private
|
|
61
62
|
|
|
63
|
+
def send_pump_idle? = @send_pump_idle
|
|
64
|
+
|
|
65
|
+
|
|
62
66
|
def start_send_pump
|
|
63
67
|
@send_pump_started = true
|
|
64
|
-
@tasks <<
|
|
68
|
+
@tasks << @engine.spawn_pump_task(annotation: "send pump") do
|
|
65
69
|
loop do
|
|
70
|
+
@send_pump_idle = true
|
|
66
71
|
batch = [@send_queue.dequeue]
|
|
72
|
+
@send_pump_idle = false
|
|
67
73
|
Routing.drain_send_queue(@send_queue, batch)
|
|
68
74
|
|
|
69
75
|
written = Set.new
|
|
@@ -19,6 +19,7 @@ module OMQ
|
|
|
19
19
|
@send_queue = Async::LimitedQueue.new(engine.options.send_hwm)
|
|
20
20
|
@tasks = []
|
|
21
21
|
@send_pump_started = false
|
|
22
|
+
@send_pump_idle = true
|
|
22
23
|
end
|
|
23
24
|
|
|
24
25
|
# @return [Async::LimitedQueue]
|
|
@@ -54,11 +55,16 @@ module OMQ
|
|
|
54
55
|
|
|
55
56
|
private
|
|
56
57
|
|
|
58
|
+
def send_pump_idle? = @send_pump_idle
|
|
59
|
+
|
|
60
|
+
|
|
57
61
|
def start_send_pump
|
|
58
62
|
@send_pump_started = true
|
|
59
|
-
@tasks <<
|
|
63
|
+
@tasks << @engine.spawn_pump_task(annotation: "send pump") do
|
|
60
64
|
loop do
|
|
65
|
+
@send_pump_idle = true
|
|
61
66
|
parts = @send_queue.dequeue
|
|
67
|
+
@send_pump_idle = false
|
|
62
68
|
frame = parts.first&.b
|
|
63
69
|
next if frame.nil? || frame.empty?
|
|
64
70
|
|
|
@@ -46,6 +46,7 @@ module OMQ
|
|
|
46
46
|
Listener.new(endpoint)
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
+
|
|
49
50
|
# Connects to a bound inproc endpoint.
|
|
50
51
|
#
|
|
51
52
|
# @param endpoint [String] e.g. "inproc://my-endpoint"
|
|
@@ -76,6 +77,7 @@ module OMQ
|
|
|
76
77
|
establish_link(engine, bound_engine, endpoint)
|
|
77
78
|
end
|
|
78
79
|
|
|
80
|
+
|
|
79
81
|
# Removes a bound endpoint from the registry.
|
|
80
82
|
#
|
|
81
83
|
# @param endpoint [String]
|
|
@@ -85,6 +87,7 @@ module OMQ
|
|
|
85
87
|
@mutex.synchronize { @registry.delete(endpoint) }
|
|
86
88
|
end
|
|
87
89
|
|
|
90
|
+
|
|
88
91
|
# Resets the registry. Used in tests.
|
|
89
92
|
#
|
|
90
93
|
# @return [void]
|
|
@@ -96,8 +99,17 @@ module OMQ
|
|
|
96
99
|
end
|
|
97
100
|
end
|
|
98
101
|
|
|
102
|
+
|
|
99
103
|
private
|
|
100
104
|
|
|
105
|
+
|
|
106
|
+
# Wires up a client-server inproc pipe pair after validating
|
|
107
|
+
# that the two socket types are compatible.
|
|
108
|
+
#
|
|
109
|
+
# @param client_engine [Engine] the connecting engine
|
|
110
|
+
# @param server_engine [Engine] the bound engine
|
|
111
|
+
# @param endpoint [String] the inproc endpoint name
|
|
112
|
+
#
|
|
101
113
|
def establish_link(client_engine, server_engine, endpoint)
|
|
102
114
|
client_type = client_engine.socket_type
|
|
103
115
|
server_type = server_engine.socket_type
|
|
@@ -138,8 +150,15 @@ module OMQ
|
|
|
138
150
|
server_engine.connection_ready(server_pipe, endpoint: endpoint)
|
|
139
151
|
end
|
|
140
152
|
|
|
153
|
+
|
|
154
|
+
# Spawns a background task that periodically retries
|
|
155
|
+
# #establish_link until the endpoint appears in the registry.
|
|
156
|
+
#
|
|
157
|
+
# @param endpoint [String] the inproc endpoint name
|
|
158
|
+
# @param engine [Engine] the connecting engine
|
|
159
|
+
#
|
|
141
160
|
def start_connect_retry(endpoint, engine)
|
|
142
|
-
Reactor.spawn_pump do
|
|
161
|
+
Reactor.spawn_pump(annotation: "reconnect") do
|
|
143
162
|
ri = engine.options.reconnect_interval
|
|
144
163
|
ivl = ri.is_a?(Range) ? ri.begin : ri
|
|
145
164
|
loop do
|
|
@@ -161,12 +180,14 @@ module OMQ
|
|
|
161
180
|
#
|
|
162
181
|
attr_reader :endpoint
|
|
163
182
|
|
|
183
|
+
|
|
164
184
|
# @param endpoint [String]
|
|
165
185
|
#
|
|
166
186
|
def initialize(endpoint)
|
|
167
187
|
@endpoint = endpoint
|
|
168
188
|
end
|
|
169
189
|
|
|
190
|
+
|
|
170
191
|
# Stops the listener by removing it from the registry.
|
|
171
192
|
#
|
|
172
193
|
# @return [void]
|
|
@@ -183,32 +204,38 @@ module OMQ
|
|
|
183
204
|
#
|
|
184
205
|
# When a routing strategy sets {#direct_recv_queue} on a pipe,
|
|
185
206
|
# {#send_message} enqueues directly into the peer's recv queue,
|
|
186
|
-
# bypassing the intermediate
|
|
187
|
-
#
|
|
207
|
+
# bypassing the intermediate pipe queues and the recv pump task.
|
|
208
|
+
# This reduces inproc from 3 queue hops to 2 (send_queue →
|
|
209
|
+
# recv_queue), eliminating the internal pipe queue in between.
|
|
188
210
|
#
|
|
189
211
|
class DirectPipe
|
|
190
212
|
# @return [String] peer's socket type
|
|
191
213
|
#
|
|
192
214
|
attr_reader :peer_socket_type
|
|
193
215
|
|
|
216
|
+
|
|
194
217
|
# @return [String] peer's identity
|
|
195
218
|
#
|
|
196
219
|
attr_reader :peer_identity
|
|
197
220
|
|
|
221
|
+
|
|
198
222
|
# @return [DirectPipe, nil] the other end of this pipe pair
|
|
199
223
|
#
|
|
200
224
|
attr_accessor :peer
|
|
201
225
|
|
|
226
|
+
|
|
202
227
|
# @return [Async::LimitedQueue, nil] when set, {#send_message}
|
|
203
228
|
# enqueues directly here instead of using the internal queue
|
|
204
229
|
#
|
|
205
230
|
attr_reader :direct_recv_queue
|
|
206
231
|
|
|
232
|
+
|
|
207
233
|
# @return [Proc, nil] optional transform applied before
|
|
208
234
|
# enqueuing into {#direct_recv_queue}
|
|
209
235
|
#
|
|
210
236
|
attr_accessor :direct_recv_transform
|
|
211
237
|
|
|
238
|
+
|
|
212
239
|
# @param send_queue [Async::Queue, nil] outgoing command queue
|
|
213
240
|
# (nil for non-PUB/SUB types that don't exchange commands)
|
|
214
241
|
# @param receive_queue [Async::Queue, nil] incoming command queue
|
|
@@ -227,6 +254,7 @@ module OMQ
|
|
|
227
254
|
@pending_direct = nil
|
|
228
255
|
end
|
|
229
256
|
|
|
257
|
+
|
|
230
258
|
# Sets the direct recv queue. Drains any messages that were
|
|
231
259
|
# buffered before the queue was available.
|
|
232
260
|
#
|
|
@@ -240,6 +268,7 @@ module OMQ
|
|
|
240
268
|
end
|
|
241
269
|
end
|
|
242
270
|
|
|
271
|
+
|
|
243
272
|
# Sends a multi-frame message.
|
|
244
273
|
#
|
|
245
274
|
# When {#direct_recv_queue} is set (inproc fast path), the
|
|
@@ -252,24 +281,27 @@ module OMQ
|
|
|
252
281
|
def send_message(parts)
|
|
253
282
|
raise IOError, "closed" if @closed
|
|
254
283
|
if @direct_recv_queue
|
|
255
|
-
msg = @direct_recv_transform ? @direct_recv_transform.call(parts) : parts
|
|
284
|
+
msg = @direct_recv_transform ? @direct_recv_transform.call(parts).freeze : parts
|
|
256
285
|
@direct_recv_queue.enqueue(msg)
|
|
257
286
|
elsif @send_queue
|
|
258
287
|
@send_queue.enqueue(parts)
|
|
259
288
|
else
|
|
260
|
-
msg = @direct_recv_transform ? @direct_recv_transform.call(parts) : parts
|
|
289
|
+
msg = @direct_recv_transform ? @direct_recv_transform.call(parts).freeze : parts
|
|
261
290
|
(@pending_direct ||= []) << msg
|
|
262
291
|
end
|
|
263
292
|
end
|
|
264
293
|
|
|
294
|
+
|
|
265
295
|
alias write_message send_message
|
|
266
296
|
|
|
297
|
+
|
|
267
298
|
# No-op — inproc has no IO buffer to flush.
|
|
268
299
|
#
|
|
269
300
|
# @return [void]
|
|
270
301
|
#
|
|
271
302
|
def flush = nil
|
|
272
303
|
|
|
304
|
+
|
|
273
305
|
# Receives a multi-frame message.
|
|
274
306
|
#
|
|
275
307
|
# @return [Array<String>]
|
|
@@ -281,6 +313,7 @@ module OMQ
|
|
|
281
313
|
msg
|
|
282
314
|
end
|
|
283
315
|
|
|
316
|
+
|
|
284
317
|
# Sends a command via the internal command queue.
|
|
285
318
|
# Only available for PUB/SUB-family pipes.
|
|
286
319
|
#
|
|
@@ -292,6 +325,7 @@ module OMQ
|
|
|
292
325
|
@send_queue.enqueue([:command, command])
|
|
293
326
|
end
|
|
294
327
|
|
|
328
|
+
|
|
295
329
|
# Reads one command frame from the internal command queue.
|
|
296
330
|
# Used by PUB/XPUB subscription listeners.
|
|
297
331
|
#
|
|
@@ -308,6 +342,7 @@ module OMQ
|
|
|
308
342
|
end
|
|
309
343
|
end
|
|
310
344
|
|
|
345
|
+
|
|
311
346
|
# Closes this pipe end.
|
|
312
347
|
#
|
|
313
348
|
# @return [void]
|
|
@@ -28,16 +28,11 @@ module OMQ
|
|
|
28
28
|
|
|
29
29
|
server = UNIXServer.new(sock_path)
|
|
30
30
|
|
|
31
|
-
accept_task = Reactor.spawn_pump do
|
|
31
|
+
accept_task = Reactor.spawn_pump(annotation: "ipc accept #{endpoint}") do
|
|
32
32
|
loop do
|
|
33
33
|
client = server.accept
|
|
34
|
-
|
|
34
|
+
Async::Task.current.defer_stop do
|
|
35
35
|
engine.handle_accepted(IO::Stream::Buffered.wrap(client), endpoint: endpoint)
|
|
36
|
-
rescue ProtocolError, *ZMTP::CONNECTION_LOST
|
|
37
|
-
# peer disconnected during handshake
|
|
38
|
-
rescue
|
|
39
|
-
client&.close rescue nil
|
|
40
|
-
raise
|
|
41
36
|
end
|
|
42
37
|
end
|
|
43
38
|
rescue IOError
|
|
@@ -92,6 +87,12 @@ module OMQ
|
|
|
92
87
|
#
|
|
93
88
|
attr_reader :endpoint
|
|
94
89
|
|
|
90
|
+
|
|
91
|
+
# @param endpoint [String] the IPC endpoint URI
|
|
92
|
+
# @param server [UNIXServer]
|
|
93
|
+
# @param accept_task [#stop] the accept loop handle
|
|
94
|
+
# @param path [String] filesystem or abstract namespace path
|
|
95
|
+
#
|
|
95
96
|
def initialize(endpoint, server, accept_task, path)
|
|
96
97
|
@endpoint = endpoint
|
|
97
98
|
@server = server
|
|
@@ -99,6 +100,7 @@ module OMQ
|
|
|
99
100
|
@path = path
|
|
100
101
|
end
|
|
101
102
|
|
|
103
|
+
|
|
102
104
|
# Stops the listener.
|
|
103
105
|
#
|
|
104
106
|
def stop
|