rabbitmq-actors 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+