rabbitmq-actors 2.0.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.
@@ -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
@@ -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
@@ -0,0 +1,5 @@
1
+ --color
2
+ --require byebug
3
+ --require json
4
+ --require spec_helper
5
+ --format documentation
@@ -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
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rabbitmq-actors.gemspec
4
+ gemspec
@@ -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
+