UNPKG

9.26 kBMarkdownView Raw
1# Messaging API supporting acknowledgements and request-response
2
3[![Build Status](http://ci.salemove.com/buildStatus/icon?job=freddy)](http://ci.salemove.com/job/freddy/)
4[![Code Climate](https://codeclimate.com/repos/52a1f75613d6374c030432d2/badges/f8f96e50aa9f57dfae00/gpa.png)](https://codeclimate.com/repos/52a1f75613d6374c030432d2/feed)
5
6## Usage
7
8### Ruby
9
10#### Setup
11
12* Inject the appropriate default logger and set up connection parameters:
13
14```ruby
15Freddy.setup(Logger.new(STDOUT), host: 'localhost', port: 5672, user: 'guest', pass: 'guest')
16```
17
18* Use Freddy to deliver and respond to messages:
19
20```ruby
21freddy = Freddy.new(logger = Freddy.logger)
22```
23
24 * by default the Freddy instance will reuse connections and queues for messaging, if you want to use a distinct tcp connection, response queue and timeout checking thread, then use
25
26```ruby
27freddy.use_distinct_connection
28```
29
30#### Destinations
31Freddy encourages but doesn't enforce the following protocol for destinations:
32
33* For sending messages to services:
34
35```
36<service_name>.<method_name>.<anything_else_you_need>.<...>
37```
38
39* For reporting errors:
40
41```
42<service_name>.<method_name>.'responder'|'producer'.'errors'
43```
44
45TODO: Finish speccing protocol.
46
47#### Delivering messages
48
49* Simply deliver a message:
50```ruby
51freddy.deliver(destination, message)
52```
53 * destination is the recipient of the message
54 * message is the contents of the message
55
56* Deliver a message expecting explicit acknowledgement
57```ruby
58freddy.deliver_with_ack(destination, message, timeout_seconds = 3) do |error|
59```
60
61 * If timeout_seconds pass without a response from the responder, then the callback is called with a timeout error.
62
63 * callback is called with one argument: a string that contains an error message if
64 * the message couldn't be sent to any responders or
65 * the responder negatively acknowledged(nacked) the message or
66 * the responder finished working but didn't positively acknowledge the message
67
68 * callback is called with one argument that is nil if the responder positively acknowledged the message
69 * note that the callback will not be called in the case that there is a responder who receives the message, but the responder doesn't finish processing the message or dies in the process.
70
71* Deliver expecting a response
72```ruby
73freddy.deliver_with_response(destination, message, timeout_seconds = 3) do |response, msg_handler|
74```
75
76 * If `timeout_seconds pass` without a response from the responder then the callback is called with the hash
77```ruby
78{ error: 'Timed out waiting for response' }
79```
80
81 * Callback is called with 2 arguments
82
83 * The parsed response
84
85 * The `MessageHandler`(described further down)
86
87* Synchronous deliver expecting response
88```ruby
89 response = freddy.deliver_with_response(destination, message, timeout_seconds = 3)
90```
91
92#### Responding to messages
93
94* Respond to messages while not blocking the current thread:
95```ruby
96freddy.respond_to destination do |message, msg_handler|
97```
98* Respond to message and block the thread
99```ruby
100freddy.respond_to_and_block destination do |message, msg_handler|
101```
102
103* The callback is called with 2 arguments
104
105 * the parsed message (note that in the message all keys are symbolized)
106 * the `MessageHandler` (described further down)
107
108#### The MessageHandler
109
110When responding to messages the MessageHandler is given as the second argument.
111```ruby
112freddy.respond_to destination do |message, msg_handler|
113```
114
115The following operations are supported:
116
117 * acknowledging the message
118```ruby
119msg_handler.ack(response = nil)
120```
121
122 * when the message was produced with `produce_with_response`, then the response is sent to the original producer
123
124 * when the message was produced with `produce_with_ack`, then only a positive acknowledgement is sent, the provided response is dicarded
125
126 * negatively acknowledging the message
127```ruby
128msg_handler.nack(error = "Couldn't process message")
129```
130
131 * when the message was produced with `produce_with_response`, then the following hash is sent to the original producer
132```ruby
133{ error: error }
134```
135
136 * when the message was produced with `produce_with_ack`, then the error (e.g negative acknowledgement) is sent to the original producer
137
138 * Getting additional properties of the message (shouldn't be necessary under normal circumstances)
139```ruby
140msg_handler.properties
141```
142
143#### Tapping into messages
144When it's necessary to receive messages but not consume them, consider tapping.
145
146```ruby
147freddy.tap_into pattern do |message, destination|
148```
149
150* `destination` refers to the destination that the message was sent to
151* Note that it is not possible to acknowledge or respond to message while tapping.
152* When tapping the following wildcards are supported in the `pattern` :
153 * `#` matching 0 or more words
154 * `*` matching exactly one word
155
156Examples:
157
158```ruby
159freddy.tap_into "i.#.free"
160```
161
162receives messages that are delivered to `"i.want.to.break.free"`
163
164```ruby
165freddy.tap_into "somebody.*.love"
166```
167
168receives messages that are delivered to `somebody.to.love` but doesn't receive messages delivered to `someboy.not.to.love`
169
170It is also possible to use the blocking version of `tap_into`:
171
172```ruby
173freddy.tap_into_and_block pattern, &callback do |message, destination|
174```
175
176#### The ResponderHandler
177
178When responding to a message or tapping the ResponderHandler is returned.
179```ruby
180responder_handler = freddy.respond_to ....
181```
182
183The following operations are supported:
184
185 * stop responding
186```ruby
187responder_handler.cancel
188```
189
190 * join the current thread to the responder thread
191```ruby
192responder_handler.join
193```
194
195 * delete the destination
196```ruby
197responder_handler.destroy_destination
198```
199
200 * Primary use case is in tests to not leave dangling destinations. It deletes the destination even if there are responders for the same destination in other parts of the system. Use with caution in production code.
201
202
203#### Notes about concurrency
204
205The underlying bunny implementation uses 1 responder thread by default. This means that if there is a time-consuming process or a sleep call in a responder then other responders will not receive messages concurrently.
206
207This is especially devious when using `deliver_with_response` in a responder because `deliver_with_response` creates a new anonymous responder which will not receive the response if the parent responder uses a sleep call.
208
209To resolve this problem *freddy* uses 4 responder threads by default (configurable by `responder_thread_count`). Note that this means that ordered message processing is not guaranteed by default. Read more from <http://rubybunny.info/articles/concurrency.html>.
210
211***
212
213### Node.js
214
215#### Setup
216```coffee
217freddy = new Freddy amqpUrl, logger
218```
219
220* amqpUrl defines the connection e.g `'amqp://guest:guest@localhost:5672'`
221
222* the message 'ready' is emitted when freddy is ready to deliver and respond to messages.
223
224#### Delivering messages
225```coffee
226freddy.deliver destination, message
227
228freddy.deliverWithAck destination, message, callback
229
230freddy.deliverWithResponse destination, message, callback
231```
232
233* The default timeout is 3 seconds, to use a custom timeout use
234
235```coffee
236freddy.withTimeout(myTimeoutInSeconds).deliverWithAck...
237
238freddy.withTimeout(myTimeoutInSeconds).deliverWithResponse...
239```
240
241#### Responding to messages
242```coffee
243freddy.respondTo destination, callback
244```
245
246* Sometimes it might be useful to know when the queue has been created for the responder. For that the 'ready' is emitted on the responderHandler.
247
248```coffee
249responderHandler = freddy.respondTo destination, callback
250responderHandler.on 'ready', () =>
251 freddy.deliver destination, {easy: 'come'}
252```
253
254#### The MessageHandler
255No differences to ruby spec
256
257#### Tapping into messages
258
259```coffee
260responderHandler = freddy.tapInto pattern, callback
261```
262
263No other differences to ruby spec, blocking variant is not provided for obvious reasons.
264
265#### The ResponderHandler
266
267* When cancelling the responder `cancelled` is emitted on the responderHandler when the responder was successfully cancelled. After that the responder will not receive any new messages.
268
269```coffee
270responderHandler = freddy.respondTo destination, () =>
271responderHandler.cancel()
272responderHandler.on 'cancelled', () =>
273 freddy.deliver destination, {easy: 'go'} #will not be received
274```
275* The join method is not provided for obvious reasons.
276
277## Development
278
279* Use RSpec and mocha, make sure the tests pass.
280* Don't leak underlying messaging protocol internals.
281
282## Credits
283
284**freddy** was originally written by [Urmas Talimaa] as part of SaleMove development team.
285
286![SaleMove Inc. 2012][SaleMove Logo]
287
288**freddy** is maintained and funded by [SaleMove, Inc].
289
290The names and logos for **SaleMove** are trademarks of SaleMove, Inc.
291
292## License
293
294**freddy** is Copyright © 2013 SaleMove Inc. It is free software, and may be redistributed under the terms specified in the [Apache License].
295
296[Urmas Talimaa]: https://github.com/urmastalimaa?source=c "Urmas"
297[SaleMove, Inc]: http://salemove.com/ "SaleMove Website"
298[SaleMove Logo]: http://app.salemove.com/assets/logo.png "SaleMove Inc. 2012"
299[Apache License]: http://choosealicense.com/licenses/apache/ "Apache License"