amqp-client 1.0.0 → 1.0.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/.github/workflows/docs.yml +25 -0
- data/.rubocop.yml +12 -1
- data/.rubocop_todo.yml +14 -58
- data/.yardopts +1 -0
- data/CHANGELOG.md +10 -0
- data/README.md +4 -0
- data/lib/amqp/client/channel.rb +480 -275
- data/lib/amqp/client/connection.rb +401 -356
- data/lib/amqp/client/errors.rb +30 -26
- data/lib/amqp/client/exchange.rb +66 -0
- data/lib/amqp/client/frames.rb +526 -522
- data/lib/amqp/client/message.rb +48 -9
- data/lib/amqp/client/properties.rb +227 -194
- data/lib/amqp/client/queue.rb +78 -0
- data/lib/amqp/client/table.rb +118 -107
- data/lib/amqp/client/version.rb +2 -1
- data/lib/amqp/client.rb +128 -95
- metadata +6 -2
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AMQP
|
4
|
+
class Client
|
5
|
+
# Queue abstraction
|
6
|
+
class Queue
|
7
|
+
# Should only be initialized from the Client
|
8
|
+
# @api private
|
9
|
+
def initialize(client, name)
|
10
|
+
@client = client
|
11
|
+
@name = name
|
12
|
+
end
|
13
|
+
|
14
|
+
# Publish to the queue
|
15
|
+
# @param body [String] The message body
|
16
|
+
# @param properties [Properties]
|
17
|
+
# @option properties [String] content_type Content type of the message body
|
18
|
+
# @option properties [String] content_encoding Content encoding of the body
|
19
|
+
# @option properties [Hash<String, Object>] headers Custom headers
|
20
|
+
# @option properties [Integer] delivery_mode 2 for persisted message, transient messages for all other values
|
21
|
+
# @option properties [Integer] priority A priority of the message (between 0 and 255)
|
22
|
+
# @option properties [Integer] correlation_id A correlation id, most often used used for RPC communication
|
23
|
+
# @option properties [String] reply_to Queue to reply RPC responses to
|
24
|
+
# @option properties [Integer, String] expiration Number of seconds the message will stay in the queue
|
25
|
+
# @option properties [String] message_id Can be used to uniquely identify the message, e.g. for deduplication
|
26
|
+
# @option properties [Date] timestamp Often used for the time the message was originally generated
|
27
|
+
# @option properties [String] type Can indicate what kind of message this is
|
28
|
+
# @option properties [String] user_id Can be used to verify that this is the user that published the message
|
29
|
+
# @option properties [String] app_id Can be used to indicates which app that generated the message
|
30
|
+
# @return [Queue] self
|
31
|
+
def publish(body, **properties)
|
32
|
+
@client.publish(body, "", @name, **properties)
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
# Subscribe/consume from the queue
|
37
|
+
# @return [Queue] self
|
38
|
+
def subscribe(no_ack: false, prefetch: 1, worker_threads: 1, arguments: {}, &blk)
|
39
|
+
@client.subscribe(@name, no_ack: no_ack, prefetch: prefetch, worker_threads: worker_threads, arguments: arguments, &blk)
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
43
|
+
# Bind the queue to an exchange
|
44
|
+
# @param exchange [String] Name of the exchange to bind to
|
45
|
+
# @param binding_key [String] Binding key on which messages that match might be routed (depending on exchange type)
|
46
|
+
# @param arguments [Hash] Message headers to match on (only relevant for header exchanges)
|
47
|
+
# @return [Queue] self
|
48
|
+
def bind(exchange, binding_key, arguments: {})
|
49
|
+
@client.bind(@name, exchange, binding_key, arguments: arguments)
|
50
|
+
self
|
51
|
+
end
|
52
|
+
|
53
|
+
# Unbind the queue from an exchange
|
54
|
+
# @param exchange [String] Name of the exchange to unbind from
|
55
|
+
# @param binding_key [String] Binding key which the queue is bound to the exchange with
|
56
|
+
# @param arguments [Hash] Arguments matching the binding that's being removed
|
57
|
+
# @return [Queue] self
|
58
|
+
def unbind(exchange, binding_key, arguments: {})
|
59
|
+
@client.unbind(@name, exchange, binding_key, arguments: arguments)
|
60
|
+
self
|
61
|
+
end
|
62
|
+
|
63
|
+
# Purge/empty the queue
|
64
|
+
# @return [Queue] self
|
65
|
+
def purge
|
66
|
+
@client.purge(@name)
|
67
|
+
self
|
68
|
+
end
|
69
|
+
|
70
|
+
# Delete the queue
|
71
|
+
# @return [nil]
|
72
|
+
def delete
|
73
|
+
@client.delete_queue(@name)
|
74
|
+
nil
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/lib/amqp/client/table.rb
CHANGED
@@ -1,122 +1,133 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module AMQP
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
class Client
|
5
|
+
# Encode and decode an AMQP table to/from hash, only used internally
|
6
|
+
# @api private
|
7
|
+
module Table
|
8
|
+
module_function
|
7
9
|
|
8
|
-
|
9
|
-
|
10
|
-
hash
|
11
|
-
|
12
|
-
|
10
|
+
# Encodes a hash into a byte array
|
11
|
+
# @return [String] Byte array
|
12
|
+
def encode(hash)
|
13
|
+
tbl = StringIO.new
|
14
|
+
hash.each do |k, v|
|
15
|
+
key = k.to_s
|
16
|
+
tbl.write [key.bytesize, key].pack("Ca*")
|
17
|
+
tbl.write encode_field(v)
|
18
|
+
end
|
19
|
+
tbl.string
|
13
20
|
end
|
14
|
-
tbl
|
15
|
-
end
|
16
|
-
|
17
|
-
def decode(bytes)
|
18
|
-
h = {}
|
19
|
-
pos = 0
|
20
21
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
pos
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
22
|
+
# Decodes an AMQP table into a hash
|
23
|
+
# @return [Hash<String, Object>]
|
24
|
+
def decode(bytes)
|
25
|
+
hash = {}
|
26
|
+
pos = 0
|
27
|
+
while pos < bytes.bytesize
|
28
|
+
key_len = bytes[pos].ord
|
29
|
+
pos += 1
|
30
|
+
key = bytes.byteslice(pos, key_len).force_encoding("utf-8")
|
31
|
+
pos += key_len
|
32
|
+
rest = bytes.byteslice(pos, bytes.bytesize - pos)
|
33
|
+
len, value = decode_field(rest)
|
34
|
+
pos += len + 1
|
35
|
+
hash[key] = value
|
36
|
+
end
|
37
|
+
hash
|
30
38
|
end
|
31
|
-
h
|
32
|
-
end
|
33
39
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
40
|
+
# Encoding a single value in a table
|
41
|
+
# @api private
|
42
|
+
def encode_field(value)
|
43
|
+
case value
|
44
|
+
when Integer
|
45
|
+
if value > 2**31
|
46
|
+
["l", value].pack("a q>")
|
47
|
+
else
|
48
|
+
["I", value].pack("a l>")
|
49
|
+
end
|
50
|
+
when Float
|
51
|
+
["d", value].pack("a G")
|
52
|
+
when String
|
53
|
+
["S", value.bytesize, value].pack("a L> a*")
|
54
|
+
when Time
|
55
|
+
["T", value.to_i].pack("a Q>")
|
56
|
+
when Array
|
57
|
+
bytes = value.map { |e| encode_field(e) }.join
|
58
|
+
["A", bytes.bytesize, bytes].pack("a L> a*")
|
59
|
+
when Hash
|
60
|
+
bytes = Table.encode(value)
|
61
|
+
["F", bytes.bytesize, bytes].pack("a L> a*")
|
62
|
+
when true
|
63
|
+
["t", 1].pack("a C")
|
64
|
+
when false
|
65
|
+
["t", 0].pack("a C")
|
66
|
+
when nil
|
67
|
+
["V"].pack("a")
|
68
|
+
else raise ArgumentError, "unsupported table field type: #{value.class}"
|
41
69
|
end
|
42
|
-
when Float
|
43
|
-
["d", value].pack("a G")
|
44
|
-
when String
|
45
|
-
["S", value.bytesize, value].pack("a L> a*")
|
46
|
-
when Time
|
47
|
-
["T", value.to_i].pack("a Q>")
|
48
|
-
when Array
|
49
|
-
bytes = value.map { |e| encode_field(e) }.join
|
50
|
-
["A", bytes.bytesize, bytes].pack("a L> a*")
|
51
|
-
when Hash
|
52
|
-
bytes = Table.encode(value)
|
53
|
-
["F", bytes.bytesize, bytes].pack("a L> a*")
|
54
|
-
when true
|
55
|
-
["t", 1].pack("a C")
|
56
|
-
when false
|
57
|
-
["t", 0].pack("a C")
|
58
|
-
when nil
|
59
|
-
["V"].pack("a")
|
60
|
-
else raise "unsupported table field type: #{value.class}"
|
61
70
|
end
|
62
|
-
end
|
63
71
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
pos
|
83
|
-
|
72
|
+
# Decodes a single value
|
73
|
+
# @return [Array<Integer, Object>] Bytes read and the parsed object
|
74
|
+
# @api private
|
75
|
+
def decode_field(bytes)
|
76
|
+
type = bytes[0]
|
77
|
+
pos = 1
|
78
|
+
case type
|
79
|
+
when "S"
|
80
|
+
len = bytes.byteslice(pos, 4).unpack1("L>")
|
81
|
+
pos += 4
|
82
|
+
[4 + len, bytes.byteslice(pos, len).force_encoding("utf-8")]
|
83
|
+
when "F"
|
84
|
+
len = bytes.byteslice(pos, 4).unpack1("L>")
|
85
|
+
pos += 4
|
86
|
+
[4 + len, decode(bytes.byteslice(pos, len))]
|
87
|
+
when "A"
|
88
|
+
len = bytes.byteslice(pos, 4).unpack1("L>")
|
89
|
+
a = []
|
90
|
+
while pos < len
|
91
|
+
length, value = decode_field(bytes.byteslice(pos, -1))
|
92
|
+
pos += length + 1
|
93
|
+
a << value
|
94
|
+
end
|
95
|
+
[4 + len, a]
|
96
|
+
when "t"
|
97
|
+
[1, bytes[pos].ord == 1]
|
98
|
+
when "b"
|
99
|
+
[1, bytes.byteslice(pos, 1).unpack1("c")]
|
100
|
+
when "B"
|
101
|
+
[1, bytes.byteslice(pos, 1).unpack1("C")]
|
102
|
+
when "s"
|
103
|
+
[2, bytes.byteslice(pos, 2).unpack1("s")]
|
104
|
+
when "u"
|
105
|
+
[2, bytes.byteslice(pos, 2).unpack1("S")]
|
106
|
+
when "I"
|
107
|
+
[4, bytes.byteslice(pos, 4).unpack1("l>")]
|
108
|
+
when "i"
|
109
|
+
[4, bytes.byteslice(pos, 4).unpack1("L>")]
|
110
|
+
when "l"
|
111
|
+
[8, bytes.byteslice(pos, 8).unpack1("q>")]
|
112
|
+
when "f"
|
113
|
+
[4, bytes.byteslice(pos, 4).unpack1("g")]
|
114
|
+
when "d"
|
115
|
+
[8, bytes.byteslice(pos, 8).unpack1("G")]
|
116
|
+
when "D"
|
117
|
+
scale = bytes[pos].ord
|
118
|
+
pos += 1
|
119
|
+
value = bytes.byteslice(pos, 4).unpack1("L>")
|
120
|
+
d = value / 10**scale
|
121
|
+
[5, d]
|
122
|
+
when "x"
|
123
|
+
len = bytes.byteslice(pos, 4).unpack1("L>")
|
124
|
+
[4 + len, bytes.byteslice(pos, len)]
|
125
|
+
when "T"
|
126
|
+
[8, Time.at(bytes.byteslice(pos, 8).unpack1("Q>"))]
|
127
|
+
when "V"
|
128
|
+
[0, nil]
|
129
|
+
else raise ArgumentError, "unsupported table field type: #{type}"
|
84
130
|
end
|
85
|
-
[4 + len, a]
|
86
|
-
when "t"
|
87
|
-
[1, bytes[pos].ord == 1]
|
88
|
-
when "b"
|
89
|
-
[1, bytes.byteslice(pos, 1).unpack1("c")]
|
90
|
-
when "B"
|
91
|
-
[1, bytes.byteslice(pos, 1).unpack1("C")]
|
92
|
-
when "s"
|
93
|
-
[2, bytes.byteslice(pos, 2).unpack1("s")]
|
94
|
-
when "u"
|
95
|
-
[2, bytes.byteslice(pos, 2).unpack1("S")]
|
96
|
-
when "I"
|
97
|
-
[4, bytes.byteslice(pos, 4).unpack1("l>")]
|
98
|
-
when "i"
|
99
|
-
[4, bytes.byteslice(pos, 4).unpack1("L>")]
|
100
|
-
when "l"
|
101
|
-
[8, bytes.byteslice(pos, 8).unpack1("q>")]
|
102
|
-
when "f"
|
103
|
-
[4, bytes.byteslice(pos, 4).unpack1("g")]
|
104
|
-
when "d"
|
105
|
-
[8, bytes.byteslice(pos, 8).unpack1("G")]
|
106
|
-
when "D"
|
107
|
-
scale = bytes[pos].ord
|
108
|
-
pos += 1
|
109
|
-
value = bytes.byteslice(pos, 4).unpack1("L>")
|
110
|
-
d = value / 10**scale
|
111
|
-
[5, d]
|
112
|
-
when "x"
|
113
|
-
len = bytes.byteslice(pos, 4).unpack1("L>")
|
114
|
-
[4 + len, bytes.byteslice(pos, len)]
|
115
|
-
when "T"
|
116
|
-
[8, Time.at(bytes.byteslice(pos, 8).unpack1("Q>"))]
|
117
|
-
when "V"
|
118
|
-
[0, nil]
|
119
|
-
else raise "unsupported table field type: #{type}"
|
120
131
|
end
|
121
132
|
end
|
122
133
|
end
|
data/lib/amqp/client/version.rb
CHANGED
data/lib/amqp/client.rb
CHANGED
@@ -3,10 +3,26 @@
|
|
3
3
|
require "set"
|
4
4
|
require_relative "client/version"
|
5
5
|
require_relative "client/connection"
|
6
|
+
require_relative "client/exchange"
|
7
|
+
require_relative "client/queue"
|
6
8
|
|
9
|
+
# AMQP 0-9-1 Protocol, this library only implements the Client
|
10
|
+
# @see Client
|
7
11
|
module AMQP
|
8
12
|
# AMQP 0-9-1 Client
|
13
|
+
# @see Connection
|
9
14
|
class Client
|
15
|
+
# Create a new Client object, this won't establish a connection yet, use {#connect} or {#start} for that
|
16
|
+
# @param uri [String] URL on the format amqp://username:password@hostname/vhost,
|
17
|
+
# use amqps:// for encrypted connection
|
18
|
+
# @option options [Boolean] connection_name (PROGRAM_NAME) Set a name for the connection to be able to identify
|
19
|
+
# the client from the broker
|
20
|
+
# @option options [Boolean] verify_peer (true) Verify broker's TLS certificate, set to false for self-signed certs
|
21
|
+
# @option options [Integer] heartbeat (0) Heartbeat timeout, defaults to 0 and relies on TCP keepalive instead
|
22
|
+
# @option options [Integer] frame_max (131_072) Maximum frame size,
|
23
|
+
# the smallest of the client's and the broker's values will be used
|
24
|
+
# @option options [Integer] channel_max (2048) Maxium number of channels the client will be allowed to have open.
|
25
|
+
# Maxium allowed is 65_536. The smallest of the client's and the broker's value will be used.
|
10
26
|
def initialize(uri, **options)
|
11
27
|
@uri = uri
|
12
28
|
@options = options
|
@@ -17,12 +33,17 @@ module AMQP
|
|
17
33
|
@connq = SizedQueue.new(1)
|
18
34
|
end
|
19
35
|
|
20
|
-
#
|
36
|
+
# @!group Connect and disconnect
|
37
|
+
|
38
|
+
# Establishes a new AMQP connection, does not try to reconnect
|
39
|
+
# @see Connection.connect
|
40
|
+
# @return [Connection]
|
21
41
|
def connect(read_loop_thread: true)
|
22
42
|
Connection.connect(@uri, read_loop_thread: read_loop_thread, **@options)
|
23
43
|
end
|
24
44
|
|
25
|
-
# Opens an AMQP connection using the high level API, will try to reconnect
|
45
|
+
# Opens an AMQP connection using the high level API, will try to reconnect if successfully connected at first
|
46
|
+
# @return [self]
|
26
47
|
def start
|
27
48
|
@stopped = false
|
28
49
|
Thread.new(connect(read_loop_thread: false)) do |conn|
|
@@ -42,7 +63,7 @@ module AMQP
|
|
42
63
|
@connq << conn
|
43
64
|
end
|
44
65
|
conn.read_loop # blocks until connection is closed, then reconnect
|
45
|
-
rescue
|
66
|
+
rescue Error => e
|
46
67
|
warn "AMQP-Client reconnect error: #{e.inspect}"
|
47
68
|
sleep @options[:reconnect_interval] || 1
|
48
69
|
ensure
|
@@ -52,76 +73,56 @@ module AMQP
|
|
52
73
|
self
|
53
74
|
end
|
54
75
|
|
76
|
+
# Close the currently open connection
|
77
|
+
# @return [nil]
|
55
78
|
def stop
|
79
|
+
return if @stopped
|
80
|
+
|
56
81
|
@stopped = true
|
57
82
|
conn = @connq.pop
|
58
83
|
conn.close
|
59
84
|
nil
|
60
85
|
end
|
61
86
|
|
62
|
-
|
87
|
+
# @!endgroup
|
88
|
+
# @!group High level objects
|
89
|
+
|
90
|
+
# Declare a queue
|
91
|
+
# @param name [String] Name of the queue
|
92
|
+
# @param durable [Boolean] If true the queue will survive broker restarts,
|
93
|
+
# messages in the queue will only survive if they are published as persistent
|
94
|
+
# @param auto_delete [Boolean] If true the queue will be deleted when the last consumer stops consuming
|
95
|
+
# (it won't be deleted until at least one consumer has consumed from it)
|
96
|
+
# @param arguments [Hash] Custom arguments, such as queue-ttl etc.
|
97
|
+
# @return [Queue]
|
98
|
+
def queue(name, durable: true, auto_delete: false, arguments: {})
|
63
99
|
raise ArgumentError, "Currently only supports named, durable queues" if name.empty?
|
64
100
|
|
65
101
|
@queues.fetch(name) do
|
66
102
|
with_connection do |conn|
|
67
|
-
conn.
|
68
|
-
ch.queue_declare(name, durable: durable, exclusive: exclusive, auto_delete: auto_delete, arguments: arguments)
|
69
|
-
end
|
103
|
+
conn.channel(1).queue_declare(name, durable: durable, auto_delete: auto_delete, arguments: arguments)
|
70
104
|
end
|
71
105
|
@queues[name] = Queue.new(self, name)
|
72
106
|
end
|
73
107
|
end
|
74
108
|
|
109
|
+
# Declare an exchange and return a high level Exchange object
|
110
|
+
# @return [Exchange]
|
75
111
|
def exchange(name, type, durable: true, auto_delete: false, internal: false, arguments: {})
|
76
112
|
@exchanges.fetch(name) do
|
77
113
|
with_connection do |conn|
|
78
|
-
conn.
|
79
|
-
|
80
|
-
end
|
114
|
+
conn.channel(1).exchange_declare(name, type, durable: durable, auto_delete: auto_delete,
|
115
|
+
internal: internal, arguments: arguments)
|
81
116
|
end
|
82
117
|
@exchanges[name] = Exchange.new(self, name)
|
83
118
|
end
|
84
119
|
end
|
85
120
|
|
86
|
-
#
|
87
|
-
|
88
|
-
def initialize(client, name)
|
89
|
-
@client = client
|
90
|
-
@name = name
|
91
|
-
end
|
92
|
-
|
93
|
-
def publish(body, routing_key, arguments: {})
|
94
|
-
@client.publish(body, @name, routing_key, arguments: arguments)
|
95
|
-
end
|
96
|
-
|
97
|
-
# Bind to another exchange
|
98
|
-
def bind(exchange, routing_key, arguments: {})
|
99
|
-
@client.exchange_bind(@name, exchange, routing_key, arguments: arguments)
|
100
|
-
end
|
101
|
-
|
102
|
-
# Unbind from another exchange
|
103
|
-
def unbind(exchange, routing_key, arguments: {})
|
104
|
-
@client.exchange_unbind(@name, exchange, routing_key, arguments: arguments)
|
105
|
-
end
|
106
|
-
|
107
|
-
def delete
|
108
|
-
@client.delete_exchange(@name)
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
def subscribe(queue_name, no_ack: false, prefetch: 1, worker_threads: 1, arguments: {}, &blk)
|
113
|
-
@subscriptions.add? [queue_name, no_ack, prefetch, worker_threads, arguments, blk]
|
114
|
-
|
115
|
-
with_connection do |conn|
|
116
|
-
ch = conn.channel
|
117
|
-
ch.basic_qos(prefetch)
|
118
|
-
ch.basic_consume(queue_name, no_ack: no_ack, worker_threads: worker_threads, arguments: arguments) do |msg|
|
119
|
-
blk.call(msg)
|
120
|
-
end
|
121
|
-
end
|
122
|
-
end
|
121
|
+
# @!endgroup
|
122
|
+
# @!group Publish
|
123
123
|
|
124
124
|
# Publish a (persistent) message and wait for confirmation
|
125
|
+
# @return [nil]
|
125
126
|
def publish(body, exchange, routing_key, **properties)
|
126
127
|
with_connection do |conn|
|
127
128
|
properties = { delivery_mode: 2 }.merge!(properties)
|
@@ -130,6 +131,7 @@ module AMQP
|
|
130
131
|
end
|
131
132
|
|
132
133
|
# Publish a (persistent) message but don't wait for a confirmation
|
134
|
+
# @return [nil]
|
133
135
|
def publish_and_forget(body, exchange, routing_key, **properties)
|
134
136
|
with_connection do |conn|
|
135
137
|
properties = { delivery_mode: 2 }.merge!(properties)
|
@@ -137,94 +139,125 @@ module AMQP
|
|
137
139
|
end
|
138
140
|
end
|
139
141
|
|
142
|
+
# Wait for unconfirmed publishes
|
143
|
+
# @return [Boolean] True if successful, false if any message negatively acknowledged
|
140
144
|
def wait_for_confirms
|
141
145
|
with_connection do |conn|
|
142
146
|
conn.channel(1).wait_for_confirms
|
143
147
|
end
|
144
148
|
end
|
145
149
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
150
|
+
# @!endgroup
|
151
|
+
# @!group Queue actions
|
152
|
+
|
153
|
+
# Consume messages from a queue
|
154
|
+
# @param queue [String] Name of the queue to subscribe to
|
155
|
+
# @param no_ack [Boolean] When false messages have to be manually acknowledged (or rejected)
|
156
|
+
# @param prefetch [Integer] Specify how many messages to prefetch for consumers with no_ack is false
|
157
|
+
# @param worker_threads [Integer] Number of threads processing messages,
|
158
|
+
# 0 means that the thread calling this method will be blocked
|
159
|
+
# @param arguments [Hash] Custom arguments to the consumer
|
160
|
+
# @yield [Message] Delivered message from the queue
|
161
|
+
# @return [Array<(String, Array<Thread>)>] Returns consumer_tag and an array of worker threads
|
162
|
+
# @return [nil] When `worker_threads` is 0 the method will return when the consumer is cancelled
|
163
|
+
def subscribe(queue, no_ack: false, prefetch: 1, worker_threads: 1, arguments: {}, &blk)
|
164
|
+
@subscriptions.add? [queue, no_ack, prefetch, worker_threads, arguments, blk]
|
151
165
|
|
152
|
-
def unbind(queue, exchange, routing_key, arguments: {})
|
153
166
|
with_connection do |conn|
|
154
|
-
conn.channel
|
167
|
+
ch = conn.channel
|
168
|
+
ch.basic_qos(prefetch)
|
169
|
+
ch.basic_consume(queue, no_ack: no_ack, worker_threads: worker_threads, arguments: arguments) do |msg|
|
170
|
+
blk.call(msg)
|
171
|
+
end
|
155
172
|
end
|
156
173
|
end
|
157
174
|
|
158
|
-
|
175
|
+
# Bind a queue to an exchange
|
176
|
+
# @param queue [String] Name of the queue to bind
|
177
|
+
# @param exchange [String] Name of the exchange to bind to
|
178
|
+
# @param binding_key [String] Binding key on which messages that match might be routed (depending on exchange type)
|
179
|
+
# @param arguments [Hash] Message headers to match on (only relevant for header exchanges)
|
180
|
+
# @return [nil]
|
181
|
+
def bind(queue, exchange, binding_key, arguments: {})
|
159
182
|
with_connection do |conn|
|
160
|
-
conn.channel(1).
|
183
|
+
conn.channel(1).queue_bind(queue, exchange, binding_key, arguments: arguments)
|
161
184
|
end
|
162
185
|
end
|
163
186
|
|
164
|
-
|
187
|
+
# Unbind a queue from an exchange
|
188
|
+
# @param queue [String] Name of the queue to unbind
|
189
|
+
# @param exchange [String] Name of the exchange to unbind from
|
190
|
+
# @param binding_key [String] Binding key which the queue is bound to the exchange with
|
191
|
+
# @param arguments [Hash] Arguments matching the binding that's being removed
|
192
|
+
# @return [nil]
|
193
|
+
def unbind(queue, exchange, binding_key, arguments: {})
|
165
194
|
with_connection do |conn|
|
166
|
-
conn.channel(1).
|
195
|
+
conn.channel(1).queue_unbind(queue, exchange, binding_key, arguments: arguments)
|
167
196
|
end
|
168
197
|
end
|
169
198
|
|
199
|
+
# Purge a queue
|
200
|
+
# @param queue [String] Name of the queue
|
201
|
+
# @return [nil]
|
170
202
|
def purge(queue)
|
171
203
|
with_connection do |conn|
|
172
204
|
conn.channel(1).queue_purge(queue)
|
173
205
|
end
|
174
206
|
end
|
175
207
|
|
176
|
-
|
208
|
+
# Delete a queue
|
209
|
+
# @param name [String] Name of the queue
|
210
|
+
# @param if_unused [Boolean] Only delete if the queue doesn't have consumers, raises a ChannelClosed error otherwise
|
211
|
+
# @param if_empty [Boolean] Only delete if the queue is empty, raises a ChannelClosed error otherwise
|
212
|
+
# @return [Integer] Number of messages in the queue when deleted
|
213
|
+
def delete_queue(name, if_unused: false, if_empty: false)
|
177
214
|
with_connection do |conn|
|
178
|
-
conn.channel(1).queue_delete(name)
|
215
|
+
msgs = conn.channel(1).queue_delete(name, if_unused: if_unused, if_empty: if_empty)
|
179
216
|
@queues.delete(name)
|
217
|
+
msgs
|
180
218
|
end
|
181
219
|
end
|
182
220
|
|
183
|
-
|
221
|
+
# @!endgroup
|
222
|
+
# @!group Exchange actions
|
223
|
+
|
224
|
+
# Bind an exchange to an exchange
|
225
|
+
# @param destination [String] Name of the exchange to bind
|
226
|
+
# @param source [String] Name of the exchange to bind to
|
227
|
+
# @param binding_key [String] Binding key on which messages that match might be routed (depending on exchange type)
|
228
|
+
# @param arguments [Hash] Message headers to match on (only relevant for header exchanges)
|
229
|
+
# @return [nil]
|
230
|
+
def exchange_bind(destination, source, binding_key, arguments: {})
|
184
231
|
with_connection do |conn|
|
185
|
-
conn.channel(1).
|
186
|
-
@exchanges.delete(name)
|
232
|
+
conn.channel(1).exchange_bind(destination, source, binding_key, arguments: arguments)
|
187
233
|
end
|
188
234
|
end
|
189
235
|
|
190
|
-
#
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
self
|
200
|
-
end
|
201
|
-
|
202
|
-
def subscribe(no_ack: false, prefetch: 1, worker_threads: 1, arguments: {}, &blk)
|
203
|
-
@client.subscribe(@name, no_ack: no_ack, prefetch: prefetch, worker_threads: worker_threads, arguments: arguments, &blk)
|
204
|
-
self
|
205
|
-
end
|
206
|
-
|
207
|
-
def bind(exchange, routing_key, **headers)
|
208
|
-
@client.bind(@name, exchange, routing_key, **headers)
|
209
|
-
self
|
210
|
-
end
|
211
|
-
|
212
|
-
def unbind(exchange, routing_key, **headers)
|
213
|
-
@client.unbind(@name, exchange, routing_key, **headers)
|
214
|
-
self
|
215
|
-
end
|
216
|
-
|
217
|
-
def purge
|
218
|
-
@client.purge(@name)
|
219
|
-
self
|
236
|
+
# Unbind an exchange from an exchange
|
237
|
+
# @param destination [String] Name of the exchange to unbind
|
238
|
+
# @param source [String] Name of the exchange to unbind from
|
239
|
+
# @param binding_key [String] Binding key which the exchange is bound to the exchange with
|
240
|
+
# @param arguments [Hash] Arguments matching the binding that's being removed
|
241
|
+
# @return [nil]
|
242
|
+
def exchange_unbind(destination, source, binding_key, arguments: {})
|
243
|
+
with_connection do |conn|
|
244
|
+
conn.channel(1).exchange_unbind(destination, source, binding_key, arguments: arguments)
|
220
245
|
end
|
246
|
+
end
|
221
247
|
|
222
|
-
|
223
|
-
|
248
|
+
# Delete an exchange
|
249
|
+
# @param name [String] Name of the exchange
|
250
|
+
# @return [nil]
|
251
|
+
def delete_exchange(name)
|
252
|
+
with_connection do |conn|
|
253
|
+
conn.channel(1).exchange_delete(name)
|
254
|
+
@exchanges.delete(name)
|
224
255
|
nil
|
225
256
|
end
|
226
257
|
end
|
227
258
|
|
259
|
+
# @!endgroup
|
260
|
+
|
228
261
|
private
|
229
262
|
|
230
263
|
def with_connection
|