omq 0.22.1 → 0.23.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 +115 -0
- data/README.md +17 -21
- data/lib/omq/channel.rb +35 -0
- data/lib/omq/client_server.rb +72 -0
- data/lib/omq/constants.rb +68 -0
- data/lib/omq/engine/connection_lifecycle.rb +11 -3
- data/lib/omq/engine/heartbeat.rb +2 -3
- data/lib/omq/engine/maintenance.rb +4 -5
- data/lib/omq/engine/reconnect.rb +12 -11
- data/lib/omq/engine/recv_pump.rb +3 -4
- data/lib/omq/engine/socket_lifecycle.rb +26 -9
- data/lib/omq/engine.rb +196 -85
- data/lib/omq/peer.rb +49 -0
- data/lib/omq/pub_sub.rb +2 -2
- data/lib/omq/radio_dish.rb +122 -0
- data/lib/omq/reactor.rb +14 -5
- data/lib/omq/routing/channel.rb +110 -0
- data/lib/omq/routing/client.rb +70 -0
- data/lib/omq/routing/conn_send_pump.rb +4 -7
- data/lib/omq/routing/dealer.rb +1 -13
- data/lib/omq/routing/dish.rb +94 -0
- data/lib/omq/routing/fan_out.rb +5 -9
- data/lib/omq/routing/gather.rb +60 -0
- data/lib/omq/routing/pair.rb +3 -22
- data/lib/omq/routing/peer.rb +95 -0
- data/lib/omq/routing/pub.rb +0 -11
- data/lib/omq/routing/pull.rb +1 -13
- data/lib/omq/routing/push.rb +1 -10
- data/lib/omq/routing/radio.rb +187 -0
- data/lib/omq/routing/rep.rb +3 -17
- data/lib/omq/routing/req.rb +4 -16
- data/lib/omq/routing/round_robin.rb +11 -15
- data/lib/omq/routing/router.rb +3 -17
- data/lib/omq/routing/scatter.rb +77 -0
- data/lib/omq/routing/server.rb +90 -0
- data/lib/omq/routing/sub.rb +1 -13
- data/lib/omq/routing/xpub.rb +0 -11
- data/lib/omq/routing/xsub.rb +6 -23
- data/lib/omq/scatter_gather.rb +56 -0
- data/lib/omq/socket.rb +8 -23
- data/lib/omq/transport/inproc/direct_pipe.rb +17 -15
- data/lib/omq/transport/inproc.rb +11 -3
- data/lib/omq/transport/ipc.rb +41 -13
- data/lib/omq/transport/tcp.rb +59 -23
- data/lib/omq/transport/udp.rb +281 -0
- data/lib/omq/version.rb +1 -1
- data/lib/omq.rb +9 -64
- metadata +16 -2
- data/lib/omq/monitor_event.rb +0 -16
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "securerandom"
|
|
4
|
+
|
|
5
|
+
module OMQ
|
|
6
|
+
module Routing
|
|
7
|
+
# SERVER socket routing: identity-based routing with auto-generated
|
|
8
|
+
# 4-byte routing IDs.
|
|
9
|
+
#
|
|
10
|
+
# Prepends routing ID on receive. Strips routing ID on send and
|
|
11
|
+
# routes to the identified connection.
|
|
12
|
+
#
|
|
13
|
+
class Server
|
|
14
|
+
# @return [Async::LimitedQueue]
|
|
15
|
+
#
|
|
16
|
+
attr_reader :recv_queue
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# @param engine [Engine]
|
|
20
|
+
#
|
|
21
|
+
def initialize(engine)
|
|
22
|
+
@engine = engine
|
|
23
|
+
@recv_queue = Routing.build_queue(engine.options.recv_hwm, :block)
|
|
24
|
+
@connections_by_routing_id = {}
|
|
25
|
+
@routing_id_by_connection = {}
|
|
26
|
+
@conn_queues = {}
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# Dequeues the next received message. Blocks until one is available.
|
|
31
|
+
#
|
|
32
|
+
# @return [Array<String>, nil]
|
|
33
|
+
#
|
|
34
|
+
def dequeue_recv
|
|
35
|
+
@recv_queue.dequeue
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# Wakes a blocked {#dequeue_recv} with a nil sentinel.
|
|
40
|
+
#
|
|
41
|
+
# @return [void]
|
|
42
|
+
#
|
|
43
|
+
def unblock_recv
|
|
44
|
+
@recv_queue.enqueue(nil)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# @param connection [Connection]
|
|
49
|
+
#
|
|
50
|
+
def connection_added(connection)
|
|
51
|
+
routing_id = SecureRandom.bytes(4)
|
|
52
|
+
@connections_by_routing_id[routing_id] = connection
|
|
53
|
+
@routing_id_by_connection[connection] = routing_id
|
|
54
|
+
|
|
55
|
+
@engine.start_recv_pump(connection, @recv_queue) { |msg| [routing_id, *msg] }
|
|
56
|
+
|
|
57
|
+
q = Routing.build_queue(@engine.options.send_hwm, :block)
|
|
58
|
+
@conn_queues[connection] = q
|
|
59
|
+
ConnSendPump.start(@engine, connection, q)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# @param connection [Connection]
|
|
64
|
+
#
|
|
65
|
+
def connection_removed(connection)
|
|
66
|
+
routing_id = @routing_id_by_connection.delete(connection)
|
|
67
|
+
@connections_by_routing_id.delete(routing_id) if routing_id
|
|
68
|
+
@conn_queues.delete(connection)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
# @param parts [Array<String>]
|
|
73
|
+
#
|
|
74
|
+
def enqueue(parts)
|
|
75
|
+
routing_id = parts.first
|
|
76
|
+
conn = @connections_by_routing_id[routing_id]
|
|
77
|
+
return unless conn
|
|
78
|
+
@conn_queues[conn]&.enqueue(parts[1..])
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
# True when all per-connection send queues are empty.
|
|
83
|
+
#
|
|
84
|
+
def send_queues_drained?
|
|
85
|
+
@conn_queues.values.all?(&:empty?)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
data/lib/omq/routing/sub.rb
CHANGED
|
@@ -20,7 +20,6 @@ module OMQ
|
|
|
20
20
|
@connections = Set.new
|
|
21
21
|
@recv_queue = Routing.build_queue(engine.options.recv_hwm, :block)
|
|
22
22
|
@subscriptions = Set.new
|
|
23
|
-
@tasks = []
|
|
24
23
|
end
|
|
25
24
|
|
|
26
25
|
|
|
@@ -51,8 +50,7 @@ module OMQ
|
|
|
51
50
|
send_subscribe(connection, prefix)
|
|
52
51
|
end
|
|
53
52
|
|
|
54
|
-
|
|
55
|
-
@tasks << task if task
|
|
53
|
+
@engine.start_recv_pump(connection, @recv_queue)
|
|
56
54
|
end
|
|
57
55
|
|
|
58
56
|
|
|
@@ -90,16 +88,6 @@ module OMQ
|
|
|
90
88
|
end
|
|
91
89
|
|
|
92
90
|
|
|
93
|
-
# Stops all background tasks.
|
|
94
|
-
#
|
|
95
|
-
# @return [void]
|
|
96
|
-
#
|
|
97
|
-
def stop
|
|
98
|
-
@tasks.each(&:stop)
|
|
99
|
-
@tasks.clear
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
|
|
103
91
|
private
|
|
104
92
|
|
|
105
93
|
|
data/lib/omq/routing/xpub.rb
CHANGED
|
@@ -24,7 +24,6 @@ module OMQ
|
|
|
24
24
|
def initialize(engine)
|
|
25
25
|
@engine = engine
|
|
26
26
|
@recv_queue = Routing.build_queue(engine.options.recv_hwm, :block)
|
|
27
|
-
@tasks = []
|
|
28
27
|
|
|
29
28
|
init_fan_out(engine)
|
|
30
29
|
end
|
|
@@ -68,16 +67,6 @@ module OMQ
|
|
|
68
67
|
end
|
|
69
68
|
|
|
70
69
|
|
|
71
|
-
# Stops all background tasks.
|
|
72
|
-
#
|
|
73
|
-
# @return [void]
|
|
74
|
-
#
|
|
75
|
-
def stop
|
|
76
|
-
@tasks.each(&:stop)
|
|
77
|
-
@tasks.clear
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
|
|
81
70
|
private
|
|
82
71
|
|
|
83
72
|
|
data/lib/omq/routing/xsub.rb
CHANGED
|
@@ -18,12 +18,10 @@ module OMQ
|
|
|
18
18
|
# @param engine [Engine]
|
|
19
19
|
#
|
|
20
20
|
def initialize(engine)
|
|
21
|
-
@engine
|
|
22
|
-
@connections
|
|
23
|
-
@recv_queue
|
|
24
|
-
@conn_queues
|
|
25
|
-
@conn_send_tasks = {} # connection => send pump task
|
|
26
|
-
@tasks = []
|
|
21
|
+
@engine = engine
|
|
22
|
+
@connections = Set.new
|
|
23
|
+
@recv_queue = Routing.build_queue(engine.options.recv_hwm, :block)
|
|
24
|
+
@conn_queues = {}
|
|
27
25
|
end
|
|
28
26
|
|
|
29
27
|
|
|
@@ -50,8 +48,7 @@ module OMQ
|
|
|
50
48
|
def connection_added(connection)
|
|
51
49
|
@connections << connection
|
|
52
50
|
|
|
53
|
-
|
|
54
|
-
@tasks << task if task
|
|
51
|
+
@engine.start_recv_pump(connection, @recv_queue)
|
|
55
52
|
|
|
56
53
|
q = Routing.build_queue(@engine.options.send_hwm, :block)
|
|
57
54
|
@conn_queues[connection] = q
|
|
@@ -64,7 +61,6 @@ module OMQ
|
|
|
64
61
|
def connection_removed(connection)
|
|
65
62
|
@connections.delete(connection)
|
|
66
63
|
@conn_queues.delete(connection)
|
|
67
|
-
@conn_send_tasks.delete(connection)&.stop
|
|
68
64
|
end
|
|
69
65
|
|
|
70
66
|
|
|
@@ -79,16 +75,6 @@ module OMQ
|
|
|
79
75
|
end
|
|
80
76
|
|
|
81
77
|
|
|
82
|
-
# Stops all background tasks.
|
|
83
|
-
#
|
|
84
|
-
# @return [void]
|
|
85
|
-
#
|
|
86
|
-
def stop
|
|
87
|
-
@tasks.each(&:stop)
|
|
88
|
-
@tasks.clear
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
|
|
92
78
|
# @return [Boolean] true when all per-connection send queues are empty
|
|
93
79
|
#
|
|
94
80
|
def send_queues_drained?
|
|
@@ -100,7 +86,7 @@ module OMQ
|
|
|
100
86
|
|
|
101
87
|
|
|
102
88
|
def start_conn_send_pump(conn, q)
|
|
103
|
-
|
|
89
|
+
@engine.spawn_conn_pump_task(conn, annotation: "send pump") do
|
|
104
90
|
loop do
|
|
105
91
|
parts = q.dequeue
|
|
106
92
|
frame = parts.first&.b
|
|
@@ -128,9 +114,6 @@ module OMQ
|
|
|
128
114
|
end
|
|
129
115
|
end
|
|
130
116
|
end
|
|
131
|
-
|
|
132
|
-
@conn_send_tasks[conn] = task
|
|
133
|
-
@tasks << task
|
|
134
117
|
end
|
|
135
118
|
|
|
136
119
|
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# OMQ SCATTER/GATHER socket types (ZeroMQ RFC 49).
|
|
4
|
+
#
|
|
5
|
+
# Not loaded by +require "omq"+; opt in with:
|
|
6
|
+
#
|
|
7
|
+
# require "omq/scatter_gather"
|
|
8
|
+
|
|
9
|
+
require "omq"
|
|
10
|
+
require_relative "routing/scatter"
|
|
11
|
+
require_relative "routing/gather"
|
|
12
|
+
|
|
13
|
+
module OMQ
|
|
14
|
+
# Pipeline sender socket that round-robins to GATHER peers (ZeroMQ RFC 49).
|
|
15
|
+
class SCATTER < Socket
|
|
16
|
+
include Writable
|
|
17
|
+
include SingleFrame
|
|
18
|
+
|
|
19
|
+
# Creates a new SCATTER socket.
|
|
20
|
+
#
|
|
21
|
+
# @param endpoints [String, Array<String>, nil] endpoint(s) to connect to
|
|
22
|
+
# @param linger [Numeric] linger period in seconds (Float::INFINITY = wait forever, 0 = drop)
|
|
23
|
+
# @param send_hwm [Integer, nil] send high-water mark
|
|
24
|
+
# @param send_timeout [Integer, nil] send timeout in seconds
|
|
25
|
+
# @param backend [Object, nil] optional transport backend
|
|
26
|
+
def initialize(endpoints = nil, linger: Float::INFINITY, send_hwm: nil, send_timeout: nil, backend: nil)
|
|
27
|
+
init_engine(:SCATTER, send_hwm: send_hwm, send_timeout: send_timeout, backend: backend)
|
|
28
|
+
@options.linger = linger
|
|
29
|
+
attach_endpoints(endpoints, default: :connect)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# Pipeline receiver socket that fair-queues from SCATTER peers (ZeroMQ RFC 49).
|
|
35
|
+
class GATHER < Socket
|
|
36
|
+
include Readable
|
|
37
|
+
include SingleFrame
|
|
38
|
+
|
|
39
|
+
# Creates a new GATHER socket.
|
|
40
|
+
#
|
|
41
|
+
# @param endpoints [String, Array<String>, nil] endpoint(s) to bind to
|
|
42
|
+
# @param linger [Numeric] linger period in seconds (Float::INFINITY = wait forever, 0 = drop)
|
|
43
|
+
# @param recv_hwm [Integer, nil] receive high-water mark
|
|
44
|
+
# @param recv_timeout [Integer, nil] receive timeout in seconds
|
|
45
|
+
# @param backend [Object, nil] optional transport backend
|
|
46
|
+
def initialize(endpoints = nil, linger: Float::INFINITY, recv_hwm: nil, recv_timeout: nil, backend: nil)
|
|
47
|
+
init_engine(:GATHER, recv_hwm: recv_hwm, recv_timeout: recv_timeout, backend: backend)
|
|
48
|
+
@options.linger = linger
|
|
49
|
+
attach_endpoints(endpoints, default: :bind)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
Routing.register(:SCATTER, Routing::Scatter)
|
|
55
|
+
Routing.register(:GATHER, Routing::Gather)
|
|
56
|
+
end
|
data/lib/omq/socket.rb
CHANGED
|
@@ -35,11 +35,6 @@ module OMQ
|
|
|
35
35
|
attr_reader :options
|
|
36
36
|
|
|
37
37
|
|
|
38
|
-
# @return [Integer, nil] last auto-selected TCP port
|
|
39
|
-
#
|
|
40
|
-
attr_reader :last_tcp_port
|
|
41
|
-
|
|
42
|
-
|
|
43
38
|
# @return [Engine] the socket's engine. Exposed for peer tooling
|
|
44
39
|
# (omq-cli, omq-ffi, omq-ractor) that needs to reach into the
|
|
45
40
|
# socket's internals — not part of the stable user API.
|
|
@@ -109,14 +104,11 @@ module OMQ
|
|
|
109
104
|
# can coordinate teardown with their own Async tree. Only the
|
|
110
105
|
# *first* bind/connect call captures the parent — subsequent
|
|
111
106
|
# calls ignore the kwarg.
|
|
112
|
-
# @return [
|
|
107
|
+
# @return [URI::Generic] resolved endpoint URI (with auto-selected port for "tcp://host:0")
|
|
113
108
|
#
|
|
114
|
-
def bind(endpoint, parent: nil)
|
|
109
|
+
def bind(endpoint, parent: nil, **opts)
|
|
115
110
|
ensure_parent_task(parent: parent)
|
|
116
|
-
Reactor.run
|
|
117
|
-
@engine.bind(endpoint) # TODO: use timeout?
|
|
118
|
-
@last_tcp_port = @engine.last_tcp_port
|
|
119
|
-
end
|
|
111
|
+
Reactor.run { @engine.bind(endpoint, **opts) } # TODO: use timeout?
|
|
120
112
|
end
|
|
121
113
|
|
|
122
114
|
|
|
@@ -124,11 +116,11 @@ module OMQ
|
|
|
124
116
|
#
|
|
125
117
|
# @param endpoint [String]
|
|
126
118
|
# @param parent [#async, nil] see {#bind}.
|
|
127
|
-
# @return [
|
|
119
|
+
# @return [URI::Generic] parsed endpoint URI
|
|
128
120
|
#
|
|
129
|
-
def connect(endpoint, parent: nil)
|
|
121
|
+
def connect(endpoint, parent: nil, **opts)
|
|
130
122
|
ensure_parent_task(parent: parent)
|
|
131
|
-
Reactor.run { @engine.connect(endpoint) } # TODO: use timeout?
|
|
123
|
+
Reactor.run { @engine.connect(endpoint, **opts) } # TODO: use timeout?
|
|
132
124
|
end
|
|
133
125
|
|
|
134
126
|
|
|
@@ -152,13 +144,6 @@ module OMQ
|
|
|
152
144
|
end
|
|
153
145
|
|
|
154
146
|
|
|
155
|
-
# @return [String, nil] last bound endpoint
|
|
156
|
-
#
|
|
157
|
-
def last_endpoint
|
|
158
|
-
@engine.last_endpoint
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
|
|
162
147
|
# @return [Async::Promise] resolves when first peer completes handshake
|
|
163
148
|
def peer_connected
|
|
164
149
|
@engine.peer_connected
|
|
@@ -167,7 +152,7 @@ module OMQ
|
|
|
167
152
|
|
|
168
153
|
# @return [Async::Promise] resolves when first subscriber joins (PUB/XPUB only)
|
|
169
154
|
def subscriber_joined
|
|
170
|
-
@engine.
|
|
155
|
+
@engine.subscriber_joined
|
|
171
156
|
end
|
|
172
157
|
|
|
173
158
|
|
|
@@ -285,7 +270,7 @@ module OMQ
|
|
|
285
270
|
# @return [String]
|
|
286
271
|
#
|
|
287
272
|
def inspect
|
|
288
|
-
format("#<%s
|
|
273
|
+
format("#<%s bound=%p>", self.class, @engine.listeners.keys)
|
|
289
274
|
end
|
|
290
275
|
|
|
291
276
|
|
|
@@ -50,12 +50,6 @@ module OMQ
|
|
|
50
50
|
attr_reader :direct_recv_queue
|
|
51
51
|
|
|
52
52
|
|
|
53
|
-
# @return [Proc, nil] optional transform applied before
|
|
54
|
-
# enqueuing into {#direct_recv_queue}
|
|
55
|
-
#
|
|
56
|
-
attr_accessor :direct_recv_transform
|
|
57
|
-
|
|
58
|
-
|
|
59
53
|
# @param send_queue [Async::Queue, nil] outgoing command queue
|
|
60
54
|
# (nil for non-PUB/SUB types that don't exchange commands)
|
|
61
55
|
# @param receive_queue [Async::Queue, nil] incoming command queue
|
|
@@ -75,18 +69,26 @@ module OMQ
|
|
|
75
69
|
end
|
|
76
70
|
|
|
77
71
|
|
|
78
|
-
#
|
|
79
|
-
#
|
|
72
|
+
# Wires up the direct recv fast-path. Called once by the recv
|
|
73
|
+
# pump when the receiving side of an inproc pipe pair is set up.
|
|
74
|
+
# After this, peer-side {#send_message} calls enqueue straight
|
|
75
|
+
# into +queue+ instead of hopping through the intermediate pipe
|
|
76
|
+
# queue and a recv pump fiber.
|
|
80
77
|
#
|
|
81
|
-
#
|
|
78
|
+
# Drains any messages the peer buffered into +@pending_direct+
|
|
79
|
+
# before the queue was available.
|
|
80
|
+
#
|
|
81
|
+
# @param queue [Async::LimitedQueue]
|
|
82
|
+
# @param transform [Proc, nil] optional per-message transform
|
|
82
83
|
# @return [void]
|
|
83
84
|
#
|
|
84
|
-
def
|
|
85
|
-
@
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
85
|
+
def wire_direct_recv(queue, transform)
|
|
86
|
+
@direct_recv_transform = transform
|
|
87
|
+
@direct_recv_queue = queue
|
|
88
|
+
return unless @pending_direct
|
|
89
|
+
|
|
90
|
+
@pending_direct.each { |msg| queue.enqueue(msg) }
|
|
91
|
+
@pending_direct = nil
|
|
90
92
|
end
|
|
91
93
|
|
|
92
94
|
|
data/lib/omq/transport/inproc.rb
CHANGED
|
@@ -15,6 +15,9 @@ module OMQ
|
|
|
15
15
|
# prevent shared mutable state without copying.
|
|
16
16
|
#
|
|
17
17
|
module Inproc
|
|
18
|
+
Engine.transports["inproc"] = self
|
|
19
|
+
|
|
20
|
+
|
|
18
21
|
# Socket types that exchange commands (SUBSCRIBE/CANCEL) over inproc.
|
|
19
22
|
#
|
|
20
23
|
COMMAND_TYPES = %i[PUB SUB XPUB XSUB RADIO DISH].freeze
|
|
@@ -28,14 +31,19 @@ module OMQ
|
|
|
28
31
|
|
|
29
32
|
|
|
30
33
|
class << self
|
|
31
|
-
#
|
|
34
|
+
# @return [Hash{String => Engine}] bound inproc endpoints
|
|
35
|
+
#
|
|
36
|
+
attr_reader :registry
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# Creates a bound inproc listener.
|
|
32
40
|
#
|
|
33
41
|
# @param endpoint [String] e.g. "inproc://my-endpoint"
|
|
34
42
|
# @param engine [Engine] the owning engine
|
|
35
43
|
# @return [Listener]
|
|
36
44
|
# @raise [ArgumentError] if endpoint is already bound
|
|
37
45
|
#
|
|
38
|
-
def
|
|
46
|
+
def listener(endpoint, engine, **)
|
|
39
47
|
@mutex.synchronize do
|
|
40
48
|
if @registry.key?(endpoint)
|
|
41
49
|
raise ArgumentError, "endpoint already bound: #{endpoint}"
|
|
@@ -57,7 +65,7 @@ module OMQ
|
|
|
57
65
|
# @param engine [Engine] the connecting engine
|
|
58
66
|
# @return [void]
|
|
59
67
|
#
|
|
60
|
-
def connect(endpoint, engine)
|
|
68
|
+
def connect(endpoint, engine, **)
|
|
61
69
|
bound_engine = @mutex.synchronize { @registry[endpoint] }
|
|
62
70
|
bound_engine ||= await_bind(endpoint, engine) or return
|
|
63
71
|
establish_link(engine, bound_engine, endpoint)
|
data/lib/omq/transport/ipc.rb
CHANGED
|
@@ -11,14 +11,17 @@ module OMQ
|
|
|
11
11
|
# (paths starting with @).
|
|
12
12
|
#
|
|
13
13
|
module IPC
|
|
14
|
+
Engine.transports["ipc"] = self
|
|
15
|
+
|
|
16
|
+
|
|
14
17
|
class << self
|
|
15
|
-
#
|
|
18
|
+
# Creates a bound IPC listener.
|
|
16
19
|
#
|
|
17
20
|
# @param endpoint [String] e.g. "ipc:///tmp/my.sock" or "ipc://@abstract"
|
|
18
21
|
# @param engine [Engine]
|
|
19
22
|
# @return [Listener]
|
|
20
23
|
#
|
|
21
|
-
def
|
|
24
|
+
def listener(endpoint, engine, **)
|
|
22
25
|
path = parse_path(endpoint)
|
|
23
26
|
sock_path = to_socket_path(path)
|
|
24
27
|
|
|
@@ -31,18 +34,14 @@ module OMQ
|
|
|
31
34
|
end
|
|
32
35
|
|
|
33
36
|
|
|
34
|
-
#
|
|
37
|
+
# Creates an IPC dialer for an endpoint.
|
|
35
38
|
#
|
|
36
39
|
# @param endpoint [String]
|
|
37
40
|
# @param engine [Engine]
|
|
38
|
-
# @return [
|
|
41
|
+
# @return [Dialer]
|
|
39
42
|
#
|
|
40
|
-
def
|
|
41
|
-
|
|
42
|
-
sock_path = to_socket_path(path)
|
|
43
|
-
sock = UNIXSocket.new(sock_path)
|
|
44
|
-
apply_buffer_sizes(sock, engine.options)
|
|
45
|
-
engine.handle_connected(IO::Stream::Buffered.wrap(sock), endpoint: endpoint)
|
|
43
|
+
def dialer(endpoint, engine, **)
|
|
44
|
+
Dialer.new(endpoint, engine)
|
|
46
45
|
end
|
|
47
46
|
|
|
48
47
|
|
|
@@ -63,9 +62,6 @@ module OMQ
|
|
|
63
62
|
end
|
|
64
63
|
|
|
65
64
|
|
|
66
|
-
private
|
|
67
|
-
|
|
68
|
-
|
|
69
65
|
# Extracts path from "ipc://path".
|
|
70
66
|
#
|
|
71
67
|
def parse_path(endpoint)
|
|
@@ -92,6 +88,38 @@ module OMQ
|
|
|
92
88
|
end
|
|
93
89
|
|
|
94
90
|
|
|
91
|
+
# An IPC dialer — stateful factory for outgoing connections.
|
|
92
|
+
#
|
|
93
|
+
class Dialer
|
|
94
|
+
# @return [String] the endpoint this dialer connects to
|
|
95
|
+
#
|
|
96
|
+
attr_reader :endpoint
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
# @param endpoint [String]
|
|
100
|
+
# @param engine [Engine]
|
|
101
|
+
#
|
|
102
|
+
def initialize(endpoint, engine)
|
|
103
|
+
@endpoint = endpoint
|
|
104
|
+
@engine = engine
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
# Establishes a Unix socket connection to the endpoint.
|
|
109
|
+
#
|
|
110
|
+
# @return [void]
|
|
111
|
+
#
|
|
112
|
+
def connect
|
|
113
|
+
path = IPC.parse_path(@endpoint)
|
|
114
|
+
sock_path = IPC.to_socket_path(path)
|
|
115
|
+
sock = UNIXSocket.new(sock_path)
|
|
116
|
+
IPC.apply_buffer_sizes(sock, @engine.options)
|
|
117
|
+
@engine.handle_connected(IO::Stream::Buffered.wrap(sock), endpoint: @endpoint)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
|
|
95
123
|
# A bound IPC listener.
|
|
96
124
|
#
|
|
97
125
|
class Listener
|
data/lib/omq/transport/tcp.rb
CHANGED
|
@@ -9,14 +9,17 @@ module OMQ
|
|
|
9
9
|
# TCP transport using Ruby sockets with Async.
|
|
10
10
|
#
|
|
11
11
|
module TCP
|
|
12
|
+
Engine.transports["tcp"] = self
|
|
13
|
+
|
|
14
|
+
|
|
12
15
|
class << self
|
|
13
|
-
#
|
|
16
|
+
# Creates a bound TCP listener.
|
|
14
17
|
#
|
|
15
18
|
# @param endpoint [String] e.g. "tcp://127.0.0.1:5555" or "tcp://*:0"
|
|
16
19
|
# @param engine [Engine]
|
|
17
20
|
# @return [Listener]
|
|
18
21
|
#
|
|
19
|
-
def
|
|
22
|
+
def listener(endpoint, engine, **)
|
|
20
23
|
host, port = self.parse_endpoint(endpoint)
|
|
21
24
|
lookup_host = normalize_bind_host(host)
|
|
22
25
|
|
|
@@ -34,6 +37,17 @@ module OMQ
|
|
|
34
37
|
end
|
|
35
38
|
|
|
36
39
|
|
|
40
|
+
# Creates a TCP dialer for an endpoint.
|
|
41
|
+
#
|
|
42
|
+
# @param endpoint [String] e.g. "tcp://127.0.0.1:5555"
|
|
43
|
+
# @param engine [Engine]
|
|
44
|
+
# @return [Dialer]
|
|
45
|
+
#
|
|
46
|
+
def dialer(endpoint, engine, **)
|
|
47
|
+
Dialer.new(endpoint, engine)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
|
|
37
51
|
# Validates that the endpoint's host can be resolved.
|
|
38
52
|
#
|
|
39
53
|
# @param endpoint [String]
|
|
@@ -46,21 +60,6 @@ module OMQ
|
|
|
46
60
|
end
|
|
47
61
|
|
|
48
62
|
|
|
49
|
-
# Connects to a TCP endpoint.
|
|
50
|
-
#
|
|
51
|
-
# @param endpoint [String] e.g. "tcp://127.0.0.1:5555"
|
|
52
|
-
# @param engine [Engine]
|
|
53
|
-
# @return [void]
|
|
54
|
-
#
|
|
55
|
-
def connect(endpoint, engine)
|
|
56
|
-
host, port = self.parse_endpoint(endpoint)
|
|
57
|
-
host = normalize_connect_host(host)
|
|
58
|
-
sock = ::Socket.tcp(host, port, connect_timeout: connect_timeout(engine.options))
|
|
59
|
-
apply_buffer_sizes(sock, engine.options)
|
|
60
|
-
engine.handle_connected(IO::Stream::Buffered.wrap(sock), endpoint: endpoint)
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
|
|
64
63
|
# Normalizes the bind host:
|
|
65
64
|
# "*" → nil (dual-stack wildcard via AI_PASSIVE)
|
|
66
65
|
# "" / nil / "localhost" → loopback_host (::1 on IPv6-capable hosts, else 127.0.0.1)
|
|
@@ -142,6 +141,42 @@ module OMQ
|
|
|
142
141
|
end
|
|
143
142
|
|
|
144
143
|
|
|
144
|
+
# A TCP dialer — stateful factory for outgoing connections.
|
|
145
|
+
#
|
|
146
|
+
# Created once per {Engine#connect}, stored in +@dialers[endpoint]+.
|
|
147
|
+
# Reconnect calls {#connect} directly — no transport lookup or opts
|
|
148
|
+
# replay needed.
|
|
149
|
+
#
|
|
150
|
+
class Dialer
|
|
151
|
+
# @return [String] the endpoint this dialer connects to
|
|
152
|
+
#
|
|
153
|
+
attr_reader :endpoint
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
# @param endpoint [String] e.g. "tcp://127.0.0.1:5555"
|
|
157
|
+
# @param engine [Engine]
|
|
158
|
+
#
|
|
159
|
+
def initialize(endpoint, engine)
|
|
160
|
+
@endpoint = endpoint
|
|
161
|
+
@engine = engine
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
# Establishes a TCP connection to the endpoint.
|
|
166
|
+
#
|
|
167
|
+
# @return [void]
|
|
168
|
+
#
|
|
169
|
+
def connect
|
|
170
|
+
host, port = TCP.parse_endpoint(@endpoint)
|
|
171
|
+
host = TCP.normalize_connect_host(host)
|
|
172
|
+
sock = ::Socket.tcp(host, port, connect_timeout: TCP.connect_timeout(@engine.options))
|
|
173
|
+
TCP.apply_buffer_sizes(sock, @engine.options)
|
|
174
|
+
@engine.handle_connected(IO::Stream::Buffered.wrap(sock), endpoint: @endpoint)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
|
|
145
180
|
# A bound TCP listener.
|
|
146
181
|
#
|
|
147
182
|
class Listener
|
|
@@ -168,7 +203,7 @@ module OMQ
|
|
|
168
203
|
@servers = servers
|
|
169
204
|
@port = port
|
|
170
205
|
@engine = engine
|
|
171
|
-
@
|
|
206
|
+
@barrier = nil
|
|
172
207
|
end
|
|
173
208
|
|
|
174
209
|
|
|
@@ -179,10 +214,11 @@ module OMQ
|
|
|
179
214
|
# @yieldparam io [IO::Stream::Buffered]
|
|
180
215
|
#
|
|
181
216
|
def start_accept_loops(parent_task)
|
|
182
|
-
@
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
217
|
+
@barrier = Async::Barrier.new(parent: parent_task)
|
|
218
|
+
|
|
219
|
+
@servers.each do |server|
|
|
220
|
+
annotation = "tcp accept #{server.local_address.inspect_sockaddr}"
|
|
221
|
+
@barrier.async(transient: true, annotation:) do
|
|
186
222
|
loop do
|
|
187
223
|
client, _addr = server.accept
|
|
188
224
|
TCP.apply_buffer_sizes(client, @engine.options)
|
|
@@ -207,7 +243,7 @@ module OMQ
|
|
|
207
243
|
# @return [void]
|
|
208
244
|
#
|
|
209
245
|
def stop
|
|
210
|
-
@
|
|
246
|
+
@barrier&.stop
|
|
211
247
|
@servers.each { |s| s.close rescue nil }
|
|
212
248
|
end
|
|
213
249
|
|