liebre 0.1.21 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +2 -0
  3. data/Gemfile.lock +9 -7
  4. data/{LICENSE → LICENSE.txt} +1 -1
  5. data/README.md +492 -195
  6. data/Rakefile +2 -0
  7. data/lib/liebre.rb +27 -16
  8. data/lib/liebre/actor.rb +11 -0
  9. data/lib/liebre/actor/consumer.rb +80 -0
  10. data/lib/liebre/actor/consumer/callback.rb +34 -0
  11. data/lib/liebre/actor/consumer/core.rb +80 -0
  12. data/lib/liebre/actor/consumer/reporter.rb +84 -0
  13. data/lib/liebre/actor/consumer/resources.rb +47 -0
  14. data/lib/liebre/actor/consumer/resources/config.rb +65 -0
  15. data/lib/liebre/actor/context.rb +40 -0
  16. data/lib/liebre/actor/context/declare.rb +44 -0
  17. data/lib/liebre/actor/context/handler.rb +44 -0
  18. data/lib/liebre/actor/publisher.rb +58 -0
  19. data/lib/liebre/actor/publisher/core.rb +42 -0
  20. data/lib/liebre/actor/publisher/reporter.rb +55 -0
  21. data/lib/liebre/actor/publisher/resources.rb +33 -0
  22. data/lib/liebre/actor/rpc/client.rb +88 -0
  23. data/lib/liebre/actor/rpc/client/core.rb +75 -0
  24. data/lib/liebre/actor/rpc/client/pending.rb +65 -0
  25. data/lib/liebre/actor/rpc/client/reporter.rb +71 -0
  26. data/lib/liebre/actor/rpc/client/resources.rb +62 -0
  27. data/lib/liebre/actor/rpc/client/task.rb +33 -0
  28. data/lib/liebre/actor/rpc/server.rb +74 -0
  29. data/lib/liebre/actor/rpc/server/callback.rb +28 -0
  30. data/lib/liebre/actor/rpc/server/core.rb +75 -0
  31. data/lib/liebre/actor/rpc/server/reporter.rb +72 -0
  32. data/lib/liebre/actor/rpc/server/resources.rb +53 -0
  33. data/lib/liebre/adapter.rb +8 -0
  34. data/lib/liebre/adapter/bunny.rb +23 -0
  35. data/lib/liebre/adapter/bunny/chan.rb +38 -0
  36. data/lib/liebre/adapter/bunny/conn.rb +32 -0
  37. data/lib/liebre/adapter/bunny/exchange.rb +20 -0
  38. data/lib/liebre/adapter/bunny/queue.rb +59 -0
  39. data/lib/liebre/adapter/interface.rb +26 -0
  40. data/lib/liebre/adapter/interface/chan.rb +29 -0
  41. data/lib/liebre/adapter/interface/conn.rb +21 -0
  42. data/lib/liebre/adapter/interface/exchange.rb +13 -0
  43. data/lib/liebre/adapter/interface/queue.rb +37 -0
  44. data/lib/liebre/bridge.rb +72 -0
  45. data/lib/liebre/bridge/channel_builder.rb +36 -0
  46. data/lib/liebre/config.rb +8 -38
  47. data/lib/liebre/engine.rb +61 -0
  48. data/lib/liebre/engine/builder.rb +48 -0
  49. data/lib/liebre/engine/repository.rb +56 -0
  50. data/lib/liebre/engine/state.rb +49 -0
  51. data/lib/liebre/runner.rb +15 -47
  52. data/lib/liebre/version.rb +1 -1
  53. data/liebre.gemspec +9 -7
  54. data/spec/integration/publish_and_consume_spec.rb +71 -0
  55. data/spec/integration/rpc_communication_spec.rb +81 -0
  56. data/spec/integration/start_twice_spec.rb +63 -0
  57. data/spec/liebre/actor/consumer_spec.rb +169 -0
  58. data/spec/liebre/actor/context/declare_spec.rb +69 -0
  59. data/spec/liebre/actor/context/handler_spec.rb +65 -0
  60. data/spec/liebre/actor/publisher_spec.rb +58 -0
  61. data/spec/liebre/actor/rpc/client_spec.rb +126 -0
  62. data/spec/liebre/actor/rpc/server_spec.rb +141 -0
  63. data/spec/liebre/adapter/bunny_spec.rb +66 -0
  64. data/spec/liebre/bridge_spec.rb +54 -0
  65. data/spec/liebre/engine/builder_spec.rb +42 -0
  66. data/spec/liebre/engine_spec.rb +90 -0
  67. data/spec/liebre/version_spec.rb +10 -0
  68. data/spec/spec_helper.rb +2 -9
  69. metadata +97 -58
  70. data/lib/liebre/common.rb +0 -7
  71. data/lib/liebre/common/utils.rb +0 -37
  72. data/lib/liebre/connection_manager.rb +0 -85
  73. data/lib/liebre/publisher.rb +0 -113
  74. data/lib/liebre/runner/consumers.rb +0 -46
  75. data/lib/liebre/runner/starter.rb +0 -44
  76. data/lib/liebre/runner/starter/consumer.rb +0 -129
  77. data/lib/liebre/runner/starter/consumer/handler.rb +0 -35
  78. data/lib/liebre/runner/starter/resources.rb +0 -45
  79. data/lib/liebre/runner/starter/resources/queue_builder.rb +0 -63
  80. data/lib/liebre/runner/starter/rpc.rb +0 -59
  81. data/lib/liebre/tasks.rb +0 -12
  82. data/spec/config/liebre.yml +0 -48
  83. data/spec/config/rabbitmq.yml +0 -35
  84. data/spec/integration_spec.rb +0 -76
  85. data/spec/liebre/config_spec.rb +0 -63
  86. data/spec/liebre/connection_manager_spec.rb +0 -44
  87. data/spec/liebre/publisher_spec.rb +0 -92
  88. data/spec/liebre/runner/consumers_spec.rb +0 -59
  89. data/spec/liebre/runner/starter/consumer_spec.rb +0 -145
  90. data/spec/liebre/runner/starter/resources/queue_builder_spec.rb +0 -69
  91. data/spec/liebre/runner/starter/resources_spec.rb +0 -38
  92. data/spec/liebre/runner/starter/rpc_spec.rb +0 -100
  93. data/spec/liebre/runner/starter_spec.rb +0 -70
  94. data/spec/liebre/runner_spec.rb +0 -54
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1e5a7ee5a915ac1eef958be46fcaa5dc4f84c6d5
4
- data.tar.gz: 162226313a78c9c3373b56edc4a66844ff69568c
3
+ metadata.gz: d8d00979bc470fd55b7dce291b96658266ea74ac
4
+ data.tar.gz: 48dff8d83158aa0a84f74a5aaac36e1f86de64b1
5
5
  SHA512:
6
- metadata.gz: f75296a952c2a8f02de319343df92d8f9caf839ec819a57a909877fffff5dfd99046d7cf0d65ed38fbffe5c76c3bdc6b2739d8f8f556901ce13dd0799ebf9da5
7
- data.tar.gz: 18b09cb54e1430ef62e2c7a807c3561cef1b8a4650987ad3f3075196fc8fc7f4eb4431dd41a788627ed6e4bc08ac34eb33f914cfed50105b72675f1fbc88dbc3
6
+ metadata.gz: b4d5fe0344c0cecf9ff3313ff58521406d20b9f6df88aae242bcfc450860f8dabd1180a3f0ec11371297fa2a1792508d279fd7d1b4bbc03dfcafc303dc9631bf
7
+ data.tar.gz: 20f95bcd2593a5bf0ef664f242fb3d53a7d429d785b83106643eafec38afaee2e021a512f4deb4b442a3f03ecae3f3fae0c9e623e4df0bf6bd1306fdad3e3b2c
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
@@ -1,28 +1,29 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- liebre (0.1.15)
5
- bunny (~> 2.5, >= 2.5.1)
4
+ liebre (0.2.0)
5
+ concurrent-ruby
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
10
  amq-protocol (2.0.1)
11
- bunny (2.5.1)
11
+ bunny (2.2.2)
12
12
  amq-protocol (>= 2.0.1)
13
13
  coderay (1.1.1)
14
- diff-lcs (1.2.5)
14
+ concurrent-ruby (1.0.5)
15
+ diff-lcs (1.3)
15
16
  method_source (0.8.2)
16
17
  pry (0.10.4)
17
18
  coderay (~> 1.1.0)
18
19
  method_source (~> 0.8.1)
19
20
  slop (~> 3.4)
20
- rake (11.2.2)
21
+ rake (12.0.0)
21
22
  rspec (3.5.0)
22
23
  rspec-core (~> 3.5.0)
23
24
  rspec-expectations (~> 3.5.0)
24
25
  rspec-mocks (~> 3.5.0)
25
- rspec-core (3.5.2)
26
+ rspec-core (3.5.4)
26
27
  rspec-support (~> 3.5.0)
27
28
  rspec-expectations (3.5.0)
28
29
  diff-lcs (>= 1.2.0, < 2.0)
@@ -38,10 +39,11 @@ PLATFORMS
38
39
 
39
40
  DEPENDENCIES
40
41
  bundler (~> 1.6)
42
+ bunny
41
43
  liebre!
42
44
  pry
43
45
  rake
44
46
  rspec
45
47
 
46
48
  BUNDLED WITH
47
- 1.13.6
49
+ 1.14.6
@@ -1,4 +1,4 @@
1
- Copyright (c) 2016 jcabotc
1
+ Copyright (c) 2017 jcabotc
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -1,268 +1,565 @@
1
1
  # Liebre
2
2
 
3
- ## Intro
3
+ A library to interact with AMQP servers.
4
4
 
5
- **Liebre stands for hare in spanish.**
5
+ Liebre stands for hare in spanish.
6
6
 
7
- This is a gem that handles RabbitMQ consumers, publishers and RPCs, based on [bunny](https://github.com/ruby-amqp/bunny).
7
+ ## Installation
8
8
 
9
- * It allows to create classes that will be invoked everytime a message it's received in its subscribed queue.
10
- * It handles RPCs as a special Consumer where a callback message is returned to the exchange.
11
- * You can use its Publisher to send message to an exchange.
12
- * There is also a Publisher RPC method to send a message and wait for its response.
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'liebre'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install liebre
20
+
21
+ ## Introduction
22
+
23
+ The Liebre library provides 4 abstractions, or **actors**, to interact with the server:
24
+
25
+ * **Publisher**: Publishes messages to an exchange
26
+ * **Consumer**: Binds a queue to an exchange, and consumes messages
27
+ * **RPC Client**: Publishes messages to an exchange and blocks until a response is received through an exclusive queue
28
+ * **RPC Server**: Binds a queue to an exchange, consumes messages, and replies the caller by putting a message at the specified queue
29
+
30
+ Each actor has its own thread and its own channel. Some actors (Consumer and RPC Server)
31
+ also have their own thread pool in order to be able to handle messages concurrently.
32
+
33
+ It leverages [concurrent-ruby](https://github.com/ruby-concurrency/concurrent-ruby) `Concurrent::Async` mixin to
34
+ implement the actors.
13
35
 
14
36
  ## Configuration
15
- It is based on 2 config files:
16
37
 
17
- * rabbitmq.yml: it contains RabbitMQ connection configurations and can be enviroment dependant (default path `config/rabbitmq.yml`), it must contain, at least the `default` connection
18
- * without environment set:
38
+ Liebre accepts the following configuration options:
39
+
40
+ ```ruby
41
+ Liebre.configure do |config|
42
+ config.logger = $logger
43
+ config.adapter = Liebre::Adapter::Bunny
44
+ config.connections = connections_config
45
+ config.actors = actors_config
46
+ end
19
47
  ```
20
- default:
21
- :host: localhost
22
- :port: 5672
23
- :user: guest
24
- :pass: guest
25
- :vhost: /
26
- :threaded: true
27
- :heartbeat: 2
48
+
49
+ ### Logger
50
+
51
+ The logger configuration option is optional and defaults to `Logger.new(nil)`.
52
+ Liebre will log in the following events:
53
+
54
+ * An actor is started
55
+ * An actor is stopped
56
+ * Some error happened on the actor's thread
57
+
58
+ Any other logging should be done from the application.
59
+
60
+ ### Adapter
61
+
62
+ It specifies the adapter to use to interact with the server. The only adapter that ships with
63
+ the library is the `Liebre::Adapter::Bunny` adapter that uses the `bunny` gem.
64
+
65
+ **IMPORTANT**: Note that you should have the `bunny` gem available to use the
66
+ `Liebre::Adapter::Bunny` adapter.
67
+
68
+ ### Connections
69
+
70
+ On startup Liebre will establish a set of connections with one or more AMQP servers. Actors can be
71
+ started on any of this connections.
72
+
73
+ ```ruby
74
+ {one: {host: "foo.com", port: 123},
75
+ other: {}}
28
76
  ```
29
- * with environment set:
77
+
78
+ The example above will establish two connections, the configuration for each connection
79
+ will be given with no modification to the adapter.
80
+
81
+ ### Actors
82
+
83
+ The configuration of all actors:
84
+
85
+ ```ruby
86
+ {
87
+ publishers: {
88
+ my_publisher: {connection: :one,
89
+ resources: {
90
+ exchange: {name: "foo", type: "fanout"}}},
91
+ my_other_pub: {connection: :other,
92
+ resources: {
93
+ exchange: {name: "bar", type: "direct", opts: {durable: true}}}}
94
+ },
95
+ consumers: {
96
+ my_consumer: {connection: :one,
97
+ handler: MyApp::Consumer,
98
+ prefetch_count: 10,
99
+ pool_size: 10,
100
+ resources: {
101
+ exchange: {name: "baz", type: "fanout"},
102
+ queue: {name: "baz_queue"},
103
+ bind: [{routing_key: "a_key"}, {routing_key: "another"}]}},
104
+ },
105
+ rpc_clients: {
106
+ my_rpc_client: {connection: :other,
107
+ resources: {
108
+ exchange: {name: "qux", type: "fanout"}}},
109
+ },
110
+ rpc_servers: {
111
+ my_rpc_server: {connection: :one,
112
+ handler: MyApp::RPCServer,
113
+ prefetch_count: 5,
114
+ pool_size: 5,
115
+ resources: {
116
+ exchange: {name: "quux", type: "fanout"},
117
+ queue: {name: "quux_queue"}}}
118
+ }
119
+ }
30
120
  ```
31
- development:
32
- default:
33
- :host: localhost
34
- :port: 5672
35
- :user: guest
36
- :pass: guest
37
- :vhost: /
38
- :threaded: true
39
- :heartbeat: 2
40
- rpc:
41
- :host: localhost
42
- :port: 5672
43
- :user: guest
44
- :pass: guest
45
- :vhost: rpc
46
- :threaded: true
47
- :heartbeat: 2
48
- test:
49
- default:
50
- :host: localhost
51
- :port: 5672
52
- :user: guest
53
- :pass: guest
54
- :vhost: /
55
- :threaded: true
56
- :heartbeat: 2
57
- rpc:
58
- :host: localhost
59
- :port: 5672
60
- :user: guest
61
- :pass: guest
62
- :vhost: rpc
63
- :threaded: true
64
- :heartbeat: 2
65
-
66
- production:
67
- ...
121
+
122
+ The example above will start 5 actors:
123
+
124
+ * A publisher to the `"foo"` exchange over the connection `:one`
125
+ * A publisher to the `"bar"` exchange over the connection `:other`
126
+ * A consumer of the "baz" queue that will consume messages over the connection `:one` and process them by running `MyApp::Consumer` handler on a pool of 10 dedicated threads
127
+ * A rpc client to the `"qux"` exchange over the connection `:other`
128
+ * A rpc server of the "quux" queue that will consume messages over the connection `:one` and process them by running `MyApp::RPCServer` handler on a pool of 5 dedicated threads
129
+
130
+ Common options:
131
+
132
+ * `:connection` - The name of the connection to open the channel over
133
+ * `:resources` - Configuration about queues and exchanges. Depends on the actor
134
+
135
+ Options for message handlers (Consumer and RPC Server):
136
+
137
+ * `:handler` - The class to use to handle messages. Its interface depends on the actor
138
+ * `:prefetch_count` - The prefetch count of the actor's channel
139
+ * `:pool_size` - The number of dedicated threads that will be used to handle messages concurrently
140
+
141
+ ## Start the engine
142
+
143
+ ### On an existing service
144
+
145
+ A `Liebre::Engine` is an object that is able to start and stop a given configuration.
146
+
147
+ ```ruby
148
+ require 'yaml'
149
+ require 'liebre'
150
+
151
+ config = Liebre::Config.new
152
+
153
+ config.adapter = Liebre::Adapter::Bunny
154
+ config.connections = YAML.load_file("config/rabbitmq.yml")
155
+ config.actors = YAML.load_file("config/liebre.yml")
156
+
157
+ engine = Liebre::Engine.new(config)
158
+ engine.start
68
159
  ```
69
160
 
70
- * liebre.yml (default path `config/liebre.yml`)
161
+ The example above will establish the specified connections and start all actors with
162
+ their own threads and thread pools.
163
+
164
+ Usually only one engine is required per application. To simplify the process, a default
165
+ engine that uses the default config is provided.
166
+
167
+ ```ruby
168
+ require 'liebre'
169
+
170
+ Liebre.configure do |config|
171
+ config.adapter = Liebre::Adapter::Bunny
172
+ config.connections = YAML.load_file("config/rabbitmq.yml")
173
+ config.actors = YAML.load_file("config/liebre.yml")
174
+ end
175
+
176
+ Liebre.engine.start
177
+ ```
178
+
179
+ This kind of startup is commonly used on rack applications. The previous code
180
+ can be called from a Rails initializer.
181
+
182
+ ### As a standalone application
183
+
184
+ If you wish to start Liebre as a standalone application `Liebre::Runner` is provided.
185
+
186
+ ```ruby
187
+ require 'liebre'
188
+
189
+ Liebre.configure do |config|
190
+ # some config
191
+ end
192
+
193
+ runner = Liebre::Runner.new(engine: Liebre.engine)
194
+ runner.run
71
195
  ```
72
- rpc_request_timeout: 5
73
-
74
- consumers:
75
- some_consumer:
76
- class_name: MyConsumer
77
- rpc: false
78
- pool_size: 1
79
- num_threads: 4
80
- exchange:
81
- name: "consumer_exchange"
82
- type: "fanout"
83
- opts:
84
- durable: false
85
- queue:
86
- name: "consumer_queue"
87
- opts:
88
- durable: false
89
- bind:
90
- routing_key: #key a string or an array of strings
91
- - key_1
92
- - key_2
93
-
94
- some_rpc:
95
- class_name: MyRPC
96
- rpc: true
97
- connection_name: rpc
98
- pool_size: 1
99
- exchange:
100
- name: "rpc_exchange"
101
- type: "fanout"
102
- opts:
103
- durable: false
104
- queue:
105
- name: "rpc_queue"
106
- opts:
107
- durable: false
108
-
109
- publishers:
110
- some_publisher:
111
- exchange:
112
- name: "consumer_exchange"
113
- type: "fanout"
114
- opts:
115
- durable: false
116
- rpc_publisher:
117
- connection_name: rpc
118
- exchange:
119
- name: "rpc_exchange"
120
- type: "fanout"
121
- opts:
122
- durable: false
196
+
197
+ The example above starts the runner with the default engine
198
+
199
+ It sets up some system signals in order to respond gracefully to unix `kill`
200
+ and sleeps the main thread forever.
201
+
202
+ The previous pattern is so common that a shortcut is provided:
203
+
204
+ ```ruby
205
+ require 'liebre'
206
+
207
+ Liebre.configure do |config|
208
+ # some config
209
+ end
210
+
211
+ Liebre.start
123
212
  ```
124
213
 
125
- ### Consumers
214
+ ### Start partially
126
215
 
127
- An entry for each consumer in your app, consumer options:
128
- * `class_name` (mandatory): The class that will be invoked everytime a message is received
129
- * `rpc` is a flag to specify the consumer behaviour (default false)
130
- * `connection_name`: the name of the connection to use (default `default`)
131
- * `pool_size` of the connection channel (default 1)
132
- * `num_threads`: number of consumers of the queue (default 1)
133
- * `exchange` a hash of options:
134
- * `name`: the exchange name
135
- * `type`: the exchange type (fanout, direct or topic)
136
- * `opts`: options hash to pass to bunny exchange function
137
- * `queue` a hash of options:
138
- * `name`: the queue name
139
- * `opts`: options hash to pass to bunny queue function
140
- * `bind` a hash of options (optional):
141
- * `routing_key`: the binding routing key, it can be a single string or an array of strings
216
+ Imagine the following setup: You have an application that may be started as a rack
217
+ application and also as a worker with no rack interface to consume from rabbitmq as
218
+ a background tasks system.
142
219
 
143
- ### Publishers
220
+ When you start the application with rack you may use publishers or rpc clients, but
221
+ you don't want to start the consumers and rpc servers.
144
222
 
145
- An entry for each exchange you want to publish to, options:
146
- * `connection_name`: the name of the connection to use (default `default`)
147
- * `exchange` a hash of options:
148
- * `name`: the exchange name
149
- * `type`: the exchange type (fanout, direct or topic)
150
- * `opts`: options hash to pass to bunny exchange function
223
+ When you start the application with `Liebre::Runner` you want all actors to start.
151
224
 
152
- ### Other configurations
225
+ To handle this kind of scenarios `Liebre` keeps track of the already started actors
226
+ and prevents them to start twice on a given engine.
153
227
 
154
- `rpc_request_timeout`: set the timeout for an RPC request
228
+ A solution to this case may be the following:
155
229
 
156
- ### Change default paths and set env and Logger
230
+ Start all publishers and consumers on an initialized that will be shared between
231
+ both ways to start the application:
157
232
 
158
- You can change these defaults in an initializer with something like:
233
+ ```ruby
234
+ require 'liebre'
159
235
 
236
+ Liebre.configure do |config|
237
+ # some config
238
+ end
239
+
240
+ Liebre.engine.start(only: [:publishers, :rpc_clients])
160
241
  ```
161
- Liebre::Config.config_path = "your/custom/path"
162
- Liebre::Config.connection_path = "your/custom/path"
163
242
 
164
- Liebre::Config.env = "production"
165
- Liebre::Config.logger = Logger.new(...)
243
+ This code will configure `Liebre` and starts all publishers and rpc clients.
244
+
245
+ When you start your application with `Liebre::Runner` you can do the following:
246
+
247
+ ```ruby
248
+ # run all your initializers including the one above
166
249
 
250
+ require 'liebre'
251
+ Liebre.start
167
252
  ```
168
253
 
169
- ## Usage
254
+ This code will start all actors, and publishers and rpc clients will only be started
255
+ once.
170
256
 
171
- There are 2 different consumer usages: `Consumer` and `RPC`.
257
+ ## Actors
172
258
 
173
- ### Consumer
259
+ Once a liebre engine has been started a `Liebre::Repository` has been populated with all actors
260
+ that can be fetched by name.
174
261
 
175
- You only need to create a class with this simple interface:
262
+ ```ruby
263
+ # ... start the engine
264
+ repo = engine.repo
176
265
 
266
+ publisher_1 = repo.publisher(:my_publisher)
267
+ publisher_2 = repo.publisher(:my_other_pub)
268
+ consumer = repo.consumer(:my_consumer)
269
+ rpc_client = repo.rpc_server(:my_rpc_server)
270
+ rpc_server = repo.rpc_server(:my_rpc_server)
177
271
  ```
178
- class MyConsumer
179
-
180
- def initialize payload, meta
181
- @payload = payload #the content of the message
182
- @meta = meta #the meta information (also called properties)
183
- end
184
272
 
185
- def call
186
- #do your stuff here
187
- #return :ack to anknowledge the message,
188
- #return :reject to requeue it
189
- #or return :error to send it to the dead-letter-exchange
190
- :ack
191
- end
273
+ ### Publisher
274
+
275
+ A publisher is an actor that declares an exchange on startup and provides a method to publish
276
+ messages to that exchange.
277
+
278
+ The `:resources` section of the actor's configuration requires the exchange specification:
279
+
280
+ ```ruby
281
+ require 'liebre'
192
282
 
283
+ Liebre.configure do |config|
284
+ config.adapter = Liebre::Adapter::Bunny
285
+ config.connections = {default: {}}
286
+
287
+ config.actors = {
288
+ publishers: {
289
+ my_pub: {
290
+ connection: :default,
291
+ resources: {
292
+ exchange: {name: "foo",
293
+ type: "direct",
294
+ opts: {durable: true}}
295
+ }
296
+ }
297
+ }
298
+ }
193
299
  end
300
+
301
+ Liebre.engine.start
302
+
303
+ publisher = Liebre.repo.publisher(:my_pub)
304
+ publisher.publish("some_data")
305
+ publisher.publish("more_data", :routing_key => "my_key")
194
306
  ```
195
307
 
196
- Every time a message is received, a new instance of this class will be created.
308
+ The exchange specification:
197
309
 
198
- ### RPC
310
+ * `:name` - The name of the exchange
311
+ * `:type` - The exchange type (`"direct"`, `"fanout"`, etc)
312
+ * `:opts` - (defaults to `{}`) The exchange options
199
313
 
200
- You only need to create a class with this simple interface:
314
+ Once started the `#publish` method can be used to send messages to the configured
315
+ exchange.
316
+
317
+ ### Consumer
318
+
319
+ A consumer is an actor that declares an exchange, a queue and binds them on startup.
320
+ It owns a thread pool to handle consumed messages.
321
+
322
+ After declaration it subscribes to the queue and starts consuming messages. Once a message
323
+ is consumed a handler for that message is started in a thread of the pool.
324
+
325
+ The handler class must implement the following interface:
326
+
327
+ * `#initialize` receives 3 arguments: `payload`, `meta`, and `callback`.
328
+ * `#call`
329
+
330
+ The handler class is initialized with that 3 arguments:
331
+
332
+ * `payload` - The actual content of the message
333
+ * `meta` - Message metadata that includes the headers and other information (depends on the adapter)
334
+ * `callback` - An object that responds to `#ack`, `#nack`, and `#reject`
335
+
336
+ ```ruby
337
+ class MyHandler
201
338
 
202
- ```
203
- class MyRPC
204
-
205
339
  def initialize payload, meta, callback
206
- @payload = payload #the content of the message
207
- @meta = meta #the meta information (also called properties)
208
- @callback = callback #a Proc that will publish an answer
340
+ @payload = payload
341
+ @callback = callback
209
342
  end
210
343
 
211
344
  def call
212
- #do your stuff here
213
- @callback.call("your response")
345
+ case payload
346
+ when "0" then zero()
347
+ when "1" then one()
348
+ else raise "unknown!"
349
+ end
214
350
  end
215
351
 
352
+ private
353
+
354
+ def zero
355
+ puts "yay!"
356
+ callback.ack()
357
+ end
358
+
359
+ def one
360
+ puts "wtf!"
361
+ callback.reject(requeue: false)
362
+ end
363
+
364
+ attr_reader :payload, :callback
365
+
216
366
  end
217
367
  ```
218
368
 
219
- Every time a message is received, a new instance of this class will be created.
369
+ The handler above will print `"yay!"` and ack the message when the payload is `"0"`,
370
+ print `"wtf!"` and reject the message when the payload is `"1"`, and will raise (and therefore
371
+ will be rejected by liebre) when the handler raises an error.
220
372
 
221
- There are 2 ways to publish a message: `enqueue` and `enqueue_and_wait`.
373
+ The `:resources` section of the actor's configuration requires the exchange and the queue,
374
+ and may include binding options.
222
375
 
223
- ### `enqueue`
376
+ ```ruby
377
+ require 'liebre'
378
+
379
+ Liebre.configure do |config|
380
+ config.adapter = Liebre::Adapter::Bunny
381
+ config.connections = {default: {}}
382
+
383
+ config.actors = {
384
+ consumers: {
385
+ my_con: {
386
+ connection: :default,
387
+ handler: MyHandler,
388
+ prefetch_count: 5,
389
+ pool_size: 5,
390
+ resources: {
391
+ exchange: {name: "foo",
392
+ type: "direct",
393
+ opts: {durable: true}},
394
+ queue: {name: "bar",
395
+ opts: {durable: true}},
396
+ bind: [{routing_key: "one"},
397
+ {routing_key: "other"}]
398
+ }
399
+ }
400
+ }
401
+ }
402
+ end
224
403
 
404
+ Liebre.engine.start
225
405
  ```
226
- publisher = Liebre::Publisher.new("some_publisher_name")
227
406
 
228
- publisher.enqueue "hello", :routing_key => "consumer_queue"
407
+ The exchange specification is the same as for the publisher.
229
408
 
230
- publisher.enqueue "bye", :routing_key => "consumer_queue"
409
+ The queue specification:
231
410
 
232
- ```
411
+ * `:name` - The name of the queue
412
+ * `:opts` - (defaults to `{}`) The queue options
233
413
 
234
- ### `enqueue_and_wait` (alias `rpc`)
414
+ The bind specification is optional and defaults to `{}`. `:bind` value may be:
235
415
 
236
- ```
237
- rpc_publisher = Liebre::Publisher.new("rpc_publisher")
416
+ * not present - the queue is bound to the exchange once with `{}` as options
417
+ * a hash - the queues is bound once to the exchange with that options
418
+ * a list of hashes - the queues is bound to the exchange once per hash of options
238
419
 
239
- response = publisher.enqueue_and_wait "hello", :routing_key => "consumer_queue"
420
+ The example above declares the exchange `"foo"`, declares the queue `"bar"`,
421
+ binds the queue to the exchange twice: one with the `"one"` routing key and
422
+ another with the `"other"` routing key).
240
423
 
241
- another_response = publisher.rpc "bye", :routing_key => "consumer_queue"
424
+ It starts a thread pool of 5 threads and starts consuming messages.
242
425
 
243
- ```
426
+ For each message the consumer receives a handler is instantiated and `#call` is called on
427
+ it in one of the threads of its pool.
244
428
 
245
- ### Installation and Execution
429
+ ### RPC Client
246
430
 
247
- Add the following to your Gemfile:
248
- ```
249
- gem "liebre", ">~ 0.1"
250
- ```
431
+ A RPC client is an actor that declares an exchange, and declares a temporary queue with
432
+ the options `exclusive`, and `auto_delete`.
251
433
 
252
- In your Raketask add:
434
+ After declaration it subscribes to the queue.
253
435
 
254
- ```
255
- require "liebre/tasks"
256
- ```
436
+ The `:resources` section of the rpc client configuration must specify the exchange.
257
437
 
258
- Then you just need to run:
259
- ```
260
- rake liebre:run
438
+ ```ruby
439
+ require 'liebre'
440
+
441
+ Liebre.configure do |config|
442
+ config.adapter = Liebre::Adapter::Bunny
443
+ config.connections = {default: {}}
444
+
445
+ config.actors = {
446
+ rpc_client: {
447
+ my_client: {
448
+ connection: :default,
449
+ resources: {
450
+ exchange: {name: "foo",
451
+ type: "direct",
452
+ opts: {durable: true}},
453
+ queue: {prefix: "client_responses"}
454
+ }
455
+ }
456
+ }
457
+ }
458
+ end
459
+
460
+ Liebre.engine.start
461
+
462
+ client = Liebre.repo.rpc_client(:my_pub)
463
+ client.request("data") # => rpc server response (or nil on timeout, 5 seconds by default)
464
+ client.request("data", routing_key: "bar") # => rpc server response (or nil on timeout, 5 seconds by default)
465
+ client.request("data", {}, 15) # => rpc server response (or nil on timeout after 15 seconds)
261
466
  ```
262
467
 
263
- Alternative way:
468
+ The exchange specification is the same as for the publisher.
469
+
470
+ The queue specification is optional and includes a prefix for the queue's name. The name of the
471
+ queue will be that prefix followed by a random token, for example: `"client_responses_q23jrefdzXw"`.
472
+
473
+ When a request is performed the client will block until the response is received or the timeout is reached.
474
+
475
+ ### RPC Server
476
+
477
+ A rpc server is an actor that declares an exchange, a queue and binds them on startup.
478
+ It owns a thread pool to handle requests.
479
+
480
+ After declaration it subscribes to the queue and starts consuming messages. Once a message
481
+ is consumed a handler for that message is started in a thread of the pool.
482
+
483
+ The handler class must implement the following interface:
484
+
485
+ * `#initialize` receives 3 arguments: `payload`, `meta`, and `callback`.
486
+ * `#call`
487
+
488
+ The handler class is initialized with that 3 arguments:
489
+
490
+ * `payload` - The actual content of the message
491
+ * `meta` - Message metadata that includes the headers and other information (depends on the adapter)
492
+ * `callback` - An object that responds to `#reply`
493
+
494
+ ```ruby
495
+ class MyHandler
496
+
497
+ def initialize payload, meta, callback
498
+ @payload = payload
499
+ @callback = callback
500
+ end
501
+
502
+ def call
503
+ case payload
504
+ when "0" then callback.reply("zero")
505
+ when "1" then callback.reply("one")
506
+ else raise "unknown!"
507
+ end
508
+ end
509
+
510
+ private
511
+
512
+ attr_reader :payload, :callback
513
+
514
+ end
264
515
  ```
516
+
517
+ The handler above will reply to the client with "zero" when payload is "0",
518
+ reply with "one" when the payload is "1", and will raise (and therefore not reply)
519
+ when the handler raises error.
520
+
521
+ The `:resources` section of the actor's configuration requires the exchange and the queue,
522
+ and may include binding options.
523
+
524
+ ```ruby
265
525
  require 'liebre'
266
526
 
267
- Liebre::Runner.new.start
268
- ```
527
+ Liebre.configure do |config|
528
+ config.adapter = Liebre::Adapter::Bunny
529
+ config.connections = {default: {}}
530
+
531
+ config.actors = {
532
+ rpc_servers: {
533
+ my_rpc_server: {
534
+ connection: :default,
535
+ handler: MyHandler,
536
+ prefetch_count: 5,
537
+ pool_size: 5,
538
+ resources: {
539
+ exchange: {name: "foo",
540
+ type: "direct",
541
+ opts: {durable: true}},
542
+ queue: {name: "bar",
543
+ opts: {durable: true}},
544
+ bind: [{routing_key: "one"},
545
+ {routing_key: "other"}]
546
+ }
547
+ }
548
+ }
549
+ }
550
+ end
551
+
552
+ Liebre.engine.start
553
+ ```
554
+
555
+ The exchange specification is the same as for the publisher.
556
+ The queue and bind specifications are the same as for the consumer.
557
+
558
+ The example above declares the exchange `"foo"`, declares the queue `"bar"`,
559
+ binds the queue to the exchange twice: one with the `"one"` routing key and
560
+ another with the `"other"` routing key).
561
+
562
+ It starts a thread pool of 5 threads and starts consuming requests.
563
+
564
+ For each message the consumer receives it instantiates and runs `#call` on
565
+ the new handler in one of the threads of its pool.