nsq-ruby-fastly 2.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ba51761d5abfe9acbb63eceac3e053b8323fd31cee0d03e165ad4263c63dc985
4
+ data.tar.gz: 26f1770323d24d83bd828cb2f0073472299d41b174177ce4bd351bcf5b876626
5
+ SHA512:
6
+ metadata.gz: fe327495f3c7487d679257c73b020ecbbbc64c91d6c3a36caff79c23bcc171cb4c898d1675b011d717c34c19b3d5f7226f81408b9af09931dac6b90bec85a861
7
+ data.tar.gz: 87eb4b1748c1c068b53df2e53befdd31bf2894f906c6273bc5576e99f1febf54f54e174a174ea3f4cefc65e2b4129cee09cd87c37014087afba6f45a4826c92b
data/CHANGELOG.md ADDED
@@ -0,0 +1,42 @@
1
+ # Changelog
2
+
3
+ ## 2.3.1
4
+
5
+ * Fix `max_attempts` bug (#46)
6
+
7
+ ## 2.3.0
8
+
9
+ * Add support for `max_attempts` (#43)
10
+
11
+ ## 2.2.0
12
+
13
+ * Fix memory leak in producer by using a limited `SizedQueue` (#42)
14
+
15
+ ## 2.1.0
16
+
17
+ * Now compatible with NSQ 1.0 thanks to @pacoguzman (#37).
18
+
19
+ ## 2.0.5
20
+
21
+ * Bugfix: Do not register `at_exit` handlers for consumers and producers to avoid memory leaks (#34)
22
+
23
+ ## 2.0.4
24
+
25
+ * Bugfix: Close connection socket when calling `.terminate`.
26
+
27
+ ## 2.0.3
28
+
29
+ * Bugfix: Ensure write_loop ends; pass message via write_queue (#32)
30
+
31
+ ## 2.0.2
32
+
33
+ * Bugfix: Use `File.read` instead of `File.open` to avoid leaving open file handles around.
34
+
35
+ ## 2.0.1
36
+
37
+ * Bugfix: Allow discovery of ephemeral topics when using JRuby.
38
+
39
+ ## 2.0.0
40
+
41
+ * Enable TLS connections without any client-side keys, certificates or certificate authorities.
42
+ * Deprecate `ssl_context` connection setting, rename it to `tls_options` moving forward.
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,421 @@
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:** Internally, we use a `SizedQueue` that can hold 10,000 messages. If you're
143
+ producing messages faster than we're able to send them to nsqd or nsqd is
144
+ offline for an extended period and you accumulate 10,000 messages in the queue,
145
+ calls to `#write` will block until there's room in the queue.
146
+
147
+ **Note:** We don't wait for nsqd to acknowledge our writes. As a result, if the
148
+ connection to nsqd fails, you can lose messages. This is acceptable for our use
149
+ cases, mostly because we are sending messages to a local nsqd instance and
150
+ failure is very rare.
151
+
152
+
153
+ ### `#write_to_topic`
154
+
155
+ Publishes one or more messages to nsqd. Like `#write`, but allows you to specify
156
+ the topic. Use this method if you want a single producer instance to write to
157
+ multiple topics.
158
+
159
+ ```Ruby
160
+ # Send a single message via PUB to the topic 'rutabega'
161
+ producer.write_to_topic('rutabega', 123)
162
+
163
+ # Send multiple messages via MPUB to the topic 'kohlrabi'
164
+ producer.write_to_topic('kohlrabi', 'a', 'b', 'c')
165
+ ```
166
+
167
+
168
+ ### `#connected?`
169
+
170
+ Returns true if it's currently connected to nsqd and false if not.
171
+
172
+ ### `#terminate`
173
+
174
+ Closes the connection to nsqd and stops it from trying to automatically
175
+ reconnect.
176
+
177
+ This is automatically called `at_exit`, but it's good practice to close your
178
+ producers when you're done with them.
179
+
180
+ **Note:** This terminates the connection to NSQ immediately. If you're writing
181
+ messages faster than they can be sent to NSQ, you may have messages in the
182
+ producer's internal queue. Calling `#terminate` before they're sent will cause
183
+ these messages to be lost. After you write your last message, consider sleeping
184
+ for a second before you call `#terminate`.
185
+
186
+
187
+
188
+
189
+ ## Consumer
190
+
191
+ ### Initialization
192
+
193
+ | Option | Description | Default |
194
+ |----------------------|-----------------------------------------------|--------------------|
195
+ | `topic` | Topic to consume messages from | |
196
+ | `channel` | Channel name for this consumer | |
197
+ | `nsqlookupd` | Use lookupd to automatically discover nsqds | |
198
+ | `nsqd` | Connect directly to a single nsqd instance | '127.0.0.1:4150' |
199
+ | `max_in_flight` | Max number of messages for this consumer to have in flight at a time | 1 |
200
+ | `discovery_interval` | Seconds between queue discovery via nsqlookupd | 60.0 |
201
+ | `msg_timeout` | Milliseconds before nsqd will timeout a message | 60000 |
202
+ | `max_attempts` | Number of times a message will be attempted before being finished | |
203
+ | `tls_v1` | Flag for tls v1 connections | false |
204
+ | `tls_options` | Optional keys and certificates for TLS connections | |
205
+
206
+
207
+ For example:
208
+
209
+ ```Ruby
210
+ consumer = Nsq::Consumer.new(
211
+ topic: 'the-topic',
212
+ channel: 'my-channel',
213
+ nsqlookupd: ['127.0.0.1:4161', '4.5.6.7:4161'],
214
+ max_in_flight: 100,
215
+ discovery_interval: 30,
216
+ msg_timeout: 120_000,
217
+ max_attempts: 10,
218
+ tls_v1: true,
219
+ tls_options: {
220
+ key: '/path/to/ssl/key.pem',
221
+ certificate: '/path/to/ssl/certificate.pem',
222
+ ca_certificate: '/path/to/ssl/ca_certificate.pem',
223
+ verify_mode: OpenSSL::SSL::VERIFY_PEER
224
+ }
225
+ )
226
+ ```
227
+
228
+ Notes:
229
+
230
+ - `nsqlookupd` can be a string or array of strings for each nsqlookupd service
231
+ you'd like to use. The format is `"<host>:<http-port>"`. If you specify
232
+ `nsqlookupd`, it ignores the `nsqd` option.
233
+ - `max_in_flight` is for the total max in flight across all the connections,
234
+ but to make the implementation of `nsq-ruby` as simple as possible, the minimum
235
+ `max_in_flight` _per_ connection is 1. So if you set `max_in_flight` to 1 and
236
+ are connected to 3 nsqds, you may have up to 3 messages in flight at a time.
237
+ - `max_attempts` is optional and if not set messages will be attempted until they
238
+ are explicitly finshed.
239
+
240
+ ### `#pop`
241
+
242
+ `nsq-ruby` works by maintaining a local queue of in flight messages from NSQ.
243
+ To get at these messages, just call pop.
244
+
245
+ ```Ruby
246
+ message = consumer.pop
247
+ ```
248
+
249
+ If there are messages on the queue, `pop` will return one immediately. If there
250
+ are no messages on the queue, `pop` will block execution until one arrives.
251
+
252
+ Be aware, while `#pop` is blocking, your process will be unresponsive. This
253
+ can be a problem in certain cases, like if you're trying to gracefully restart
254
+ a worker process by sending it a `TERM` signal. See `#pop_without_blocking` for
255
+ information on how to mitigate this issue.
256
+
257
+
258
+ ### `#pop_without_blocking`
259
+
260
+ This is just like `#pop` except it doesn't block. It always returns immediately.
261
+ If there are no messages in the queue, it will return `nil`.
262
+
263
+ If you're consuming from a low-volume topic and don't want to get stuck in a
264
+ blocking state, you can use this method to consume messages like so:
265
+
266
+ ```Ruby
267
+ loop do
268
+ if msg = @messages.pop_without_blocking
269
+ # do something
270
+ msg.finish
271
+ else
272
+ # wait for a bit before checking for new messages
273
+ sleep 0.01
274
+ end
275
+ end
276
+ ```
277
+
278
+
279
+ ### `#size`
280
+
281
+ `size` returns the size of the local message queue.
282
+
283
+
284
+ ### `#terminate`
285
+
286
+ Gracefully closes all connections and stops the consumer. You should call this
287
+ when you're finished with a consumer object.
288
+
289
+
290
+ ## Message
291
+
292
+ The `Message` object is what you get when you call `pop` on a consumer.
293
+ Once you have a message, you'll likely want to get its contents using the `#body`
294
+ method, and then call `#finish` once you're done with it.
295
+
296
+ ### `body`
297
+
298
+ Returns the body of the message as a UTF-8 encoded string.
299
+
300
+ ### `attempts`
301
+
302
+ Returns the number of times this message was attempted to be processed. For
303
+ most messages this should be 1 (since it will be your first attempt processing
304
+ them). If it's more than 1, that means that you requeued the message or it
305
+ timed out in flight.
306
+
307
+ ### `timestamp`
308
+
309
+ Returns the time this message was originally sent to NSQ as a `Time` object.
310
+
311
+ ### `#finish`
312
+
313
+ Notify NSQ that you've completed processing of this message.
314
+
315
+ ### `#touch`
316
+
317
+ Tells NSQ to reset the message timeout for this message so you have more time
318
+ to process it.
319
+
320
+ ### `#requeue(timeout = 0)`
321
+
322
+ Tells NSQ to requeue this message. Called with no arguments, this will requeue
323
+ the message and it will be available to be received immediately.
324
+
325
+ Optionally you can pass a number of milliseconds as an argument. This tells
326
+ NSQ to delay its requeueing by that number of milliseconds.
327
+
328
+
329
+ ## Logging
330
+
331
+ By default, `nsq-ruby` doesn't log anything. To enable logging, use
332
+ `Nsq.logger=` and point it at a Ruby Logger instance. Like this:
333
+
334
+ ```Ruby
335
+ Nsq.logger = Logger.new(STDOUT)
336
+ ```
337
+
338
+
339
+ ## Requirements
340
+
341
+ NSQ v0.2.29 or later for IDENTIFY metadata specification (0.2.28) and per-
342
+ connection timeout support (0.2.29).
343
+
344
+
345
+ ### Supports
346
+
347
+ - Discovery via nsqlookupd
348
+ - Automatic reconnection to nsqd
349
+ - Producing to all nsqd instances automatically via nsqlookupd
350
+ - TLS
351
+
352
+
353
+ ### Does not support
354
+
355
+ - Compression
356
+ - Backoff
357
+
358
+ If you need more advanced features, like these, you should check out
359
+ [Krakow](https://github.com/chrisroberts/krakow), a more fully featured NSQ
360
+ client for Ruby.
361
+
362
+
363
+ ## Testing
364
+
365
+ Run the tests like this:
366
+
367
+ ```
368
+ rake spec
369
+ ```
370
+
371
+ Want a deluge of logging while running the specs to help determine what is
372
+ going on?
373
+
374
+ ```
375
+ VERBOSE=true rake spec
376
+ ```
377
+
378
+
379
+ ## Is this production ready?
380
+
381
+ Yes! It's used in several critical parts of our infrastructure at
382
+ [Wistia](http://wistia.com) and currently produces and consumes hundreds of
383
+ millions of messages a day.
384
+
385
+
386
+ ## Authors & Contributors
387
+
388
+ - Robby Grossman ([@freerobby](https://github.com/freerobby))
389
+ - Brendan Schwartz ([@bschwartz](https://github.com/bschwartz))
390
+ - Marshall Moutenot ([@mmoutenot](https://github.com/mmoutenot))
391
+ - Danielle Sucher ([@DanielleSucher](https://github.com/DanielleSucher))
392
+ - Anders Chen ([@chen-anders](https://github.com/chen-anders))
393
+ - Thomas O'Neil ([@alieander](https://github.com/alieander))
394
+ - Unbekandt Léo ([@soulou](https://github.com/Soulou))
395
+ - Matthias Schneider ([@mschneider82](https://github.com/mschneider82))
396
+ - Lukas Eklund ([@leklund](https://github.com/leklund))
397
+ - Paco Guzmán ([@pacoguzman](https://github.com/pacoguzman))
398
+
399
+
400
+ ## MIT License
401
+
402
+ Copyright (C) 2018 Wistia, Inc.
403
+
404
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
405
+ this software and associated documentation files (the "Software"), to deal in
406
+ the Software without restriction, including without limitation the rights to
407
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
408
+ of the Software, and to permit persons to whom the Software is furnished to do
409
+ so, subject to the following conditions:
410
+
411
+ The above copyright notice and this permission notice shall be included in all
412
+ copies or substantial portions of the Software.
413
+
414
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
415
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
416
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
417
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
418
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
419
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
420
+ SOFTWARE.
421
+
@@ -0,0 +1,120 @@
1
+ require_relative 'discovery'
2
+ require_relative 'connection'
3
+ require_relative 'logger'
4
+
5
+ module Nsq
6
+ class ClientBase
7
+ include Nsq::AttributeLogger
8
+ @@log_attributes = [:topic]
9
+
10
+ attr_reader :topic
11
+ attr_reader :connections
12
+
13
+ def connected?
14
+ @connections.values.any?(&:connected?)
15
+ end
16
+
17
+
18
+ def terminate
19
+ @discovery_thread.kill if @discovery_thread
20
+ drop_all_connections
21
+ end
22
+
23
+
24
+ private
25
+
26
+ # discovers nsqds from an nsqlookupd repeatedly
27
+ #
28
+ # opts:
29
+ # nsqlookups: ['127.0.0.1:4161'],
30
+ # topic: 'topic-to-find-nsqds-for',
31
+ # interval: 60
32
+ #
33
+ def discover_repeatedly(opts = {})
34
+ @discovery_thread = Thread.new do
35
+
36
+ @discovery = Discovery.new(opts[:nsqlookupds])
37
+
38
+ loop do
39
+ begin
40
+ nsqds = nsqds_from_lookupd(opts[:topic])
41
+ drop_and_add_connections(nsqds)
42
+ rescue DiscoveryException
43
+ # We can't connect to any nsqlookupds. That's okay, we'll just
44
+ # leave our current nsqd connections alone and try again later.
45
+ warn 'Could not connect to any nsqlookupd instances in discovery loop'
46
+ end
47
+ sleep opts[:interval]
48
+ end
49
+
50
+ end
51
+
52
+ @discovery_thread.abort_on_exception = true
53
+ end
54
+
55
+
56
+ def nsqds_from_lookupd(topic = nil)
57
+ if topic
58
+ @discovery.nsqds_for_topic(topic)
59
+ else
60
+ @discovery.nsqds
61
+ end
62
+ end
63
+
64
+
65
+ def drop_and_add_connections(nsqds)
66
+ # drop nsqd connections that are no longer in lookupd
67
+ missing_nsqds = @connections.keys - nsqds
68
+ missing_nsqds.each do |nsqd|
69
+ drop_connection(nsqd)
70
+ end
71
+
72
+ # add new ones
73
+ new_nsqds = nsqds - @connections.keys
74
+ new_nsqds.each do |nsqd|
75
+ begin
76
+ add_connection(nsqd)
77
+ rescue Exception => ex
78
+ error "Failed to connect to nsqd @ #{nsqd}: #{ex}"
79
+ end
80
+ end
81
+
82
+ # balance RDY state amongst the connections
83
+ connections_changed
84
+ end
85
+
86
+
87
+ def add_connection(nsqd, options = {})
88
+ info "+ Adding connection #{nsqd}"
89
+ host, port = nsqd.split(':')
90
+ connection = Connection.new({
91
+ host: host,
92
+ port: port,
93
+ ssl_context: @ssl_context,
94
+ tls_options: @tls_options,
95
+ tls_v1: @tls_v1
96
+ }.merge(options))
97
+ @connections[nsqd] = connection
98
+ end
99
+
100
+
101
+ def drop_connection(nsqd)
102
+ info "- Dropping connection #{nsqd}"
103
+ connection = @connections.delete(nsqd)
104
+ connection.close if connection
105
+ connections_changed
106
+ end
107
+
108
+
109
+ def drop_all_connections
110
+ @connections.keys.each do |nsqd|
111
+ drop_connection(nsqd)
112
+ end
113
+ end
114
+
115
+
116
+ # optional subclass hook
117
+ def connections_changed
118
+ end
119
+ end
120
+ end