nats 0.9.2 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 55e7d5acd3bc948f6238b7b294e5c7dfd03698e0173b677115bb2361a29db842
4
- data.tar.gz: a1d0c1d206407117fbe35604a9e2b4f0f5954fcda03af9dd23be2ed0dacdeb69
3
+ metadata.gz: 8caff8d7b6a0c87a312c9942174fe3c455e2cfc1c3abdb7c6baf7eb5db7445ef
4
+ data.tar.gz: 2dd5eb84f34dea3c6509c732b8da35d095af409fb955de312bc75c0b5306c7b0
5
5
  SHA512:
6
- metadata.gz: 5d11348d74d766bcf25169b3af693f4cd1bc5a2b1adb4d60919feb44718670c162cd93a0bf728e4f8624eb2c54ae952ab556f725a3e7254be91923df4b2b1986
7
- data.tar.gz: 29b9b7a9bd9152fa19491a9ac56670564549656102422b383887d919ad32268ce481f7cf2a74be07df232fa25d802fa1791f850503f82a40eab49b4e8c5dfdd7
6
+ metadata.gz: 2d8e2aa2738f152cc9f15bb1a5759714062535d55a6c449fc0a55e61b2cd59f45d37e39129fade9acf9cb0a82c2f5bf555f00fd82302e97ec9ec603ee2d2e493
7
+ data.tar.gz: '07828fd12c5c5a89eebfabaf8dae0315fa0bb0eb5dd013fee13282a63a5c037e04d2a072424fd97c266cabeb9d465ad3f79b66c4ace6c1280af85a4acf5da203'
data/README.md CHANGED
@@ -3,14 +3,14 @@
3
3
  A [Ruby](http://ruby-lang.org) client for the [NATS messaging system](https://nats.io).
4
4
 
5
5
  [![License Apache 2.0](https://img.shields.io/badge/License-Apache2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)
6
- [![Build Status](https://travis-ci.org/nats-io/ruby-nats.svg)](http://travis-ci.org/nats-io/ruby-nats) [![Gem Version](https://d25lcipzij17d.cloudfront.net/badge.svg?id=rb&type=5&v=0.9.2)](https://rubygems.org/gems/nats/versions/0.9.2) [![Yard Docs](http://img.shields.io/badge/yard-docs-blue.svg)](https://www.rubydoc.info/gems/nats)
6
+ [![Build Status](https://travis-ci.org/nats-io/ruby-nats.svg)](http://travis-ci.org/nats-io/ruby-nats) [![Gem Version](https://d25lcipzij17d.cloudfront.net/badge.svg?id=rb&type=5&v=0.10.0)](https://rubygems.org/gems/nats/versions/0.10.0) [![Yard Docs](http://img.shields.io/badge/yard-docs-blue.svg)](https://www.rubydoc.info/gems/nats)
7
7
 
8
8
  ## Supported Platforms
9
9
 
10
10
  This gem and the client are known to work on the following Ruby platforms:
11
11
 
12
- - MRI 2.2, 2.3.0, 2.4.0, 2.5.0
13
- - JRuby 9.1.2.0, 9.1.15.0
12
+ - MRI 2.3.0, 2.4.0, 2.5.0
13
+ - JRuby 9.1.2.0, 9.1.15.0, 9.2.0.0
14
14
 
15
15
  If you're looking for a non-EventMachine alternative, check out the [nats-pure](https://github.com/nats-io/pure-ruby-nats) gem.
16
16
 
@@ -108,11 +108,10 @@ end
108
108
 
109
109
  ### Auto discovery
110
110
 
111
- Starting from release `0.8.0` of the gem, the client also auto
112
- discovers new nodes announced by the server as they attach to the
113
- cluster. Reconnection logic parameters such as time to back-off on
114
- failure and max attempts apply the same to both discovered nodes and
115
- those defined explicitly on connect:
111
+ The client also auto discovers new nodes announced by the server as
112
+ they attach to the cluster. Reconnection logic parameters such as
113
+ time to back-off on failure and max attempts apply the same to both
114
+ discovered nodes and those defined explicitly on connect:
116
115
 
117
116
  ```ruby
118
117
  opts = {
@@ -149,7 +148,10 @@ NATS.unsubscribe(sid, MAX_WANTED)
149
148
  # Multiple connections
150
149
  NATS.subscribe('test') do |msg|
151
150
  puts "received msg"
152
- NATS.stop
151
+
152
+ # Gracefully disconnect from NATS after handling
153
+ # messages that have already been delivered by server.
154
+ NATS.drain
153
155
  end
154
156
 
155
157
  # Form second connection to send message on
@@ -42,6 +42,9 @@ module NATS
42
42
  DEFAULT_PING_INTERVAL = 120
43
43
  DEFAULT_PING_MAX = 2
44
44
 
45
+ # Drain mode support
46
+ DEFAULT_DRAIN_TIMEOUT = 30
47
+
45
48
  # Protocol
46
49
  # @private
47
50
  MSG = /\AMSG\s+([^\s]+)\s+([^\s]+)\s+(([^\s]+)[^\S\r\n]+)?(\d+)\r\n/i #:nodoc:
@@ -93,6 +96,7 @@ module NATS
93
96
  # Create and return a connection to the server with the given options.
94
97
  # The optional block will be called when the connection has been completed.
95
98
  #
99
+ # @param [String] uri The URI or comma separated list of URIs of NATS servers to connect to.
96
100
  # @param [Hash] opts
97
101
  # @option opts [String|URI] :uri The URI to connect to, example nats://localhost:4222
98
102
  # @option opts [Boolean] :reconnect Boolean that can be used to suppress reconnect functionality.
@@ -107,7 +111,50 @@ module NATS
107
111
  # @option opts [Integer] :max_outstanding_pings Integer that can be used to set the max number of outstanding pings before declaring a connection closed.
108
112
  # @param [Block] &blk called when the connection is completed. Connection will be passed to the block.
109
113
  # @return [NATS] connection to the server.
110
- def connect(opts={}, &blk)
114
+ #
115
+ # @example Connect to local NATS server.
116
+ # NATS.connect do |nc|
117
+ # # ...
118
+ # end
119
+ #
120
+ # @example Setting custom server URI to connect.
121
+ # NATS.connect("nats://localhost:4222") do |nc|
122
+ # # ...
123
+ # end
124
+ #
125
+ # @example Setting username and password to authenticate.
126
+ # NATS.connect("nats://user:password@localhost:4222") do |nc|
127
+ # # ...
128
+ # end
129
+ #
130
+ # @example Specifying explicit list of servers via options.
131
+ # NATS.connect(servers: ["nats://127.0.0.1:4222","nats://127.0.0.1:4223","nats://127.0.0.1:4224"]) do |nc|
132
+ # # ...
133
+ # end
134
+ #
135
+ # @example Using comma separated array to define list of servers.
136
+ # NATS.connect("nats://localhost:4223,nats://localhost:4224") do |nc|
137
+ # # ...
138
+ # end
139
+ #
140
+ # @example Only specifying endpoint uses NATS default scheme and port.
141
+ # NATS.connect("demo.nats.io") do |nc|
142
+ # # ...
143
+ # end
144
+ #
145
+ # @example Setting infinite reconnect retries with 2 seconds back off against custom URI.
146
+ # NATS.connect("demo.nats.io:4222", max_reconnect_attempts: -1, reconnect_time_wait: 2) do |nc|
147
+ # # ...
148
+ # end
149
+ #
150
+ def connect(uri=nil, opts={}, &blk)
151
+ case uri
152
+ when String
153
+ opts[:uri] = process_uri(uri)
154
+ when Hash
155
+ opts = uri
156
+ end
157
+
111
158
  # Defaults
112
159
  opts[:verbose] = false if opts[:verbose].nil?
113
160
  opts[:pedantic] = false if opts[:pedantic].nil?
@@ -117,6 +164,7 @@ module NATS
117
164
  opts[:reconnect_time_wait] = RECONNECT_TIME_WAIT if opts[:reconnect_time_wait].nil?
118
165
  opts[:ping_interval] = DEFAULT_PING_INTERVAL if opts[:ping_interval].nil?
119
166
  opts[:max_outstanding_pings] = DEFAULT_PING_MAX if opts[:max_outstanding_pings].nil?
167
+ opts[:drain_timeout] = DEFAULT_DRAIN_TIMEOUT if opts[:drain_timeout].nil?
120
168
 
121
169
  # Override with ENV
122
170
  opts[:uri] ||= ENV['NATS_URI'] || DEFAULT_URI
@@ -129,10 +177,10 @@ module NATS
129
177
  opts[:max_reconnect_attempts] = ENV['NATS_MAX_RECONNECT_ATTEMPTS'].to_i unless ENV['NATS_MAX_RECONNECT_ATTEMPTS'].nil?
130
178
  opts[:reconnect_time_wait] = ENV['NATS_RECONNECT_TIME_WAIT'].to_i unless ENV['NATS_RECONNECT_TIME_WAIT'].nil?
131
179
  opts[:name] ||= ENV['NATS_CONNECTION_NAME']
132
-
133
-
180
+ opts[:no_echo] ||= ENV['NATS_NO_ECHO'] || false
134
181
  opts[:ping_interval] = ENV['NATS_PING_INTERVAL'].to_i unless ENV['NATS_PING_INTERVAL'].nil?
135
182
  opts[:max_outstanding_pings] = ENV['NATS_MAX_OUTSTANDING_PINGS'].to_i unless ENV['NATS_MAX_OUTSTANDING_PINGS'].nil?
183
+ opts[:drain_timeout] ||= ENV['NATS_DRAIN_TIMEOUT'].to_i unless ENV['NATS_DRAIN_TIMEOUT'].nil?
136
184
 
137
185
  uri = opts[:uris] || opts[:servers] || opts[:uri]
138
186
 
@@ -211,6 +259,16 @@ module NATS
211
259
  @disconnect_cb = nil
212
260
  end
213
261
 
262
+ # Drain gracefully disconnects from the server, letting
263
+ # subscribers process pending messages already sent by server and
264
+ # optionally calls the associated block.
265
+ # @param [Block] &blk called when drain is done and connection is closed.
266
+ def drain(&blk)
267
+ if (client and !client.draining? and (client.connected? || client.reconnecting?))
268
+ client.drain { blk.call if blk }
269
+ end
270
+ end
271
+
214
272
  # @return [URI] Connected server
215
273
  def connected_server
216
274
  return nil unless client
@@ -229,6 +287,12 @@ module NATS
229
287
  client.reconnecting?
230
288
  end
231
289
 
290
+ # @return [Boolean] Draining state
291
+ def draining?
292
+ return false unless client
293
+ client.draining?
294
+ end
295
+
232
296
  # @return [Hash] Options
233
297
  def options
234
298
  return {} unless client
@@ -343,16 +407,48 @@ module NATS
343
407
  uri.host != 'localhost' && uri.host != '127.0.0.1'
344
408
  end
345
409
 
410
+ def process_uri(uris)
411
+ connect_uris = []
412
+ uris.split(',').each do |uri|
413
+ opts = {}
414
+
415
+ # Scheme
416
+ if uri.include?("://")
417
+ scheme, uri = uri.split("://")
418
+ opts[:scheme] = scheme
419
+ else
420
+ opts[:scheme] = 'nats'
421
+ end
422
+
423
+ # UserInfo
424
+ if uri.include?("@")
425
+ userinfo, endpoint = uri.split("@")
426
+ host, port = endpoint.split(":")
427
+ opts[:userinfo] = userinfo
428
+ else
429
+ host, port = uri.split(":")
430
+ end
431
+
432
+ # Host and Port
433
+ opts[:host] = host || "localhost"
434
+ opts[:port] = port || DEFAULT_PORT
435
+
436
+ connect_uris << URI::Generic.build(opts)
437
+ end
438
+
439
+ connect_uris
440
+ end
346
441
  end
347
442
 
348
443
  attr_reader :connected, :connect_cb, :err_cb, :err_cb_overridden, :pongs_received #:nodoc:
349
- attr_reader :closing, :reconnecting, :server_pool, :options, :server_info #:nodoc
444
+ attr_reader :closing, :reconnecting, :draining, :server_pool, :options, :server_info #:nodoc
350
445
  attr_reader :msgs_received, :msgs_sent, :bytes_received, :bytes_sent, :pings
351
446
  attr_reader :disconnect_cb, :close_cb
352
447
 
353
448
  alias :connected? :connected
354
449
  alias :closing? :closing
355
450
  alias :reconnecting? :reconnecting
451
+ alias :draining? :draining
356
452
 
357
453
  def initialize(options)
358
454
  @options = options
@@ -383,6 +479,9 @@ module NATS
383
479
  @resp_sub_prefix = nil
384
480
  @nuid = NATS::NUID.new
385
481
 
482
+ # Drain mode
483
+ @draining = false
484
+ @drained_subs = false
386
485
  send_connect_command
387
486
  end
388
487
 
@@ -392,7 +491,7 @@ module NATS
392
491
  # @param [String] opt_reply
393
492
  # @param [Block] blk, closure called when publish has been processed by the server.
394
493
  def publish(subject, msg=EMPTY_MSG, opt_reply=nil, &blk)
395
- return unless subject
494
+ return unless subject and not @drained_subs
396
495
  msg = msg.to_s
397
496
 
398
497
  # Accounting
@@ -412,7 +511,7 @@ module NATS
412
511
  # @param [Block] callback, called when a message is delivered.
413
512
  # @return [Object] sid, Subject Identifier
414
513
  def subscribe(subject, opts={}, &callback)
415
- return unless subject
514
+ return unless subject and not draining?
416
515
  sid = (@ssid += 1)
417
516
  sub = @subs[sid] = { :subject => subject, :callback => callback, :received => 0 }
418
517
  sub[:queue] = opts[:queue] if opts[:queue]
@@ -427,6 +526,7 @@ module NATS
427
526
  # @param [Object] sid
428
527
  # @param [Number] opt_max, optional number of responses to receive before auto-unsubscribing
429
528
  def unsubscribe(sid, opt_max=nil)
529
+ return if draining?
430
530
  opt_max_str = " #{opt_max}" unless opt_max.nil?
431
531
  send_command("UNSUB #{sid}#{opt_max_str}#{CR_LF}")
432
532
  return unless sub = @subs[sid]
@@ -434,6 +534,48 @@ module NATS
434
534
  @subs.delete(sid) unless (sub[:max] && (sub[:received] < sub[:max]))
435
535
  end
436
536
 
537
+ # Drain gracefully closes the connection.
538
+ # @param [Block] blk called when drain is done and connection is closed.
539
+ def drain(&blk)
540
+ return if draining? or closing?
541
+ @draining = true
542
+
543
+ # Remove interest in all subjects to stop receiving messages.
544
+ @subs.each do |sid, _|
545
+ send_command("UNSUB #{sid} #{CR_LF}")
546
+ end
547
+
548
+ # Roundtrip to ensure no more messages are received.
549
+ flush do
550
+ drain_timeout_timer, draining_timer = nil, nil
551
+ drain_timeout_timer = EM.add_timer(options[:drain_timeout]) do
552
+ EM.cancel_timer(draining_timer)
553
+
554
+ # Report the timeout via the error callback and just close
555
+ err_cb.call(NATS::ClientError.new("Drain Timeout"))
556
+ @draining = false
557
+ close unless closing?
558
+ blk.call if blk
559
+ end
560
+
561
+ # Periodically check for the pending data to be empty.
562
+ draining_timer = EM.add_periodic_timer(0.1) do
563
+ next unless closing? or @buf.nil? or @buf.empty?
564
+
565
+ # Subscriptions have been drained already so disallow publishing.
566
+ @drained_subs = true
567
+ next unless pending_data_size == 0
568
+ EM.cancel_timer(draining_timer)
569
+ EM.cancel_timer(drain_timeout_timer)
570
+
571
+ # We're done draining and can close now.
572
+ @draining = false
573
+ close unless closing?
574
+ blk.call if blk
575
+ end
576
+ end
577
+ end
578
+
437
579
  # Return the active subscription count.
438
580
  # @return [Number]
439
581
  def subscription_count
@@ -628,7 +770,7 @@ module NATS
628
770
  end
629
771
 
630
772
  def auth_connection?
631
- !@uri.user.nil?
773
+ !@uri.user.nil? || @options[:token]
632
774
  end
633
775
 
634
776
  def connect_command #:nodoc:
@@ -637,12 +779,19 @@ module NATS
637
779
  :pedantic => @options[:pedantic],
638
780
  :lang => ::NATS::LANG,
639
781
  :version => ::NATS::VERSION,
640
- :protocol => ::NATS::PROTOCOL_VERSION
782
+ :protocol => ::NATS::PROTOCOL_VERSION,
783
+ :echo => !@options[:no_echo]
641
784
  }
642
- if auth_connection?
785
+ case
786
+ when @options[:token]
787
+ cs[:auth_token] = @options[:token]
788
+ when @uri.password.nil?
789
+ cs[:auth_token] = @uri.user
790
+ else
643
791
  cs[:user] = @uri.user
644
792
  cs[:pass] = @uri.password
645
- end
793
+ end if auth_connection?
794
+
646
795
  cs[:name] = @options[:name] if @options[:name]
647
796
  cs[:ssl_required] = @ssl if @ssl
648
797
  cs[:tls_required] = true if @tls
@@ -701,6 +850,7 @@ module NATS
701
850
 
702
851
  def receive_data(data) #:nodoc:
703
852
  @buf = @buf ? @buf << data : data
853
+
704
854
  while (@buf)
705
855
  case @parse_state
706
856
  when AWAITING_CONTROL_LINE
@@ -748,7 +898,6 @@ module NATS
748
898
  @parse_state = AWAITING_CONTROL_LINE
749
899
  @buf = nil if (@buf && @buf.empty?)
750
900
  end
751
-
752
901
  end
753
902
  end
754
903
 
@@ -775,6 +924,14 @@ module NATS
775
924
  # Otherwise, use a regular connection.
776
925
  end
777
926
 
927
+ # Check whether there no echo is supported by the server.
928
+ if @options[:no_echo]
929
+ if @server_info[:proto].nil? || @server_info[:proto] < 1
930
+ err_cb.call(NATS::ServerError.new('No echo option not supported by this server'))
931
+ close_connection_after_writing
932
+ end
933
+ end
934
+
778
935
  # Detect any announced server that we might not be aware of...
779
936
  connect_urls = @server_info[:connect_urls]
780
937
  if connect_urls
@@ -792,9 +949,9 @@ module NATS
792
949
  u.password = options[:pass] if options[:pass]
793
950
 
794
951
  # Use creds from the current server if not set explicitly.
795
- if @uri
796
- u.user ||= @uri.user if @uri.user
797
- u.password ||= @uri.password if @uri.password
952
+ if @uri and !@uri.user.nil? and !@uri.user.empty?
953
+ u.user ||= @uri.user
954
+ u.password ||= @uri.password
798
955
  end
799
956
 
800
957
  srvs << { :uri => u, :reconnect_attempts => 0, :discovered => true }
@@ -14,7 +14,7 @@
14
14
 
15
15
  module NATS
16
16
  # NOTE: These are all announced to the server on CONNECT
17
- VERSION = "0.9.2".freeze
17
+ VERSION = "0.10.0".freeze
18
18
  LANG = RUBY_ENGINE
19
19
  PROTOCOL_VERSION = 1
20
20
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nats
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.2
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Derek Collison
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-07-27 00:00:00.000000000 Z
11
+ date: 2018-08-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: eventmachine