nsq-ruby-fastly 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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