omq 0.9.0 → 0.11.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 +129 -0
- data/README.md +28 -3
- data/lib/omq/channel.rb +5 -5
- data/lib/omq/client_server.rb +10 -10
- data/lib/omq/engine.rb +702 -0
- data/lib/omq/options.rb +48 -0
- data/lib/omq/pair.rb +4 -4
- data/lib/omq/peer.rb +5 -5
- data/lib/omq/pub_sub.rb +18 -18
- data/lib/omq/push_pull.rb +6 -6
- data/lib/omq/queue_interface.rb +73 -0
- data/lib/omq/radio_dish.rb +6 -6
- data/lib/omq/reactor.rb +128 -0
- data/lib/omq/readable.rb +44 -0
- data/lib/omq/req_rep.rb +8 -8
- data/lib/omq/router_dealer.rb +8 -8
- data/lib/omq/routing/channel.rb +83 -0
- data/lib/omq/routing/client.rb +56 -0
- data/lib/omq/routing/dealer.rb +57 -0
- data/lib/omq/routing/dish.rb +78 -0
- data/lib/omq/routing/fan_out.rb +140 -0
- data/lib/omq/routing/gather.rb +46 -0
- data/lib/omq/routing/pair.rb +86 -0
- data/lib/omq/routing/peer.rb +101 -0
- data/lib/omq/routing/pub.rb +60 -0
- data/lib/omq/routing/pull.rb +46 -0
- data/lib/omq/routing/push.rb +81 -0
- data/lib/omq/routing/radio.rb +150 -0
- data/lib/omq/routing/rep.rb +101 -0
- data/lib/omq/routing/req.rb +65 -0
- data/lib/omq/routing/round_robin.rb +168 -0
- data/lib/omq/routing/router.rb +110 -0
- data/lib/omq/routing/scatter.rb +82 -0
- data/lib/omq/routing/server.rb +101 -0
- data/lib/omq/routing/sub.rb +78 -0
- data/lib/omq/routing/xpub.rb +72 -0
- data/lib/omq/routing/xsub.rb +83 -0
- data/lib/omq/routing.rb +66 -0
- data/lib/omq/scatter_gather.rb +8 -8
- data/lib/omq/single_frame.rb +18 -0
- data/lib/omq/socket.rb +32 -11
- data/lib/omq/transport/inproc.rb +355 -0
- data/lib/omq/transport/ipc.rb +117 -0
- data/lib/omq/transport/tcp.rb +111 -0
- data/lib/omq/transport/tls.rb +146 -0
- data/lib/omq/version.rb +1 -1
- data/lib/omq/writable.rb +66 -0
- data/lib/omq.rb +64 -4
- metadata +34 -33
- data/lib/omq/zmtp/engine.rb +0 -551
- data/lib/omq/zmtp/options.rb +0 -48
- data/lib/omq/zmtp/reactor.rb +0 -131
- data/lib/omq/zmtp/readable.rb +0 -29
- data/lib/omq/zmtp/routing/channel.rb +0 -81
- data/lib/omq/zmtp/routing/client.rb +0 -56
- data/lib/omq/zmtp/routing/dealer.rb +0 -57
- data/lib/omq/zmtp/routing/dish.rb +0 -80
- data/lib/omq/zmtp/routing/fan_out.rb +0 -131
- data/lib/omq/zmtp/routing/gather.rb +0 -48
- data/lib/omq/zmtp/routing/pair.rb +0 -84
- data/lib/omq/zmtp/routing/peer.rb +0 -100
- data/lib/omq/zmtp/routing/pub.rb +0 -62
- data/lib/omq/zmtp/routing/pull.rb +0 -48
- data/lib/omq/zmtp/routing/push.rb +0 -80
- data/lib/omq/zmtp/routing/radio.rb +0 -139
- data/lib/omq/zmtp/routing/rep.rb +0 -101
- data/lib/omq/zmtp/routing/req.rb +0 -65
- data/lib/omq/zmtp/routing/round_robin.rb +0 -143
- data/lib/omq/zmtp/routing/router.rb +0 -109
- data/lib/omq/zmtp/routing/scatter.rb +0 -81
- data/lib/omq/zmtp/routing/server.rb +0 -100
- data/lib/omq/zmtp/routing/sub.rb +0 -80
- data/lib/omq/zmtp/routing/xpub.rb +0 -74
- data/lib/omq/zmtp/routing/xsub.rb +0 -86
- data/lib/omq/zmtp/routing.rb +0 -65
- data/lib/omq/zmtp/single_frame.rb +0 -20
- data/lib/omq/zmtp/transport/inproc.rb +0 -359
- data/lib/omq/zmtp/transport/ipc.rb +0 -118
- data/lib/omq/zmtp/transport/tcp.rb +0 -117
- data/lib/omq/zmtp/writable.rb +0 -61
- data/lib/omq/zmtp.rb +0 -81
|
@@ -0,0 +1,101 @@
|
|
|
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
|
+
# @param engine [Engine]
|
|
15
|
+
#
|
|
16
|
+
def initialize(engine)
|
|
17
|
+
@engine = engine
|
|
18
|
+
@recv_queue = Async::LimitedQueue.new(engine.options.recv_hwm)
|
|
19
|
+
@send_queue = Async::LimitedQueue.new(engine.options.send_hwm)
|
|
20
|
+
@connections_by_routing_id = {}
|
|
21
|
+
@routing_id_by_connection = {}
|
|
22
|
+
@tasks = []
|
|
23
|
+
@send_pump_started = false
|
|
24
|
+
@send_pump_idle = true
|
|
25
|
+
@written = Set.new
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# @return [Async::LimitedQueue]
|
|
29
|
+
#
|
|
30
|
+
attr_reader :recv_queue, :send_queue
|
|
31
|
+
|
|
32
|
+
# @param connection [Connection]
|
|
33
|
+
#
|
|
34
|
+
def connection_added(connection)
|
|
35
|
+
routing_id = SecureRandom.bytes(4)
|
|
36
|
+
@connections_by_routing_id[routing_id] = connection
|
|
37
|
+
@routing_id_by_connection[connection] = routing_id
|
|
38
|
+
|
|
39
|
+
task = @engine.start_recv_pump(connection, @recv_queue) do |msg|
|
|
40
|
+
[routing_id, *msg]
|
|
41
|
+
end
|
|
42
|
+
@tasks << task if task
|
|
43
|
+
|
|
44
|
+
start_send_pump unless @send_pump_started
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# @param connection [Connection]
|
|
48
|
+
#
|
|
49
|
+
def connection_removed(connection)
|
|
50
|
+
routing_id = @routing_id_by_connection.delete(connection)
|
|
51
|
+
@connections_by_routing_id.delete(routing_id) if routing_id
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# @param parts [Array<String>]
|
|
55
|
+
#
|
|
56
|
+
def enqueue(parts)
|
|
57
|
+
@send_queue.enqueue(parts)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def stop
|
|
61
|
+
@tasks.each(&:stop)
|
|
62
|
+
@tasks.clear
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def send_pump_idle? = @send_pump_idle
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
def start_send_pump
|
|
70
|
+
@send_pump_started = true
|
|
71
|
+
@tasks << @engine.spawn_pump_task(annotation: "send pump") do
|
|
72
|
+
loop do
|
|
73
|
+
@send_pump_idle = true
|
|
74
|
+
batch = [@send_queue.dequeue]
|
|
75
|
+
@send_pump_idle = false
|
|
76
|
+
Routing.drain_send_queue(@send_queue, batch)
|
|
77
|
+
|
|
78
|
+
@written.clear
|
|
79
|
+
batch.each do |parts|
|
|
80
|
+
routing_id = parts.first
|
|
81
|
+
conn = @connections_by_routing_id[routing_id]
|
|
82
|
+
next unless conn # silently drop if peer gone
|
|
83
|
+
begin
|
|
84
|
+
conn.write_message(parts[1..])
|
|
85
|
+
@written << conn
|
|
86
|
+
rescue *CONNECTION_LOST
|
|
87
|
+
# will be cleaned up
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
@written.each do |conn|
|
|
92
|
+
conn.flush
|
|
93
|
+
rescue *CONNECTION_LOST
|
|
94
|
+
# will be cleaned up
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OMQ
|
|
4
|
+
module Routing
|
|
5
|
+
# SUB socket routing: subscription-based receive from PUB peers.
|
|
6
|
+
#
|
|
7
|
+
# Sends SUBSCRIBE/CANCEL commands to connected PUB peers.
|
|
8
|
+
#
|
|
9
|
+
class Sub
|
|
10
|
+
|
|
11
|
+
# @param engine [Engine]
|
|
12
|
+
#
|
|
13
|
+
def initialize(engine)
|
|
14
|
+
@engine = engine
|
|
15
|
+
@connections = []
|
|
16
|
+
@recv_queue = Async::LimitedQueue.new(engine.options.recv_hwm)
|
|
17
|
+
@subscriptions = Set.new
|
|
18
|
+
@tasks = []
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# @return [Async::LimitedQueue]
|
|
22
|
+
#
|
|
23
|
+
attr_reader :recv_queue
|
|
24
|
+
|
|
25
|
+
# @param connection [Connection]
|
|
26
|
+
#
|
|
27
|
+
def connection_added(connection)
|
|
28
|
+
@connections << connection
|
|
29
|
+
# Send existing subscriptions to new peer
|
|
30
|
+
@subscriptions.each do |prefix|
|
|
31
|
+
connection.send_command(Protocol::ZMTP::Codec::Command.subscribe(prefix))
|
|
32
|
+
end
|
|
33
|
+
task = @engine.start_recv_pump(connection, @recv_queue)
|
|
34
|
+
@tasks << task if task
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @param connection [Connection]
|
|
38
|
+
#
|
|
39
|
+
def connection_removed(connection)
|
|
40
|
+
@connections.delete(connection)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# SUB is read-only.
|
|
44
|
+
#
|
|
45
|
+
def enqueue(_parts)
|
|
46
|
+
raise "SUB sockets cannot send"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Subscribes to a topic prefix.
|
|
50
|
+
#
|
|
51
|
+
# @param prefix [String]
|
|
52
|
+
#
|
|
53
|
+
def subscribe(prefix)
|
|
54
|
+
@subscriptions << prefix
|
|
55
|
+
@connections.each do |conn|
|
|
56
|
+
conn.send_command(Protocol::ZMTP::Codec::Command.subscribe(prefix))
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Unsubscribes from a topic prefix.
|
|
61
|
+
#
|
|
62
|
+
# @param prefix [String]
|
|
63
|
+
#
|
|
64
|
+
def unsubscribe(prefix)
|
|
65
|
+
@subscriptions.delete(prefix)
|
|
66
|
+
@connections.each do |conn|
|
|
67
|
+
conn.send_command(Protocol::ZMTP::Codec::Command.cancel(prefix))
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def stop
|
|
72
|
+
@tasks.each(&:stop)
|
|
73
|
+
@tasks.clear
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OMQ
|
|
4
|
+
module Routing
|
|
5
|
+
# XPUB socket routing: like PUB but exposes subscription messages.
|
|
6
|
+
#
|
|
7
|
+
# Subscription/unsubscription messages from peers are delivered to
|
|
8
|
+
# the application as data frames: \x01 + prefix for subscribe,
|
|
9
|
+
# \x00 + prefix for unsubscribe.
|
|
10
|
+
#
|
|
11
|
+
class XPub
|
|
12
|
+
include FanOut
|
|
13
|
+
|
|
14
|
+
# @param engine [Engine]
|
|
15
|
+
#
|
|
16
|
+
def initialize(engine)
|
|
17
|
+
@engine = engine
|
|
18
|
+
@recv_queue = Async::LimitedQueue.new(engine.options.recv_hwm)
|
|
19
|
+
@tasks = []
|
|
20
|
+
init_fan_out(engine)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @return [Async::LimitedQueue]
|
|
24
|
+
#
|
|
25
|
+
attr_reader :recv_queue, :send_queue
|
|
26
|
+
|
|
27
|
+
# @param connection [Connection]
|
|
28
|
+
#
|
|
29
|
+
def connection_added(connection)
|
|
30
|
+
@connections << connection
|
|
31
|
+
@subscriptions[connection] = Set.new
|
|
32
|
+
start_subscription_listener(connection)
|
|
33
|
+
start_send_pump unless @send_pump_started
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# @param connection [Connection]
|
|
37
|
+
#
|
|
38
|
+
def connection_removed(connection)
|
|
39
|
+
@connections.delete(connection)
|
|
40
|
+
@subscriptions.delete(connection)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# @param parts [Array<String>]
|
|
44
|
+
#
|
|
45
|
+
def enqueue(parts)
|
|
46
|
+
@send_queue.enqueue(parts)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
#
|
|
50
|
+
def stop
|
|
51
|
+
@tasks.each(&:stop)
|
|
52
|
+
@tasks.clear
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
# Expose subscription to application as data message.
|
|
58
|
+
#
|
|
59
|
+
def on_subscribe(conn, prefix)
|
|
60
|
+
super
|
|
61
|
+
@recv_queue.enqueue(["\x01#{prefix}".b])
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Expose unsubscription to application as data message.
|
|
65
|
+
#
|
|
66
|
+
def on_cancel(conn, prefix)
|
|
67
|
+
super
|
|
68
|
+
@recv_queue.enqueue(["\x00#{prefix}".b])
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OMQ
|
|
4
|
+
module Routing
|
|
5
|
+
# XSUB socket routing: like SUB but subscriptions sent as data messages.
|
|
6
|
+
#
|
|
7
|
+
# Subscriptions are sent as data frames: \x01 + prefix for subscribe,
|
|
8
|
+
# \x00 + prefix for unsubscribe.
|
|
9
|
+
#
|
|
10
|
+
class XSub
|
|
11
|
+
|
|
12
|
+
# @param engine [Engine]
|
|
13
|
+
#
|
|
14
|
+
def initialize(engine)
|
|
15
|
+
@engine = engine
|
|
16
|
+
@connections = []
|
|
17
|
+
@recv_queue = Async::LimitedQueue.new(engine.options.recv_hwm)
|
|
18
|
+
@send_queue = Async::LimitedQueue.new(engine.options.send_hwm)
|
|
19
|
+
@tasks = []
|
|
20
|
+
@send_pump_started = false
|
|
21
|
+
@send_pump_idle = true
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# @return [Async::LimitedQueue]
|
|
25
|
+
#
|
|
26
|
+
attr_reader :recv_queue, :send_queue
|
|
27
|
+
|
|
28
|
+
# @param connection [Connection]
|
|
29
|
+
#
|
|
30
|
+
def connection_added(connection)
|
|
31
|
+
@connections << connection
|
|
32
|
+
task = @engine.start_recv_pump(connection, @recv_queue)
|
|
33
|
+
@tasks << task if task
|
|
34
|
+
start_send_pump unless @send_pump_started
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @param connection [Connection]
|
|
38
|
+
#
|
|
39
|
+
def connection_removed(connection)
|
|
40
|
+
@connections.delete(connection)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# @param parts [Array<String>]
|
|
44
|
+
#
|
|
45
|
+
def enqueue(parts)
|
|
46
|
+
@send_queue.enqueue(parts)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
#
|
|
50
|
+
def stop
|
|
51
|
+
@tasks.each(&:stop)
|
|
52
|
+
@tasks.clear
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def send_pump_idle? = @send_pump_idle
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def start_send_pump
|
|
60
|
+
@send_pump_started = true
|
|
61
|
+
@tasks << @engine.spawn_pump_task(annotation: "send pump") do
|
|
62
|
+
loop do
|
|
63
|
+
@send_pump_idle = true
|
|
64
|
+
parts = @send_queue.dequeue
|
|
65
|
+
@send_pump_idle = false
|
|
66
|
+
frame = parts.first&.b
|
|
67
|
+
next if frame.nil? || frame.empty?
|
|
68
|
+
|
|
69
|
+
flag = frame.getbyte(0)
|
|
70
|
+
prefix = frame.byteslice(1..) || "".b
|
|
71
|
+
|
|
72
|
+
case flag
|
|
73
|
+
when 0x01
|
|
74
|
+
@connections.each { |c| c.send_command(Protocol::ZMTP::Codec::Command.subscribe(prefix)) }
|
|
75
|
+
when 0x00
|
|
76
|
+
@connections.each { |c| c.send_command(Protocol::ZMTP::Codec::Command.cancel(prefix)) }
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
data/lib/omq/routing.rb
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "async"
|
|
4
|
+
require "async/queue"
|
|
5
|
+
require "async/limited_queue"
|
|
6
|
+
|
|
7
|
+
module OMQ
|
|
8
|
+
# Routing strategies for each ZMQ socket type.
|
|
9
|
+
#
|
|
10
|
+
# Each strategy manages how messages flow between connections and
|
|
11
|
+
# the socket's send/recv queues.
|
|
12
|
+
#
|
|
13
|
+
module Routing
|
|
14
|
+
# Shared frozen empty binary string to avoid repeated allocations.
|
|
15
|
+
EMPTY_BINARY = "".b.freeze
|
|
16
|
+
|
|
17
|
+
# Drains all available messages from +queue+ into +batch+ without
|
|
18
|
+
# blocking. Call after the initial blocking dequeue.
|
|
19
|
+
#
|
|
20
|
+
# No cap is needed: IO::Stream auto-flushes at 64 KB, so the
|
|
21
|
+
# write buffer hits the wire naturally under sustained load.
|
|
22
|
+
# The explicit flush after the batch pushes out the remainder.
|
|
23
|
+
#
|
|
24
|
+
# @param queue [Async::LimitedQueue]
|
|
25
|
+
# @param batch [Array]
|
|
26
|
+
# @return [void]
|
|
27
|
+
#
|
|
28
|
+
def self.drain_send_queue(queue, batch)
|
|
29
|
+
loop do
|
|
30
|
+
msg = queue.dequeue(timeout: 0)
|
|
31
|
+
break unless msg
|
|
32
|
+
batch << msg
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Returns the routing strategy class for a socket type.
|
|
37
|
+
#
|
|
38
|
+
# @param socket_type [Symbol] e.g. :PAIR, :REQ
|
|
39
|
+
# @return [Class]
|
|
40
|
+
#
|
|
41
|
+
def self.for(socket_type)
|
|
42
|
+
case socket_type
|
|
43
|
+
when :PAIR then Pair
|
|
44
|
+
when :REQ then Req
|
|
45
|
+
when :REP then Rep
|
|
46
|
+
when :DEALER then Dealer
|
|
47
|
+
when :ROUTER then Router
|
|
48
|
+
when :PUB then Pub
|
|
49
|
+
when :SUB then Sub
|
|
50
|
+
when :XPUB then XPub
|
|
51
|
+
when :XSUB then XSub
|
|
52
|
+
when :PUSH then Push
|
|
53
|
+
when :PULL then Pull
|
|
54
|
+
when :CLIENT then Client
|
|
55
|
+
when :SERVER then Server
|
|
56
|
+
when :RADIO then Radio
|
|
57
|
+
when :DISH then Dish
|
|
58
|
+
when :SCATTER then Scatter
|
|
59
|
+
when :GATHER then Gather
|
|
60
|
+
when :PEER then Peer
|
|
61
|
+
when :CHANNEL then Channel
|
|
62
|
+
else raise ArgumentError, "unknown socket type: #{socket_type}"
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
data/lib/omq/scatter_gather.rb
CHANGED
|
@@ -2,21 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
module OMQ
|
|
4
4
|
class SCATTER < Socket
|
|
5
|
-
include
|
|
6
|
-
include
|
|
5
|
+
include Writable
|
|
6
|
+
include SingleFrame
|
|
7
7
|
|
|
8
|
-
def initialize(endpoints = nil, linger: 0, send_hwm: nil, send_timeout: nil)
|
|
9
|
-
_init_engine(:SCATTER, linger: linger, send_hwm: send_hwm, send_timeout: send_timeout)
|
|
8
|
+
def initialize(endpoints = nil, linger: 0, send_hwm: nil, send_timeout: nil, backend: nil)
|
|
9
|
+
_init_engine(:SCATTER, linger: linger, send_hwm: send_hwm, send_timeout: send_timeout, backend: backend)
|
|
10
10
|
_attach(endpoints, default: :connect)
|
|
11
11
|
end
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
class GATHER < Socket
|
|
15
|
-
include
|
|
16
|
-
include
|
|
15
|
+
include Readable
|
|
16
|
+
include SingleFrame
|
|
17
17
|
|
|
18
|
-
def initialize(endpoints = nil, linger: 0, recv_hwm: nil, recv_timeout: nil)
|
|
19
|
-
_init_engine(:GATHER, linger: linger, recv_hwm: recv_hwm, recv_timeout: recv_timeout)
|
|
18
|
+
def initialize(endpoints = nil, linger: 0, recv_hwm: nil, recv_timeout: nil, backend: nil)
|
|
19
|
+
_init_engine(:GATHER, linger: linger, recv_hwm: recv_hwm, recv_timeout: recv_timeout, backend: backend)
|
|
20
20
|
_attach(endpoints, default: :bind)
|
|
21
21
|
end
|
|
22
22
|
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OMQ
|
|
4
|
+
# Mixin that rejects multipart messages.
|
|
5
|
+
#
|
|
6
|
+
# All draft socket types (CLIENT, SERVER, RADIO, DISH, SCATTER,
|
|
7
|
+
# GATHER, PEER, CHANNEL) require single-frame messages for
|
|
8
|
+
# thread-safe atomic operations.
|
|
9
|
+
#
|
|
10
|
+
module SingleFrame
|
|
11
|
+
def send(message)
|
|
12
|
+
if message.is_a?(Array) && message.size > 1
|
|
13
|
+
raise ArgumentError, "#{self.class} does not support multipart messages"
|
|
14
|
+
end
|
|
15
|
+
super
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
data/lib/omq/socket.rb
CHANGED
|
@@ -6,7 +6,7 @@ module OMQ
|
|
|
6
6
|
# Socket base class.
|
|
7
7
|
#
|
|
8
8
|
class Socket
|
|
9
|
-
# @return [
|
|
9
|
+
# @return [Options]
|
|
10
10
|
#
|
|
11
11
|
attr_reader :options
|
|
12
12
|
|
|
@@ -36,7 +36,8 @@ module OMQ
|
|
|
36
36
|
:heartbeat_ttl, :heartbeat_ttl=,
|
|
37
37
|
:heartbeat_timeout, :heartbeat_timeout=,
|
|
38
38
|
:max_message_size, :max_message_size=,
|
|
39
|
-
:mechanism, :mechanism
|
|
39
|
+
:mechanism, :mechanism=,
|
|
40
|
+
:tls_context, :tls_context=
|
|
40
41
|
|
|
41
42
|
|
|
42
43
|
# Creates a new socket and binds it to the given endpoint.
|
|
@@ -70,8 +71,11 @@ module OMQ
|
|
|
70
71
|
# @return [void]
|
|
71
72
|
#
|
|
72
73
|
def bind(endpoint)
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
ensure_parent_task
|
|
75
|
+
Reactor.run do
|
|
76
|
+
@engine.bind(endpoint)
|
|
77
|
+
@last_tcp_port = @engine.last_tcp_port
|
|
78
|
+
end
|
|
75
79
|
end
|
|
76
80
|
|
|
77
81
|
|
|
@@ -81,7 +85,8 @@ module OMQ
|
|
|
81
85
|
# @return [void]
|
|
82
86
|
#
|
|
83
87
|
def connect(endpoint)
|
|
84
|
-
|
|
88
|
+
ensure_parent_task
|
|
89
|
+
Reactor.run { @engine.connect(endpoint) }
|
|
85
90
|
end
|
|
86
91
|
|
|
87
92
|
|
|
@@ -91,7 +96,7 @@ module OMQ
|
|
|
91
96
|
# @return [void]
|
|
92
97
|
#
|
|
93
98
|
def disconnect(endpoint)
|
|
94
|
-
@engine.disconnect(endpoint)
|
|
99
|
+
Reactor.run { @engine.disconnect(endpoint) }
|
|
95
100
|
end
|
|
96
101
|
|
|
97
102
|
|
|
@@ -101,7 +106,7 @@ module OMQ
|
|
|
101
106
|
# @return [void]
|
|
102
107
|
#
|
|
103
108
|
def unbind(endpoint)
|
|
104
|
-
@engine.unbind(endpoint)
|
|
109
|
+
Reactor.run { @engine.unbind(endpoint) }
|
|
105
110
|
end
|
|
106
111
|
|
|
107
112
|
|
|
@@ -145,7 +150,7 @@ module OMQ
|
|
|
145
150
|
# Closes the socket.
|
|
146
151
|
#
|
|
147
152
|
def close
|
|
148
|
-
@engine.close
|
|
153
|
+
Reactor.run { @engine.close }
|
|
149
154
|
nil
|
|
150
155
|
end
|
|
151
156
|
|
|
@@ -187,6 +192,15 @@ module OMQ
|
|
|
187
192
|
end
|
|
188
193
|
|
|
189
194
|
|
|
195
|
+
# Sets the engine's parent task before the first bind or connect.
|
|
196
|
+
# Must be called OUTSIDE Reactor.run so that non-Async callers
|
|
197
|
+
# get the IO thread's root task, not an ephemeral work task.
|
|
198
|
+
#
|
|
199
|
+
def ensure_parent_task
|
|
200
|
+
@engine.capture_parent_task
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
|
|
190
204
|
# Connects or binds based on endpoint prefix convention.
|
|
191
205
|
#
|
|
192
206
|
# @param endpoints [String, nil]
|
|
@@ -211,14 +225,21 @@ module OMQ
|
|
|
211
225
|
# @param linger [Integer]
|
|
212
226
|
#
|
|
213
227
|
def _init_engine(socket_type, linger:, send_hwm: nil, recv_hwm: nil,
|
|
214
|
-
send_timeout: nil, recv_timeout: nil, conflate: false
|
|
215
|
-
|
|
228
|
+
send_timeout: nil, recv_timeout: nil, conflate: false,
|
|
229
|
+
backend: nil)
|
|
230
|
+
@options = Options.new(linger: linger)
|
|
216
231
|
@options.send_hwm = send_hwm if send_hwm
|
|
217
232
|
@options.recv_hwm = recv_hwm if recv_hwm
|
|
218
233
|
@options.send_timeout = send_timeout if send_timeout
|
|
219
234
|
@options.recv_timeout = recv_timeout if recv_timeout
|
|
220
235
|
@options.conflate = conflate
|
|
221
|
-
@
|
|
236
|
+
@recv_buffer = []
|
|
237
|
+
@recv_mutex = Mutex.new
|
|
238
|
+
@engine = case backend
|
|
239
|
+
when nil, :ruby then Engine.new(socket_type, @options)
|
|
240
|
+
when :ffi then FFI::Engine.new(socket_type, @options)
|
|
241
|
+
else raise ArgumentError, "unknown backend: #{backend}"
|
|
242
|
+
end
|
|
222
243
|
end
|
|
223
244
|
end
|
|
224
245
|
end
|