georgi-nsq-ruby 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +405 -0
- data/lib/nsq.rb +13 -0
- data/lib/nsq/client_base.rb +120 -0
- data/lib/nsq/connection.rb +461 -0
- data/lib/nsq/consumer.rb +98 -0
- data/lib/nsq/discovery.rb +98 -0
- data/lib/nsq/exceptions.rb +5 -0
- data/lib/nsq/frames/error.rb +6 -0
- data/lib/nsq/frames/frame.rb +16 -0
- data/lib/nsq/frames/message.rb +33 -0
- data/lib/nsq/frames/response.rb +6 -0
- data/lib/nsq/logger.rb +38 -0
- data/lib/nsq/producer.rb +94 -0
- data/lib/version.rb +9 -0
- metadata +116 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: '0592446085ea7e5ee98b2823b84159594c70802d'
|
4
|
+
data.tar.gz: 4f64b4526e26ed2cb194222b9b6046a3063bb366
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f689bef377f060f4b9407750a05be8385df077baf064f2be8e650e955217ed84f7a4427d34126b2f4a31a489cf23fca8f853f0dcd7461eabcb003f34f13bcb53
|
7
|
+
data.tar.gz: 93b240b8475c2bed9312371b3ecc85e98d1c36f017c31e2200ab6d8005d5a65190ab93ff2ccec1822e879591b5b4a20342d5940d68d69bc0834dd64c1f0f7b73
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Wistia, Inc.
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,405 @@
|
|
1
|
+
# nsq-ruby
|
2
|
+
|
3
|
+
nsq-ruby is a simple NSQ client library written in Ruby.
|
4
|
+
|
5
|
+
- The code is straightforward.
|
6
|
+
- It has no dependencies.
|
7
|
+
- It's well tested.
|
8
|
+
- It's being used in production and has processed billions of messages.
|
9
|
+
|
10
|
+
[![Build Status](https://travis-ci.org/wistia/nsq-ruby.svg?branch=master)](https://travis-ci.org/wistia/nsq-ruby)
|
11
|
+
|
12
|
+
|
13
|
+
## Quick start
|
14
|
+
|
15
|
+
### Publish messages
|
16
|
+
|
17
|
+
```Ruby
|
18
|
+
require 'nsq'
|
19
|
+
producer = Nsq::Producer.new(
|
20
|
+
nsqd: '127.0.0.1:4150',
|
21
|
+
topic: 'some-topic'
|
22
|
+
)
|
23
|
+
|
24
|
+
# Write a message to NSQ
|
25
|
+
producer.write('some-message')
|
26
|
+
|
27
|
+
# Write a bunch of messages to NSQ (uses mpub)
|
28
|
+
producer.write('one', 'two', 'three', 'four', 'five')
|
29
|
+
|
30
|
+
# Write a deferred message to NSQ (uses dpub)
|
31
|
+
|
32
|
+
# Message deferred of 10s
|
33
|
+
producer.deferred_write(10, 'one')
|
34
|
+
|
35
|
+
# Message deferred of 1250ms
|
36
|
+
producer.deferred_write(1.25, 'one')
|
37
|
+
|
38
|
+
# Close the connection
|
39
|
+
producer.terminate
|
40
|
+
```
|
41
|
+
|
42
|
+
### Consume messages
|
43
|
+
|
44
|
+
```Ruby
|
45
|
+
require 'nsq'
|
46
|
+
consumer = Nsq::Consumer.new(
|
47
|
+
nsqlookupd: '127.0.0.1:4161',
|
48
|
+
topic: 'some-topic',
|
49
|
+
channel: 'some-channel'
|
50
|
+
)
|
51
|
+
|
52
|
+
# Pop a message off the queue
|
53
|
+
msg = consumer.pop
|
54
|
+
puts msg.body
|
55
|
+
msg.finish
|
56
|
+
|
57
|
+
# Close the connections
|
58
|
+
consumer.terminate
|
59
|
+
```
|
60
|
+
|
61
|
+
|
62
|
+
## Producer
|
63
|
+
|
64
|
+
### Initialization
|
65
|
+
|
66
|
+
The Nsq::Producer constructor takes the following options:
|
67
|
+
|
68
|
+
| Option | Description | Default |
|
69
|
+
|---------------|----------------------------------------|--------------------|
|
70
|
+
| `topic` | Topic to which to publish messages | |
|
71
|
+
| `nsqd` | Host and port of the nsqd instance | '127.0.0.1:4150' |
|
72
|
+
| `nsqlookupd` | Use lookupd to auto discover nsqds | |
|
73
|
+
| `tls_v1` | Flag for tls v1 connections | false |
|
74
|
+
| `tls_options` | Optional keys+certs for TLS connections| |
|
75
|
+
|
76
|
+
For example, if you'd like to publish messages to a single nsqd.
|
77
|
+
|
78
|
+
```Ruby
|
79
|
+
producer = Nsq::Producer.new(
|
80
|
+
nsqd: '6.7.8.9:4150',
|
81
|
+
topic: 'topic-of-great-esteem'
|
82
|
+
)
|
83
|
+
```
|
84
|
+
|
85
|
+
Alternatively, you can use nsqlookupd to find all nsqd nodes in the cluster.
|
86
|
+
When you instantiate Nsq::Producer in this way, it will automatically maintain
|
87
|
+
connections to all nsqd instances. When you publish a message, it will be sent
|
88
|
+
to a random nsqd instance.
|
89
|
+
|
90
|
+
```Ruby
|
91
|
+
producer = Nsq::Producer.new(
|
92
|
+
nsqlookupd: ['1.2.3.4:4161', '6.7.8.9:4161'],
|
93
|
+
topic: 'topic-of-great-esteem'
|
94
|
+
)
|
95
|
+
```
|
96
|
+
|
97
|
+
If you need to connect using SSL/TLS Authentication via `tls_options`
|
98
|
+
|
99
|
+
```Ruby
|
100
|
+
producer = Nsq::Producer.new(
|
101
|
+
nsqlookupd: ['1.2.3.4:4161', '6.7.8.9:4161'],
|
102
|
+
topic: 'topic-of-great-esteem',
|
103
|
+
tls_v1: true,
|
104
|
+
tls_options: {
|
105
|
+
key: '/path/to/ssl/key.pem',
|
106
|
+
certificate: '/path/to/ssl/certificate.pem',
|
107
|
+
ca_certificate: '/path/to/ssl/ca_certificate.pem',
|
108
|
+
verify_mode: OpenSSL::SSL::VERIFY_PEER
|
109
|
+
}
|
110
|
+
)
|
111
|
+
```
|
112
|
+
|
113
|
+
If you need to connect using simple `tls_v1`
|
114
|
+
|
115
|
+
```Ruby
|
116
|
+
producer = Nsq::Producer.new(
|
117
|
+
nsqlookupd: ['1.2.3.4:4161', '6.7.8.9:4161'],
|
118
|
+
topic: 'topic-of-great-esteem',
|
119
|
+
tls_v1: true
|
120
|
+
)
|
121
|
+
```
|
122
|
+
|
123
|
+
### `#write`
|
124
|
+
|
125
|
+
Publishes one or more messages to nsqd. If you give it a single argument, it will
|
126
|
+
send it to nsqd via `PUB`. If you give it multiple arguments, it will send all
|
127
|
+
those messages to nsqd via `MPUB`. It will automatically call `to_s` on any
|
128
|
+
arguments you give it.
|
129
|
+
|
130
|
+
```Ruby
|
131
|
+
# Send a single message via PUB
|
132
|
+
producer.write(123)
|
133
|
+
|
134
|
+
# Send three messages via MPUB
|
135
|
+
producer.write(456, 'another-message', { key: 'value' }.to_json)
|
136
|
+
```
|
137
|
+
|
138
|
+
If its connection to nsqd fails, it will automatically try to reconnect with
|
139
|
+
exponential backoff. Any messages that were sent to `#write` will be queued
|
140
|
+
and transmitted after reconnecting.
|
141
|
+
|
142
|
+
**Note** we don't wait for nsqd to acknowledge our writes. As a result, if the
|
143
|
+
connection to nsqd fails, you can lose messages. This is acceptable for our use
|
144
|
+
cases, mostly because we are sending messages to a local nsqd instance and
|
145
|
+
failure is very rare.
|
146
|
+
|
147
|
+
|
148
|
+
### `#write_to_topic`
|
149
|
+
|
150
|
+
Publishes one or more messages to nsqd. Like `#write`, but allows you to specify
|
151
|
+
the topic. Use this method if you want a single producer instance to write to
|
152
|
+
multiple topics.
|
153
|
+
|
154
|
+
```Ruby
|
155
|
+
# Send a single message via PUB to the topic 'rutabega'
|
156
|
+
producer.write_to_topic('rutabega', 123)
|
157
|
+
|
158
|
+
# Send multiple messages via MPUB to the topic 'kohlrabi'
|
159
|
+
producer.write_to_topic('kohlrabi', 'a', 'b', 'c')
|
160
|
+
```
|
161
|
+
|
162
|
+
|
163
|
+
### `#connected?`
|
164
|
+
|
165
|
+
Returns true if it's currently connected to nsqd and false if not.
|
166
|
+
|
167
|
+
### `#terminate`
|
168
|
+
|
169
|
+
Closes the connection to nsqd and stops it from trying to automatically
|
170
|
+
reconnect.
|
171
|
+
|
172
|
+
This is automatically called `at_exit`, but it's good practice to close your
|
173
|
+
producers when you're done with them.
|
174
|
+
|
175
|
+
|
176
|
+
## Consumer
|
177
|
+
|
178
|
+
### Initialization
|
179
|
+
|
180
|
+
| Option | Description | Default |
|
181
|
+
|----------------------|-----------------------------------------------|--------------------|
|
182
|
+
| `topic` | Topic to consume messages from | |
|
183
|
+
| `channel` | Channel name for this consumer | |
|
184
|
+
| `nsqlookupd` | Use lookupd to automatically discover nsqds | |
|
185
|
+
| `nsqd` | Connect directly to a single nsqd instance | '127.0.0.1:4150' |
|
186
|
+
| `max_in_flight` | Max number of messages for this consumer to have in flight at a time | 1 |
|
187
|
+
| `discovery_interval` | Seconds between queue discovery via nsqlookupd | 60.0 |
|
188
|
+
| `msg_timeout` | Milliseconds before nsqd will timeout a message | 60000 |
|
189
|
+
| `tls_v1` | Flag for tls v1 connections | false |
|
190
|
+
| `tls_options` | Optional keys and certificates for TLS connections | |
|
191
|
+
|
192
|
+
|
193
|
+
For example:
|
194
|
+
|
195
|
+
```Ruby
|
196
|
+
consumer = Nsq::Consumer.new(
|
197
|
+
topic: 'the-topic',
|
198
|
+
channel: 'my-channel',
|
199
|
+
nsqlookupd: ['127.0.0.1:4161', '4.5.6.7:4161'],
|
200
|
+
max_in_flight: 100,
|
201
|
+
discovery_interval: 30,
|
202
|
+
msg_timeout: 120_000,
|
203
|
+
tls_v1: true,
|
204
|
+
tls_options: {
|
205
|
+
key: '/path/to/ssl/key.pem',
|
206
|
+
certificate: '/path/to/ssl/certificate.pem',
|
207
|
+
ca_certificate: '/path/to/ssl/ca_certificate.pem',
|
208
|
+
verify_mode: OpenSSL::SSL::VERIFY_PEER
|
209
|
+
}
|
210
|
+
)
|
211
|
+
```
|
212
|
+
|
213
|
+
Notes:
|
214
|
+
|
215
|
+
- `nsqlookupd` can be a string or array of strings for each nsqlookupd service
|
216
|
+
you'd like to use. The format is `"<host>:<http-port>"`. If you specify
|
217
|
+
`nsqlookupd`, it ignores the `nsqd` option.
|
218
|
+
- `max_in_flight` is for the total max in flight across all the connections,
|
219
|
+
but to make the implementation of `nsq-ruby` as simple as possible, the minimum
|
220
|
+
`max_in_flight` _per_ connection is 1. So if you set `max_in_flight` to 1 and
|
221
|
+
are connected to 3 nsqds, you may have up to 3 messages in flight at a time.
|
222
|
+
|
223
|
+
|
224
|
+
### `#pop`
|
225
|
+
|
226
|
+
`nsq-ruby` works by maintaining a local queue of in flight messages from NSQ.
|
227
|
+
To get at these messages, just call pop.
|
228
|
+
|
229
|
+
```Ruby
|
230
|
+
message = consumer.pop
|
231
|
+
```
|
232
|
+
|
233
|
+
If there are messages on the queue, `pop` will return one immediately. If there
|
234
|
+
are no messages on the queue, `pop` will block execution until one arrives.
|
235
|
+
|
236
|
+
Be aware, while `#pop` is blocking, your process will be unresponsive. This
|
237
|
+
can be a problem in certain cases, like if you're trying to gracefully restart
|
238
|
+
a worker process by sending it a `TERM` signal. See `#pop_without_blocking` for
|
239
|
+
information on how to mitigate this issue.
|
240
|
+
|
241
|
+
|
242
|
+
### `#pop_without_blocking`
|
243
|
+
|
244
|
+
This is just like `#pop` except it doesn't block. It always returns immediately.
|
245
|
+
If there are no messages in the queue, it will return `nil`.
|
246
|
+
|
247
|
+
If you're consuming from a low-volume topic and don't want to get stuck in a
|
248
|
+
blocking state, you can use this method to consume messages like so:
|
249
|
+
|
250
|
+
```Ruby
|
251
|
+
loop do
|
252
|
+
if msg = @messages.pop_without_blocking
|
253
|
+
# do something
|
254
|
+
msg.finish
|
255
|
+
else
|
256
|
+
# wait for a bit before checking for new messages
|
257
|
+
sleep 0.01
|
258
|
+
end
|
259
|
+
end
|
260
|
+
```
|
261
|
+
|
262
|
+
|
263
|
+
### `#size`
|
264
|
+
|
265
|
+
`size` returns the size of the local message queue.
|
266
|
+
|
267
|
+
|
268
|
+
### `#terminate`
|
269
|
+
|
270
|
+
Gracefully closes all connections and stops the consumer. You should call this
|
271
|
+
when you're finished with a consumer object.
|
272
|
+
|
273
|
+
|
274
|
+
## Message
|
275
|
+
|
276
|
+
The `Message` object is what you get when you call `pop` on a consumer.
|
277
|
+
Once you have a message, you'll likely want to get its contents using the `#body`
|
278
|
+
method, and then call `#finish` once you're done with it.
|
279
|
+
|
280
|
+
### `body`
|
281
|
+
|
282
|
+
Returns the body of the message as a UTF-8 encoded string.
|
283
|
+
|
284
|
+
### `attempts`
|
285
|
+
|
286
|
+
Returns the number of times this message was attempted to be processed. For
|
287
|
+
most messages this should be 1 (since it will be your first attempt processing
|
288
|
+
them). If it's more than 1, that means that you requeued the message or it
|
289
|
+
timed out in flight.
|
290
|
+
|
291
|
+
### `timestamp`
|
292
|
+
|
293
|
+
Returns the time this message was originally sent to NSQ as a `Time` object.
|
294
|
+
|
295
|
+
### `#finish`
|
296
|
+
|
297
|
+
Notify NSQ that you've completed processing of this message.
|
298
|
+
|
299
|
+
### `#touch`
|
300
|
+
|
301
|
+
Tells NSQ to reset the message timeout for this message so you have more time
|
302
|
+
to process it.
|
303
|
+
|
304
|
+
### `#requeue(timeout = 0)`
|
305
|
+
|
306
|
+
Tells NSQ to requeue this message. Called with no arguments, this will requeue
|
307
|
+
the message and it will be available to be received immediately.
|
308
|
+
|
309
|
+
Optionally you can pass a number of milliseconds as an argument. This tells
|
310
|
+
NSQ to delay its requeueing by that number of milliseconds.
|
311
|
+
|
312
|
+
|
313
|
+
## Logging
|
314
|
+
|
315
|
+
By default, `nsq-ruby` doesn't log anything. To enable logging, use
|
316
|
+
`Nsq.logger=` and point it at a Ruby Logger instance. Like this:
|
317
|
+
|
318
|
+
```Ruby
|
319
|
+
Nsq.logger = Logger.new(STDOUT)
|
320
|
+
```
|
321
|
+
|
322
|
+
|
323
|
+
## Requirements
|
324
|
+
|
325
|
+
NSQ v0.2.29 or later for IDENTIFY metadata specification (0.2.28) and per-
|
326
|
+
connection timeout support (0.2.29).
|
327
|
+
|
328
|
+
|
329
|
+
### Supports
|
330
|
+
|
331
|
+
- Discovery via nsqlookupd
|
332
|
+
- Automatic reconnection to nsqd
|
333
|
+
- Producing to all nsqd instances automatically via nsqlookupd
|
334
|
+
- TLS
|
335
|
+
|
336
|
+
|
337
|
+
### Does not support
|
338
|
+
|
339
|
+
- Compression
|
340
|
+
- Backoff
|
341
|
+
|
342
|
+
If you need more advanced features, like these, you should check out
|
343
|
+
[Krakow](https://github.com/chrisroberts/krakow), a more fully featured NSQ
|
344
|
+
client for Ruby.
|
345
|
+
|
346
|
+
|
347
|
+
## Testing
|
348
|
+
|
349
|
+
Run the tests like this:
|
350
|
+
|
351
|
+
```
|
352
|
+
rake spec
|
353
|
+
```
|
354
|
+
|
355
|
+
Want a deluge of logging while running the specs to help determine what is
|
356
|
+
going on?
|
357
|
+
|
358
|
+
```
|
359
|
+
VERBOSE=true rake spec
|
360
|
+
```
|
361
|
+
|
362
|
+
|
363
|
+
## Is this production ready?
|
364
|
+
|
365
|
+
Yes! It's used in several critical parts of our infrastructure at
|
366
|
+
[Wistia](http://wistia.com) and currently produces and consumes hundreds of
|
367
|
+
millions of messages a day.
|
368
|
+
|
369
|
+
|
370
|
+
## Authors & Contributors
|
371
|
+
|
372
|
+
- Robby Grossman ([@freerobby](https://github.com/freerobby))
|
373
|
+
- Brendan Schwartz ([@bschwartz](https://github.com/bschwartz))
|
374
|
+
- Marshall Moutenot ([@mmoutenot](https://github.com/mmoutenot))
|
375
|
+
- Danielle Sucher ([@DanielleSucher](https://github.com/DanielleSucher))
|
376
|
+
- Anders Chen ([@chen-anders](https://github.com/chen-anders))
|
377
|
+
- Thomas O'Neil ([@alieander](https://github.com/alieander))
|
378
|
+
- Unbekandt Léo ([@soulou](https://github.com/Soulou))
|
379
|
+
- Matthias Schneider ([@mschneider82](https://github.com/mschneider82))
|
380
|
+
- Lukas Eklund ([@leklund](https://github.com/leklund))
|
381
|
+
- Paco Guzmán ([@pacoguzman](https://github.com/pacoguzman))
|
382
|
+
|
383
|
+
|
384
|
+
## MIT License
|
385
|
+
|
386
|
+
Copyright (C) 2016 Wistia, Inc.
|
387
|
+
|
388
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
389
|
+
this software and associated documentation files (the "Software"), to deal in
|
390
|
+
the Software without restriction, including without limitation the rights to
|
391
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
392
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
393
|
+
so, subject to the following conditions:
|
394
|
+
|
395
|
+
The above copyright notice and this permission notice shall be included in all
|
396
|
+
copies or substantial portions of the Software.
|
397
|
+
|
398
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
399
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
400
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
401
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
402
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
403
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
404
|
+
SOFTWARE.
|
405
|
+
|
data/lib/nsq.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require_relative 'version'
|
2
|
+
|
3
|
+
require_relative 'nsq/logger'
|
4
|
+
|
5
|
+
require_relative 'nsq/exceptions'
|
6
|
+
|
7
|
+
require_relative 'nsq/frames/frame'
|
8
|
+
require_relative 'nsq/frames/error'
|
9
|
+
require_relative 'nsq/frames/response'
|
10
|
+
require_relative 'nsq/frames/message'
|
11
|
+
|
12
|
+
require_relative 'nsq/consumer'
|
13
|
+
require_relative 'nsq/producer'
|