liebre 0.1.21 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rspec +2 -0
- data/Gemfile.lock +9 -7
- data/{LICENSE → LICENSE.txt} +1 -1
- data/README.md +492 -195
- data/Rakefile +2 -0
- data/lib/liebre.rb +27 -16
- data/lib/liebre/actor.rb +11 -0
- data/lib/liebre/actor/consumer.rb +80 -0
- data/lib/liebre/actor/consumer/callback.rb +34 -0
- data/lib/liebre/actor/consumer/core.rb +80 -0
- data/lib/liebre/actor/consumer/reporter.rb +84 -0
- data/lib/liebre/actor/consumer/resources.rb +47 -0
- data/lib/liebre/actor/consumer/resources/config.rb +65 -0
- data/lib/liebre/actor/context.rb +40 -0
- data/lib/liebre/actor/context/declare.rb +44 -0
- data/lib/liebre/actor/context/handler.rb +44 -0
- data/lib/liebre/actor/publisher.rb +58 -0
- data/lib/liebre/actor/publisher/core.rb +42 -0
- data/lib/liebre/actor/publisher/reporter.rb +55 -0
- data/lib/liebre/actor/publisher/resources.rb +33 -0
- data/lib/liebre/actor/rpc/client.rb +88 -0
- data/lib/liebre/actor/rpc/client/core.rb +75 -0
- data/lib/liebre/actor/rpc/client/pending.rb +65 -0
- data/lib/liebre/actor/rpc/client/reporter.rb +71 -0
- data/lib/liebre/actor/rpc/client/resources.rb +62 -0
- data/lib/liebre/actor/rpc/client/task.rb +33 -0
- data/lib/liebre/actor/rpc/server.rb +74 -0
- data/lib/liebre/actor/rpc/server/callback.rb +28 -0
- data/lib/liebre/actor/rpc/server/core.rb +75 -0
- data/lib/liebre/actor/rpc/server/reporter.rb +72 -0
- data/lib/liebre/actor/rpc/server/resources.rb +53 -0
- data/lib/liebre/adapter.rb +8 -0
- data/lib/liebre/adapter/bunny.rb +23 -0
- data/lib/liebre/adapter/bunny/chan.rb +38 -0
- data/lib/liebre/adapter/bunny/conn.rb +32 -0
- data/lib/liebre/adapter/bunny/exchange.rb +20 -0
- data/lib/liebre/adapter/bunny/queue.rb +59 -0
- data/lib/liebre/adapter/interface.rb +26 -0
- data/lib/liebre/adapter/interface/chan.rb +29 -0
- data/lib/liebre/adapter/interface/conn.rb +21 -0
- data/lib/liebre/adapter/interface/exchange.rb +13 -0
- data/lib/liebre/adapter/interface/queue.rb +37 -0
- data/lib/liebre/bridge.rb +72 -0
- data/lib/liebre/bridge/channel_builder.rb +36 -0
- data/lib/liebre/config.rb +8 -38
- data/lib/liebre/engine.rb +61 -0
- data/lib/liebre/engine/builder.rb +48 -0
- data/lib/liebre/engine/repository.rb +56 -0
- data/lib/liebre/engine/state.rb +49 -0
- data/lib/liebre/runner.rb +15 -47
- data/lib/liebre/version.rb +1 -1
- data/liebre.gemspec +9 -7
- data/spec/integration/publish_and_consume_spec.rb +71 -0
- data/spec/integration/rpc_communication_spec.rb +81 -0
- data/spec/integration/start_twice_spec.rb +63 -0
- data/spec/liebre/actor/consumer_spec.rb +169 -0
- data/spec/liebre/actor/context/declare_spec.rb +69 -0
- data/spec/liebre/actor/context/handler_spec.rb +65 -0
- data/spec/liebre/actor/publisher_spec.rb +58 -0
- data/spec/liebre/actor/rpc/client_spec.rb +126 -0
- data/spec/liebre/actor/rpc/server_spec.rb +141 -0
- data/spec/liebre/adapter/bunny_spec.rb +66 -0
- data/spec/liebre/bridge_spec.rb +54 -0
- data/spec/liebre/engine/builder_spec.rb +42 -0
- data/spec/liebre/engine_spec.rb +90 -0
- data/spec/liebre/version_spec.rb +10 -0
- data/spec/spec_helper.rb +2 -9
- metadata +97 -58
- data/lib/liebre/common.rb +0 -7
- data/lib/liebre/common/utils.rb +0 -37
- data/lib/liebre/connection_manager.rb +0 -85
- data/lib/liebre/publisher.rb +0 -113
- data/lib/liebre/runner/consumers.rb +0 -46
- data/lib/liebre/runner/starter.rb +0 -44
- data/lib/liebre/runner/starter/consumer.rb +0 -129
- data/lib/liebre/runner/starter/consumer/handler.rb +0 -35
- data/lib/liebre/runner/starter/resources.rb +0 -45
- data/lib/liebre/runner/starter/resources/queue_builder.rb +0 -63
- data/lib/liebre/runner/starter/rpc.rb +0 -59
- data/lib/liebre/tasks.rb +0 -12
- data/spec/config/liebre.yml +0 -48
- data/spec/config/rabbitmq.yml +0 -35
- data/spec/integration_spec.rb +0 -76
- data/spec/liebre/config_spec.rb +0 -63
- data/spec/liebre/connection_manager_spec.rb +0 -44
- data/spec/liebre/publisher_spec.rb +0 -92
- data/spec/liebre/runner/consumers_spec.rb +0 -59
- data/spec/liebre/runner/starter/consumer_spec.rb +0 -145
- data/spec/liebre/runner/starter/resources/queue_builder_spec.rb +0 -69
- data/spec/liebre/runner/starter/resources_spec.rb +0 -38
- data/spec/liebre/runner/starter/rpc_spec.rb +0 -100
- data/spec/liebre/runner/starter_spec.rb +0 -70
- data/spec/liebre/runner_spec.rb +0 -54
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d8d00979bc470fd55b7dce291b96658266ea74ac
|
4
|
+
data.tar.gz: 48dff8d83158aa0a84f74a5aaac36e1f86de64b1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b4d5fe0344c0cecf9ff3313ff58521406d20b9f6df88aae242bcfc450860f8dabd1180a3f0ec11371297fa2a1792508d279fd7d1b4bbc03dfcafc303dc9631bf
|
7
|
+
data.tar.gz: 20f95bcd2593a5bf0ef664f242fb3d53a7d429d785b83106643eafec38afaee2e021a512f4deb4b442a3f03ecae3f3fae0c9e623e4df0bf6bd1306fdad3e3b2c
|
data/.rspec
ADDED
data/Gemfile.lock
CHANGED
@@ -1,28 +1,29 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
liebre (0.
|
5
|
-
|
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.
|
11
|
+
bunny (2.2.2)
|
12
12
|
amq-protocol (>= 2.0.1)
|
13
13
|
coderay (1.1.1)
|
14
|
-
|
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 (
|
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.
|
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.
|
49
|
+
1.14.6
|
data/{LICENSE → LICENSE.txt}
RENAMED
data/README.md
CHANGED
@@ -1,268 +1,565 @@
|
|
1
1
|
# Liebre
|
2
2
|
|
3
|
-
|
3
|
+
A library to interact with AMQP servers.
|
4
4
|
|
5
|
-
|
5
|
+
Liebre stands for hare in spanish.
|
6
6
|
|
7
|
-
|
7
|
+
## Installation
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
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
|
-
|
18
|
-
|
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
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
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
|
-
|
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
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
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
|
-
|
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
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
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
|
-
###
|
214
|
+
### Start partially
|
126
215
|
|
127
|
-
|
128
|
-
|
129
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
228
|
+
A solution to this case may be the following:
|
155
229
|
|
156
|
-
|
230
|
+
Start all publishers and consumers on an initialized that will be shared between
|
231
|
+
both ways to start the application:
|
157
232
|
|
158
|
-
|
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
|
165
|
-
|
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
|
-
|
254
|
+
This code will start all actors, and publishers and rpc clients will only be started
|
255
|
+
once.
|
170
256
|
|
171
|
-
|
257
|
+
## Actors
|
172
258
|
|
173
|
-
|
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
|
-
|
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
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
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
|
-
|
308
|
+
The exchange specification:
|
197
309
|
|
198
|
-
|
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
|
-
|
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
|
207
|
-
@
|
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
|
-
|
213
|
-
|
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
|
-
|
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
|
-
|
373
|
+
The `:resources` section of the actor's configuration requires the exchange and the queue,
|
374
|
+
and may include binding options.
|
222
375
|
|
223
|
-
|
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
|
-
|
407
|
+
The exchange specification is the same as for the publisher.
|
229
408
|
|
230
|
-
|
409
|
+
The queue specification:
|
231
410
|
|
232
|
-
|
411
|
+
* `:name` - The name of the queue
|
412
|
+
* `:opts` - (defaults to `{}`) The queue options
|
233
413
|
|
234
|
-
|
414
|
+
The bind specification is optional and defaults to `{}`. `:bind` value may be:
|
235
415
|
|
236
|
-
|
237
|
-
|
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
|
-
|
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
|
-
|
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
|
-
###
|
429
|
+
### RPC Client
|
246
430
|
|
247
|
-
|
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
|
-
|
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
|
-
|
259
|
-
|
260
|
-
|
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
|
-
|
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
|
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.
|