rabbitmq-actors 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +101 -0
- data/.rspec +5 -0
- data/.travis.yml +12 -0
- data/Gemfile +4 -0
- data/README.md +602 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/rabbitmq/actors.rb +13 -0
- data/lib/rabbitmq/actors/base/agent.rb +108 -0
- data/lib/rabbitmq/actors/base/consumer.rb +105 -0
- data/lib/rabbitmq/actors/base/producer.rb +83 -0
- data/lib/rabbitmq/actors/patterns.rb +10 -0
- data/lib/rabbitmq/actors/patterns/headers/headers_consumer.rb +106 -0
- data/lib/rabbitmq/actors/patterns/headers/headers_producer.rb +64 -0
- data/lib/rabbitmq/actors/patterns/master_workers/master_producer.rb +61 -0
- data/lib/rabbitmq/actors/patterns/master_workers/worker.rb +63 -0
- data/lib/rabbitmq/actors/patterns/publish_subscribe/publisher.rb +72 -0
- data/lib/rabbitmq/actors/patterns/publish_subscribe/subscriber.rb +70 -0
- data/lib/rabbitmq/actors/patterns/routing/routing_consumer.rb +99 -0
- data/lib/rabbitmq/actors/patterns/routing/routing_producer.rb +75 -0
- data/lib/rabbitmq/actors/patterns/topics/topic_consumer.rb +105 -0
- data/lib/rabbitmq/actors/patterns/topics/topic_producer.rb +64 -0
- data/lib/rabbitmq/actors/testing.rb +8 -0
- data/lib/rabbitmq/actors/testing/rspec.rb +8 -0
- data/lib/rabbitmq/actors/testing/rspec/stub.rb +52 -0
- data/lib/rabbitmq/actors/version.rb +5 -0
- data/lib/rabbitmq/server.rb +18 -0
- data/rabbitmq-actors.gemspec +32 -0
- metadata +185 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: cffb9d414d6249d8d1da35687662d07d67764a8b
|
4
|
+
data.tar.gz: bcf47adfc06a9f41585c265dc3e1e99365ade4c9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3e206d7af8c02776499fb664234588fb0829afc9b1a1b08ce7c8319c8aff2a77f893978cc7ae97b30ac5a86eaf9bde87bed834df5d96598ee8f31a39593b94bf
|
7
|
+
data.tar.gz: 1b14cf154fd035b3c463af0bafa702b495b0b81c0f420d0153fd633ae8112e451e6a20bd424446fa11f2ee86ab8de4d4b45e6d73e03e0496845f4a8495c1eaee
|
data/.gitignore
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
2
|
+
#
|
3
|
+
# If you find yourself ignoring temporary files generated by your text editor
|
4
|
+
# or operating system, you probably want to add a global ignore instead:
|
5
|
+
# git config --global core.excludesfile '~/.gitignore_global'
|
6
|
+
|
7
|
+
# Ignore bundler config.
|
8
|
+
/.bundle
|
9
|
+
|
10
|
+
# Gems
|
11
|
+
/Gemfile.lock
|
12
|
+
|
13
|
+
# Ignore the default SQLite database.
|
14
|
+
/db/*.sqlite3
|
15
|
+
/db/*.sqlite3-journal
|
16
|
+
|
17
|
+
# Ignore gem building folder: pkg
|
18
|
+
pkg
|
19
|
+
|
20
|
+
# Ignore log and tmp files
|
21
|
+
log/*
|
22
|
+
*.log
|
23
|
+
tmp
|
24
|
+
tmp/**/*
|
25
|
+
|
26
|
+
# Documentation
|
27
|
+
/doc/
|
28
|
+
doc/api
|
29
|
+
doc/app
|
30
|
+
.yardoc
|
31
|
+
/_yardoc/
|
32
|
+
.yardopts
|
33
|
+
|
34
|
+
# Public Uploads
|
35
|
+
public/system/*
|
36
|
+
public/themes/*
|
37
|
+
|
38
|
+
# Public Cache
|
39
|
+
public/javascripts/cache
|
40
|
+
public/stylesheets/cache
|
41
|
+
|
42
|
+
# Vendor Cache
|
43
|
+
vendor/cache
|
44
|
+
|
45
|
+
# Acts as Indexed
|
46
|
+
index/**/*
|
47
|
+
|
48
|
+
# Refinery Specific
|
49
|
+
*.tmproj
|
50
|
+
*.autobackupbyrefinery.*
|
51
|
+
refinerycms-*.gem
|
52
|
+
.autotest
|
53
|
+
|
54
|
+
# Mac
|
55
|
+
.DS_Store
|
56
|
+
|
57
|
+
# Windows
|
58
|
+
Thumbs.db
|
59
|
+
|
60
|
+
# NetBeans
|
61
|
+
nbproject
|
62
|
+
|
63
|
+
# Eclipse
|
64
|
+
.project
|
65
|
+
|
66
|
+
# Redcar
|
67
|
+
.redcar
|
68
|
+
|
69
|
+
# Rubinius
|
70
|
+
*.rbc
|
71
|
+
|
72
|
+
# RVM / rbenv
|
73
|
+
.ruby-version
|
74
|
+
.ruby-gemset
|
75
|
+
.rvmrc
|
76
|
+
|
77
|
+
# Vim
|
78
|
+
*.swp
|
79
|
+
*.swo
|
80
|
+
|
81
|
+
# RubyMine
|
82
|
+
.idea
|
83
|
+
|
84
|
+
# E-texteditor
|
85
|
+
.eprj
|
86
|
+
|
87
|
+
# Backup
|
88
|
+
*~
|
89
|
+
|
90
|
+
# Capybara Bug
|
91
|
+
capybara-*html
|
92
|
+
|
93
|
+
# sass
|
94
|
+
.sass-cache
|
95
|
+
.sass-cache/*
|
96
|
+
|
97
|
+
# SimpleCov (tests code converage)
|
98
|
+
/coverage
|
99
|
+
|
100
|
+
# RSpec
|
101
|
+
/spec/reports/
|
data/.rspec
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
sudo: false
|
2
|
+
language: ruby
|
3
|
+
rvm:
|
4
|
+
- 2.3.1
|
5
|
+
- 2.4.0
|
6
|
+
branches:
|
7
|
+
only:
|
8
|
+
- master
|
9
|
+
script: bundle exec rspec spec
|
10
|
+
notifications:
|
11
|
+
slack:
|
12
|
+
secure: HzwhTguhPlUK35NGFFrdMCDW0FFUOej4Ffl8MNKU6+lUH7dsac63wlGdxXh0MWC68QXpPgKys///bvmutDII6wbsqHoNijMQAPm+9ITg85m1/XQIbWu/O0bl31Mw4QH9c+sTUVSMW6HBg9L5mrRJi6I3I937iCbV7KRvgH8qrmYD43OM3xGircQv0LNbZl1D1mpfoAIhZmLh1G5eztxhbKT7EwDubxiJoz/AosQRY9YmwJ19X6huuq69xQB+74+Gs3EX1o7V0+3vkIqLzVOL2QoX4dCvMfOakim+FYKaytj+IIvC361ddLJQmHq+EBuF791bEJFk2FIQrPCYBRQiRJcAQMOnGBokCrWV/01NQyjMZCWuMpMTbMBMvA4by7cjok3fPw6kHmCl4vDEL+sqKa7/6BXEu140xfqRHcthvteqglimplXqflffiH9fT7IhyueJpfDVyo7BhCAGevMSazP9SJ1yeO30iPjQHC8TUf72uuRdzWid7JVV4QIYSiCs+dQSuW41I1eqmk6zoceOijPma77vPF91Ucr+rvlTCAG8ipjv1l2K96gS7nvFEus2GVc/GpukXkGS91MFNPNddCljK3YY0s4vke4TcV5l+/Y9hCbYOGjXxhxyimEBkoop3ar19gSvHjrbaPMR8evwl6nifCKu8IYU19iqPaSbYsI=
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,602 @@
|
|
1
|
+
# RabbitMQ::Actors
|
2
|
+
|
3
|
+
rabbitmq-actors is a simple and direct way to use RabbitMQ in your Ruby applications.
|
4
|
+
It uses the excellent [bunny](http://rubybunny.info/) Ruby client to RabbitMQ and basically, it provides a set of Ruby classes
|
5
|
+
implementing the most common producer-consumer patterns for multiple applications to exchange messages
|
6
|
+
using a RabbitMQ message broker:
|
7
|
+
- _master/worker_
|
8
|
+
- _publisher/subscriber_
|
9
|
+
- _routing producers/consumers_
|
10
|
+
- _topic producers/consumers_ and
|
11
|
+
- _headers producers/consumers_
|
12
|
+
|
13
|
+
|
14
|
+
## Installation
|
15
|
+
|
16
|
+
If you don't have RabbitMQ installed, you can do it following the instructions in [RabbitMQ website](https://www.rabbitmq.com/download.html).
|
17
|
+
On Mac OSX, the fastest way is via [Homebrew](https://brew.sh/):
|
18
|
+
```
|
19
|
+
$ brew install rabbitmq
|
20
|
+
$ rabbitmq-server
|
21
|
+
```
|
22
|
+
Make sure, `/usr/local/sbin` is in your `$PATH`
|
23
|
+
|
24
|
+
Once rabbitmq is installed locally or remotely accessible, add this line to your application's Gemfile:
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
gem 'rabbitmq-actors'
|
28
|
+
```
|
29
|
+
|
30
|
+
and execute:
|
31
|
+
|
32
|
+
$ bundle
|
33
|
+
|
34
|
+
or install it yourself as:
|
35
|
+
|
36
|
+
$ gem install rabbitmq-actors
|
37
|
+
|
38
|
+
## Usage
|
39
|
+
|
40
|
+
To use rabbitmq-actors in your application, the first thing you need to do is set the url where the RabbitMQ messaging broker is running:
|
41
|
+
|
42
|
+
```
|
43
|
+
RabbitMQ::Server.url = 'amqp://localhost' # talk to local RabbitMQ server
|
44
|
+
```
|
45
|
+
|
46
|
+
### Patterns
|
47
|
+
There are different ways you can use RabbitMQ depending on the domain of your application.
|
48
|
+
RabbitMQ allows you to implement several message exchange patterns between producers and consumers.
|
49
|
+
Use the one/s that fit most the needs of your application:
|
50
|
+
|
51
|
+
### Master - Worker pattern
|
52
|
+
Under this strategy, one or several programs (masters) produce and publish messages to a known work queue.
|
53
|
+
On the other hand, consumer programs (workers) bind to that queue so that RabbitMQ distributes all the received
|
54
|
+
messages among the workers in a round robin manner. Therefore, every message is routed to only one of the workers.
|
55
|
+
|
56
|
+
**Creating a master producer**
|
57
|
+
|
58
|
+
This gem provides the `RabbitMQ::Actors::MasterProducer` class to help you implement master producers in your
|
59
|
+
application:
|
60
|
+
|
61
|
+
```
|
62
|
+
master = RabbitMQ::Actors::MasterProducer.new(
|
63
|
+
queue_name: 'transactions',
|
64
|
+
auto_delete: false,
|
65
|
+
reply_queue_name: 'confirmations',
|
66
|
+
logger: Rails.logger)
|
67
|
+
```
|
68
|
+
In the example above, `master` is a new MasterProducer instance routing messages to the `"purchases"`
|
69
|
+
queue (which won't be automatically deleted by RabbitMQ when there are no consumers listening to it: `auto_delete: false`).
|
70
|
+
|
71
|
+
The `reply_queue_name` param will add a `reply_to: "confirmations"` property to every message published by
|
72
|
+
this master instance so that the consumer receiving the message knows the queue where it has to publish
|
73
|
+
any response message.
|
74
|
+
|
75
|
+
The activity happening inside `master` (when publishing messages, closing connections...) will be logged to
|
76
|
+
`Rails.logger` (STDOUT by default) with `:info` severity level.
|
77
|
+
|
78
|
+
|
79
|
+
**Publishing messages**
|
80
|
+
|
81
|
+
To publish a message using our fresh new `master` instance, we just call `publish` instance method with some
|
82
|
+
mandatory arguments (message and :message_id):
|
83
|
+
|
84
|
+
```
|
85
|
+
message = { stock: 'Apple', number: 1000 }.to_json
|
86
|
+
master.publish(message, message_id: '1234837325', content_type: "application/json")
|
87
|
+
```
|
88
|
+
|
89
|
+
- `message` is a string containing the body of the message to be published to RabbitMQ
|
90
|
+
- `:message_id` is a keyword argument and contains any id our application uses to identify the message.
|
91
|
+
|
92
|
+
There are lots of optional params you can add to publish a message (see the
|
93
|
+
MasterProducer code documentation and Bunny::Exchange#publish code documentation):
|
94
|
+
|
95
|
+
- `persistent:` Should the message be persisted to disk?. Default true.
|
96
|
+
- `mandatory:` Should the message be returned if it cannot be routed to any queue?
|
97
|
+
- `timestamp:` A timestamp associated with this message
|
98
|
+
- `expiration:` Expiration time after which the message will be deleted
|
99
|
+
- `type:` Message type, e.g. what type of event or command this message represents. Can be any string
|
100
|
+
- `reply_to:` Queue name other apps should send the response to. Default to `:reply_queue_name if set at creation time.
|
101
|
+
- `content_type:` Message content type (e.g. application/json)
|
102
|
+
- `content_encoding:` Message content encoding (e.g. gzip)
|
103
|
+
- `correlation_id:` Message correlated to this one, e.g. what request this message is a reply for
|
104
|
+
- `priority:` Message priority, 0 to 9. Not used by RabbitMQ, only applications
|
105
|
+
- `user_id:` Optional user ID. Verified by RabbitMQ against the actual connection username
|
106
|
+
- `app_id:` Optional application ID
|
107
|
+
|
108
|
+
|
109
|
+
**Closing the channel**
|
110
|
+
|
111
|
+
Finally, close the channel when no more messages are going to be published by the master instance:
|
112
|
+
```
|
113
|
+
master.close
|
114
|
+
```
|
115
|
+
or using a chained method call after publishing the last message:
|
116
|
+
```
|
117
|
+
master.publish(message, message_id: '1234837325', content_type: "application/json").and_close
|
118
|
+
```
|
119
|
+
|
120
|
+
Actors (consumers and producers of all types) can share a connection to RabbitMQ broker
|
121
|
+
but each one connects via its own private channel. Although RabbitMQ can have thousands
|
122
|
+
of channels open simulataneously, it is a good practice to close it when an actor is not
|
123
|
+
being used anymore.
|
124
|
+
|
125
|
+
**Defining a worker consumer**
|
126
|
+
|
127
|
+
To define worker consumers of master produced messages, subclass `RabbitMQ::Actors::Worker` class and
|
128
|
+
define a private `perform` instance method to process a received message like this:
|
129
|
+
|
130
|
+
```
|
131
|
+
class MyListener < RabbitMQ::Actors::Worker
|
132
|
+
def initialize
|
133
|
+
super(queue_name: 'transactions',
|
134
|
+
manual_ack: true
|
135
|
+
logger: Rails.logger,
|
136
|
+
on_cancellation: ->{ ActiveRecord::Base.connection.close }
|
137
|
+
end
|
138
|
+
|
139
|
+
private
|
140
|
+
|
141
|
+
def perform(**task)
|
142
|
+
message, message_id = JSON.parse(task[:body]), task[:properties][:message_id]
|
143
|
+
...
|
144
|
+
# your code to process the message
|
145
|
+
end
|
146
|
+
end
|
147
|
+
```
|
148
|
+
|
149
|
+
`RabbitMQ::Actors::Worker` class requires a mandatory keyword argument to initialize instances:
|
150
|
+
|
151
|
+
- `queue_name:` string containing the name of the durable queue where to receive messages from.
|
152
|
+
|
153
|
+
Other optional params you can add to initialize a worker (see the Worker code documentation):
|
154
|
+
|
155
|
+
- `manual_ack:` tells RabbitMQ to wait for a manual acknowledge from the worker before
|
156
|
+
marking a message as "processed" and remove it from the queue. If true, the acknowledgement will be
|
157
|
+
automatically sent by the worker after returning from `perform` method. Defaults to false.
|
158
|
+
- `logger:` where to log worker activity with :info severity. Defaults to STDOUT if none provided.
|
159
|
+
- `on_cancellation`: a Proc/Lambda object to be called right before the worker is terminated.
|
160
|
+
|
161
|
+
|
162
|
+
**Running a worker consumer**
|
163
|
+
|
164
|
+
Call #start! method on a worker instance to start listening and processing messages from the associated queue.
|
165
|
+
|
166
|
+
```
|
167
|
+
MyListener.new.start!
|
168
|
+
```
|
169
|
+
|
170
|
+
Press ^c on a console to stop a worker or send a process termination signal.
|
171
|
+
|
172
|
+
|
173
|
+
### Publish - Subscribe pattern
|
174
|
+
The idea behind this strategy is to broadcast messages to all the subscribed consumers (subscribers)
|
175
|
+
as opposed to only one consumer as it was the case in the Master - Worker pattern.
|
176
|
+
|
177
|
+
**Creating a publisher**
|
178
|
+
|
179
|
+
Instantiate the class `RabbitMQ::Actors::Publisher` to create publishers of messages under this scheme:
|
180
|
+
|
181
|
+
```
|
182
|
+
publisher = RabbitMQ::Actors::Publisher.new(exchange_name: 'sports', logger: Rails.logger)
|
183
|
+
```
|
184
|
+
|
185
|
+
`exchange_name:` param is mandatory and contains the name of the RabbitMQ exchange where to publis the
|
186
|
+
messages.
|
187
|
+
|
188
|
+
As we know from the master producer, the activity happening inside `publisher` can be sent to a logger
|
189
|
+
instance (`Rails.logger` in the example) with `:info` severity level.
|
190
|
+
|
191
|
+
Again, like master producers, `RabbitMQ::Actors::Publisher` you can pass a `reply_queue_name:` keyword
|
192
|
+
param to create a new instance.
|
193
|
+
|
194
|
+
|
195
|
+
**Publishing messages**
|
196
|
+
|
197
|
+
The way for a publisher to publish messages is identical to that of master producers. See documentation
|
198
|
+
above.
|
199
|
+
|
200
|
+
```
|
201
|
+
publisher.publish(message, message_id: '1232357390', content_type: "application/json")
|
202
|
+
```
|
203
|
+
|
204
|
+
**Closing the channel**
|
205
|
+
|
206
|
+
```
|
207
|
+
publisher.close
|
208
|
+
```
|
209
|
+
or using the chained way:
|
210
|
+
```
|
211
|
+
publisher.publish(message, message_id: '1234837390', content_type: "application/json").and_close
|
212
|
+
```
|
213
|
+
|
214
|
+
|
215
|
+
**Defining a subscriber**
|
216
|
+
|
217
|
+
To define your subscriber class, make it a subclass of `RabbitMQ::Actors::Subscriber` class and
|
218
|
+
define a private `perform` instance method to process a received message like this:
|
219
|
+
|
220
|
+
```
|
221
|
+
class ScoresListener < RabbitMQ::Actors::Subscriber
|
222
|
+
def initialize
|
223
|
+
super(exchange_name: 'scores',
|
224
|
+
logger: Rails.logger,
|
225
|
+
on_cancellation: ->{ ActiveRecord::Base.connection.close })
|
226
|
+
end
|
227
|
+
|
228
|
+
private
|
229
|
+
|
230
|
+
def perform(**task)
|
231
|
+
match_data = JSON.parse(task[:body])
|
232
|
+
process_match(match_data)
|
233
|
+
end
|
234
|
+
...
|
235
|
+
end
|
236
|
+
```
|
237
|
+
|
238
|
+
`RabbitMQ::Actors::Publiser` class requires the following mandatory keyword argument:
|
239
|
+
|
240
|
+
- `exchange_name:` name of the exchange where to consume messages from.
|
241
|
+
|
242
|
+
You can also add `logger:` and `on_cancellation:` keyword params (see worker documentation above)
|
243
|
+
|
244
|
+
|
245
|
+
**Running a subscriber**
|
246
|
+
|
247
|
+
Like every consumer actor, call #start! method on an instance to start listening and processing messages.
|
248
|
+
|
249
|
+
```
|
250
|
+
ScoresListener.new.start!
|
251
|
+
```
|
252
|
+
|
253
|
+
Press ^c on a console or send a process termination signal to stop it.
|
254
|
+
|
255
|
+
|
256
|
+
### Routing pattern
|
257
|
+
The routing pattern is similar to _publish/subscribe_ strategy but messages are not routed to all consumers but
|
258
|
+
only to those bound to the value a property of the message named `routing_key`.
|
259
|
+
Every consumer bound to the exchange states those `routing_key` values it is interested in. When a message
|
260
|
+
arrives it comes with a certain `routing_key` value, so the exchange routes it to only those consumers
|
261
|
+
interested in that particular value.
|
262
|
+
|
263
|
+
**Creating a routing producer**
|
264
|
+
|
265
|
+
Instantiate the class `RabbitMQ::Actors::RoutingProducer` to create publishers of messages under this scheme:
|
266
|
+
|
267
|
+
```
|
268
|
+
routing_producer = RabbitMQ::Actors::RoutingProducer.new(
|
269
|
+
exchange_name: 'sports',
|
270
|
+
replay_queue_name: 'scores',
|
271
|
+
logger: Rails.logger)
|
272
|
+
```
|
273
|
+
The description of the 3 params is similar to that of the previous producer classes. See above.
|
274
|
+
|
275
|
+
**Publishing messages**
|
276
|
+
|
277
|
+
```
|
278
|
+
message = {
|
279
|
+
championship: 'Wimbledon',
|
280
|
+
match: {
|
281
|
+
player_1: 'Rafa Nadal',
|
282
|
+
player_2: 'Roger Federed',
|
283
|
+
date: '01-Jul-2016'
|
284
|
+
} }.to_json
|
285
|
+
|
286
|
+
routing_producer.publish(message, message_id: '1234837633', content_type: "application/json", routing_key: 'tennis')
|
287
|
+
```
|
288
|
+
The way to publish messages is similar to that of the rest of producers. Note the mandatory param `routing_key:`
|
289
|
+
|
290
|
+
- `routing_key:` send the message only to queues bound to this string value.
|
291
|
+
|
292
|
+
**Closing the channel**
|
293
|
+
|
294
|
+
```
|
295
|
+
routing_producer.close
|
296
|
+
```
|
297
|
+
or using the chained way:
|
298
|
+
```
|
299
|
+
routing_producer.publish(message, message_id: '1234837633', content_type: "application/json", routing_key: 'tennis').and_close
|
300
|
+
```
|
301
|
+
|
302
|
+
|
303
|
+
**Defining a routing consumer**
|
304
|
+
|
305
|
+
Use the class `RabbitMQ::Actors::RoutingConsumer` in a similar way as to the rest of consumer types:
|
306
|
+
|
307
|
+
```
|
308
|
+
class TennisListener < RabbitMQ::Actors::RoutingConsumer
|
309
|
+
def initialize
|
310
|
+
super(exchange_name: 'sports',
|
311
|
+
binding_keys: ['tennis'],
|
312
|
+
logger: Rails.logger,
|
313
|
+
on_cancellation: ->{ ActiveRecord::Base.connection.close })
|
314
|
+
end
|
315
|
+
|
316
|
+
private
|
317
|
+
|
318
|
+
def perform(**task)
|
319
|
+
match_data = JSON.parse(task[:body])
|
320
|
+
process_tennis_match(match_data)
|
321
|
+
end
|
322
|
+
...
|
323
|
+
end
|
324
|
+
|
325
|
+
class FootballListener < RabbitMQ::Actors::RoutingConsumer
|
326
|
+
def initialize
|
327
|
+
super(exchange_name: 'sports',
|
328
|
+
binding_keys: ['football', 'soccer'],
|
329
|
+
logger: Rails.logger,
|
330
|
+
on_cancellation: ->{ ActiveRecord::Base.connection.close })
|
331
|
+
end
|
332
|
+
|
333
|
+
private
|
334
|
+
|
335
|
+
def perform(**task)
|
336
|
+
match_data = JSON.parse(task[:body])
|
337
|
+
process_footbal_match(match_data)
|
338
|
+
end
|
339
|
+
...
|
340
|
+
end
|
341
|
+
```
|
342
|
+
|
343
|
+
`RabbitMQ::Actors::RoutingConsumer` class requires the following mandatory keyword arguments:
|
344
|
+
|
345
|
+
- `exchange_name:` name of the exchange where to consume messages from.
|
346
|
+
- `binding_keys:` a string or list of strings with the routing key values this consumer is interested in.
|
347
|
+
|
348
|
+
You can also add `logger:` and `on_cancellation:` keyword params (see worker documentation above)
|
349
|
+
|
350
|
+
|
351
|
+
**Running a routing consumer**
|
352
|
+
|
353
|
+
Like every consumer actor, call #start! method on an instance to start listening and processing messages.
|
354
|
+
|
355
|
+
```
|
356
|
+
TennisListener.new.start!
|
357
|
+
FootballListener.new.start!
|
358
|
+
```
|
359
|
+
|
360
|
+
Press ^c on a console or send a process termination signal to stop it.
|
361
|
+
|
362
|
+
|
363
|
+
### Topics pattern
|
364
|
+
The topics pattern is very similar to the routing one. However routing keys are not free string values. Instead,
|
365
|
+
every routing key is a dot separated list of words.
|
366
|
+
|
367
|
+
Binding keys can use special chars to match one or several words:
|
368
|
+
|
369
|
+
`* can substitute for exactly one word`
|
370
|
+
`# can substitute for zero or more words`
|
371
|
+
|
372
|
+
**Creating a topic producer**
|
373
|
+
|
374
|
+
Instantiate the class `RabbitMQ::Actors::TopicProducer` to create publishers of messages under this scheme:
|
375
|
+
|
376
|
+
```
|
377
|
+
topic_producer = RabbitMQ::Actors::TopicProducer.new(topic_name: 'weather', logger: Rails.logger)
|
378
|
+
```
|
379
|
+
|
380
|
+
where
|
381
|
+
|
382
|
+
- `topic_name:` (mandatory) is the name of the exchange to send messages to.
|
383
|
+
|
384
|
+
`reply_queue_name:` and `logger` are the 2 optional params that can be added.
|
385
|
+
|
386
|
+
|
387
|
+
**Publishing messages**
|
388
|
+
|
389
|
+
```
|
390
|
+
message = { temperature: 20, rain: 30%, wind: 'NorthEast' }.to_json
|
391
|
+
topic_producer.publish(message, message_id: '1234837633', content_type: "application/json", routing_key: 'Europe.Spain.Madrid')
|
392
|
+
```
|
393
|
+
|
394
|
+
Note the special format of the mandatory `routing_key:` param
|
395
|
+
|
396
|
+
|
397
|
+
**Closing the channel**
|
398
|
+
|
399
|
+
```
|
400
|
+
topic_producer.close
|
401
|
+
```
|
402
|
+
|
403
|
+
or using the chained way:
|
404
|
+
|
405
|
+
```
|
406
|
+
topic_producer.publish(message, message_id: '1234837633', content_type: "application/json", routing_key: 'Europe.Spain.Madrid').and_close
|
407
|
+
```
|
408
|
+
|
409
|
+
|
410
|
+
**Defining a topic consumer**
|
411
|
+
|
412
|
+
Use the class `RabbitMQ::Actors::TopicConsumer` in a similar way as to the rest of consumer types:
|
413
|
+
|
414
|
+
```
|
415
|
+
class SpainTennisListener < RabbitMQ::Actors::TopicConsumer
|
416
|
+
def initialize
|
417
|
+
super(topic_name: 'sports',
|
418
|
+
binding_keys: '#.tennis.#.spain.#',
|
419
|
+
logger: Rails.logger,
|
420
|
+
on_cancellation: ->{ ActiveRecord::Base.connection.close })
|
421
|
+
end
|
422
|
+
|
423
|
+
private
|
424
|
+
|
425
|
+
def perform(**task)
|
426
|
+
match_data = JSON.parse(task[:body])
|
427
|
+
process_tennis_match(match_data)
|
428
|
+
end
|
429
|
+
|
430
|
+
def process_tennis_match(data)
|
431
|
+
...
|
432
|
+
end
|
433
|
+
end
|
434
|
+
|
435
|
+
class AmericaSoccerListener < RabbitMQ::Actors::TopicConsumer
|
436
|
+
def initialize
|
437
|
+
super(exchange_name: 'sports',
|
438
|
+
binding_keys: '#.soccer.#.america.#',
|
439
|
+
logger: Rails.logger,
|
440
|
+
on_cancellation: ->{ ActiveRecord::Base.connection.close })
|
441
|
+
end
|
442
|
+
|
443
|
+
private
|
444
|
+
|
445
|
+
def perform(**task)
|
446
|
+
match_data = JSON.parse(task[:body])
|
447
|
+
process_soccer_match(match_data)
|
448
|
+
end
|
449
|
+
|
450
|
+
def process_soccer_match(data)
|
451
|
+
...
|
452
|
+
end
|
453
|
+
end
|
454
|
+
```
|
455
|
+
|
456
|
+
`RabbitMQ::Actors::TopicConsumer` class requires the following mandatory keyword arguments:
|
457
|
+
|
458
|
+
- `topic_name:` name of the exchange where to consume messages from.
|
459
|
+
- `binding_keys:` a string or list of strings with the routing key matching patterns this consumer
|
460
|
+
is interested in.
|
461
|
+
|
462
|
+
As always, you can also add `logger:` and `on_cancellation:` keyword params (see worker documentation above)
|
463
|
+
|
464
|
+
|
465
|
+
**Running a topic consumer**
|
466
|
+
|
467
|
+
Like every consumer actor, call #start! method on an instance to start listening and processing messages.
|
468
|
+
|
469
|
+
```
|
470
|
+
SpainTennisListener.new.start!
|
471
|
+
AmericaSoccerListener.new.start!
|
472
|
+
```
|
473
|
+
|
474
|
+
Press ^c on a console or send a process termination signal to stop it.
|
475
|
+
|
476
|
+
|
477
|
+
### Headers pattern
|
478
|
+
The headers pattern is a strategy based on headers instead of routing keys to deliver messages
|
479
|
+
to consumers. Messages add a `headers:` property including pairs of key-value entries.
|
480
|
+
Consumers show interest in certain headers to get messages sent.
|
481
|
+
|
482
|
+
**Creating a headers producer**
|
483
|
+
|
484
|
+
Instantiate the class `RabbitMQ::Actors::HeadersProducer` to create publishers of messages under this scheme:
|
485
|
+
|
486
|
+
```
|
487
|
+
headers_producer = RabbitMQ::Actors::HeadersProducer.new(headers_name: 'reports', logger: Rails.logger)
|
488
|
+
```
|
489
|
+
|
490
|
+
where
|
491
|
+
|
492
|
+
- `headers_name:` (mandatory) is the name of the exchange to send messages to.
|
493
|
+
|
494
|
+
`reply_queue_name:` and `logger` are the 2 optional params that can be added.
|
495
|
+
|
496
|
+
|
497
|
+
**Publishing messages**
|
498
|
+
|
499
|
+
```
|
500
|
+
message = 'A report about USA economy'
|
501
|
+
headers_producer.publish(
|
502
|
+
message,
|
503
|
+
message_id: '1234837633',
|
504
|
+
headers: { 'type' => :economy, 'area' => 'USA'})
|
505
|
+
```
|
506
|
+
|
507
|
+
where
|
508
|
+
|
509
|
+
- `headers:` send the message only to consumers bound to this exchange and matching any/all
|
510
|
+
of these header pairs.
|
511
|
+
|
512
|
+
As usual, `message` and `message_id:` are also mandatory params. See documentation above for
|
513
|
+
all the optional message params.
|
514
|
+
|
515
|
+
|
516
|
+
**Closing the channel**
|
517
|
+
|
518
|
+
```
|
519
|
+
headers_producer.close
|
520
|
+
```
|
521
|
+
|
522
|
+
or using the chained way:
|
523
|
+
|
524
|
+
```
|
525
|
+
headers_producer.publish(...).and_close
|
526
|
+
```
|
527
|
+
|
528
|
+
|
529
|
+
**Defining a headers consumer**
|
530
|
+
|
531
|
+
Use the class `RabbitMQ::Actors::HeadersConsumer` in a similar way as to the rest of consumer types:
|
532
|
+
|
533
|
+
````
|
534
|
+
class NewYorkBranchListener < RabbitMQ::Actors::HeadersConsumer
|
535
|
+
def initialize
|
536
|
+
super(headers_name: 'reports',
|
537
|
+
binding_headers: { 'type' => :econony, 'area' => 'USA', 'x-match' => 'any' },
|
538
|
+
logger: Rails.logger,
|
539
|
+
on_cancellation: ->{ ActiveRecord::Base.connection.close })
|
540
|
+
end
|
541
|
+
|
542
|
+
private
|
543
|
+
|
544
|
+
def perform(**task)
|
545
|
+
report_data = JSON.parse(task[:body])
|
546
|
+
process_report(report_data)
|
547
|
+
end
|
548
|
+
|
549
|
+
def process_report(data)
|
550
|
+
...
|
551
|
+
end
|
552
|
+
end
|
553
|
+
|
554
|
+
class LondonBranchListener < RabbitMQ::Actors::HeadersConsumer
|
555
|
+
def initialize
|
556
|
+
super(headers_name: 'reports',
|
557
|
+
binding_headers: { 'type' => :industry, 'area' => 'Europe', 'x-match' =>'any' },
|
558
|
+
logger: Rails.logger,
|
559
|
+
on_cancellation: ->{ ActiveRecord::Base.connection.close })
|
560
|
+
end
|
561
|
+
|
562
|
+
private
|
563
|
+
|
564
|
+
def perform(**task)
|
565
|
+
report_data = JSON.parse(task[:body])
|
566
|
+
process_report(report_data)
|
567
|
+
end
|
568
|
+
|
569
|
+
def process_report(data)
|
570
|
+
...
|
571
|
+
end
|
572
|
+
end
|
573
|
+
````
|
574
|
+
|
575
|
+
`RabbitMQ::Actors::HedersConsumer` class requires the following mandatory keyword arguments:
|
576
|
+
|
577
|
+
- `headers_name:` name of the exchange where to consume messages from.
|
578
|
+
- `binding_headers:` hash of headers this consumer is interested in.
|
579
|
+
|
580
|
+
Note the special mandatory binding header `'x-match'`. Its value can be one of these:
|
581
|
+
- `'any'` receive the message if any of the message headers matches any of the binding headers.
|
582
|
+
- `'all'` receive the message only if all of the binding headers are included in the message headers.
|
583
|
+
|
584
|
+
Optional params `logger:` and `on_cancellation:`
|
585
|
+
|
586
|
+
|
587
|
+
**Running a headers consumer**
|
588
|
+
|
589
|
+
Like every consumer actor, call #start! method on an instance to start listening and processing messages.
|
590
|
+
|
591
|
+
```
|
592
|
+
NewYorkBranchListener.new.start!
|
593
|
+
LondonBranchListener.new.start!
|
594
|
+
```
|
595
|
+
|
596
|
+
Press ^c on a console or send a process termination signal to stop it.
|
597
|
+
|
598
|
+
|
599
|
+
## Contributing
|
600
|
+
|
601
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[ltello]/rabbitmq-actors.
|
602
|
+
|