eventq_rabbitmq 1.16.1 → 1.17.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 033f285524edc8a8a2b10e19d09c8232149fcbd1
4
- data.tar.gz: 03e5dd42f6fc3916dd1348e5204e99d6156c9f96
3
+ metadata.gz: 859eb994800fdee7f510b5fd525b228f4b24a6cf
4
+ data.tar.gz: d07925490ff91692f4b9eb1f4c15dc2fc87ec1d0
5
5
  SHA512:
6
- metadata.gz: a1985bdcc2362ff882c597922ec960d249d98453cd87fb10e6091fad72ab9263c604671061d7dc74e28937019e535a33c24abf02d5c4a1a9fb7cd723e9fc4050
7
- data.tar.gz: ff35645dc87c4fcc4036dadf918ff69487ace76cc578fcbee09167939cca51389d7bab7722b752c7816df4cf4356fb98a6fd7809dc6aee521be707134d5cc7e6
6
+ metadata.gz: 3824b54c58ebe5192b5b9d4e504ea3cda7a3ec6d95a14d419b71b5e982e1614161e732978dbd1832abe531b5a0be43509c7f1f25da24119e13a325817518695b
7
+ data.tar.gz: b910bfa1e00aa03aee5c80a99c80721ec9b5f8312484c8d566956e1690a7209b496408e3bf70224731986186a68f3b3b1148dc9d1c02575363b8ee854c28037e
@@ -1,10 +1,21 @@
1
1
  require 'eventq_base'
2
- require 'bunny'
2
+ if RUBY_PLATFORM =~ /java/
3
+ require 'march_hare'
4
+ else
5
+ require 'bunny'
6
+ end
7
+
3
8
  require 'hash_kit'
4
9
  require_relative '../lib/eventq_rabbitmq/version'
5
10
  require_relative '../lib/eventq_rabbitmq/rabbitmq_queue_client'
6
11
  require_relative '../lib/eventq_rabbitmq/rabbitmq_queue_manager'
7
- require_relative '../lib/eventq_rabbitmq/rabbitmq_queue_worker'
12
+
13
+ if RUBY_PLATFORM =~ /java/
14
+ require_relative '../lib/eventq_rabbitmq/jruby/rabbitmq_queue_worker'
15
+ else
16
+ require_relative '../lib/eventq_rabbitmq/rabbitmq_queue_worker'
17
+ end
18
+
8
19
  require_relative '../lib/eventq_rabbitmq/rabbitmq_subscription_manager'
9
20
  require_relative '../lib/eventq_rabbitmq/rabbitmq_eventq_client'
10
21
  require_relative '../lib/eventq_rabbitmq/default_queue'
@@ -0,0 +1,373 @@
1
+ require 'java'
2
+ java_import java.util.concurrent.Executors
3
+ module EventQ
4
+ module RabbitMq
5
+ class QueueWorker
6
+ include EventQ::WorkerId
7
+
8
+ attr_accessor :is_running
9
+
10
+ def initialize
11
+ @threads = []
12
+ @forks = []
13
+ @is_running = false
14
+
15
+ @retry_exceeded_block = nil
16
+ @on_retry_block = nil
17
+ @on_error_block = nil
18
+ @hash_helper = HashKit::Helper.new
19
+ @serialization_provider_manager = EventQ::SerializationProviders::Manager.new
20
+ @signature_provider_manager = EventQ::SignatureProviders::Manager.new
21
+ @last_gc_flush = Time.now
22
+ @gc_flush_interval = 10
23
+ end
24
+
25
+ def start(queue, options = {}, &block)
26
+
27
+ EventQ.logger.info("[#{self.class}] - Preparing to start listening for messages.")
28
+
29
+ configure(queue, options)
30
+
31
+ raise "[#{self.class}] - Worker is already running." if running?
32
+
33
+ if options[:client] == nil
34
+ raise "[#{self.class}] - :client (QueueClient) must be specified."
35
+ end
36
+
37
+ EventQ.logger.info("[#{self.class}] - Listening for messages.")
38
+ EventQ.logger.debug do
39
+ "[#{self.class} #start] - Listening for messages on queue: #{EventQ.create_queue_name(queue.name)}"
40
+ end
41
+
42
+ start_process(options, queue, block)
43
+
44
+ end
45
+
46
+ def start_process(options, queue, block)
47
+
48
+ @is_running = true
49
+
50
+ %w'INT TERM'.each do |sig|
51
+ Signal.trap(sig) {
52
+ stop
53
+ exit
54
+ }
55
+ end
56
+
57
+ if !options.key?(:durable)
58
+ options[:durable] = true
59
+ end
60
+
61
+ client = options[:client]
62
+ manager = EventQ::RabbitMq::QueueManager.new
63
+ manager.durable = options[:durable]
64
+ @connection = client.get_connection
65
+
66
+ @executor = java.util.concurrent.Executors::newFixedThreadPool @thread_count
67
+
68
+ #loop through each thread count
69
+ @thread_count.times do
70
+
71
+ @executor.execute do
72
+
73
+ #begin the queue loop for this thread
74
+ while true do
75
+
76
+ #check if the worker is still allowed to run and break out of thread loop if not
77
+ unless running?
78
+ break
79
+ end
80
+
81
+ if @executor.is_shutdown
82
+ break
83
+ end
84
+
85
+ has_received_message = false
86
+
87
+ begin
88
+
89
+ channel = @connection.create_channel
90
+
91
+ has_received_message = thread_process_iteration(channel, manager, queue, block)
92
+
93
+ rescue => e
94
+ EventQ.logger.error("An unhandled error occurred. Error: #{e} | Backtrace: #{e.backtrace}")
95
+ call_on_error_block(error: e)
96
+ end
97
+
98
+ if channel != nil && channel.open?
99
+ channel.close
100
+ end
101
+
102
+ gc_flush
103
+
104
+ if !has_received_message
105
+ EventQ.logger.debug { "[#{self.class}] - No message received." }
106
+ if @sleep > 0
107
+ EventQ.logger.debug { "[#{self.class}] - Sleeping for #{@sleep} seconds" }
108
+ sleep(@sleep)
109
+ end
110
+ end
111
+
112
+ end
113
+
114
+ end
115
+
116
+ end
117
+
118
+ if options.key?(:wait) && options[:wait] == true
119
+ while running? do end
120
+ @connection.close if @connection.open?
121
+ end
122
+
123
+ return true
124
+
125
+ end
126
+
127
+ def call_on_error_block(error:, message: nil)
128
+ if @on_error_block
129
+ EventQ.logger.debug { "[#{self.class}] - Executing on_error block." }
130
+ begin
131
+ @on_error_block.call(error, message)
132
+ rescue => e
133
+ EventQ.logger.error("[#{self.class}] - An error occurred executing the on_error block. Error: #{e}")
134
+ end
135
+ else
136
+ EventQ.logger.debug { "[#{self.class}] - No on_error block specified to execute." }
137
+ end
138
+ end
139
+
140
+ def gc_flush
141
+ if Time.now - last_gc_flush > @gc_flush_interval
142
+ GC.start
143
+ @last_gc_flush = Time.now
144
+ end
145
+ end
146
+
147
+ def last_gc_flush
148
+ @last_gc_flush
149
+ end
150
+
151
+ def thread_process_iteration(channel, manager, queue, block)
152
+
153
+ #get the queue
154
+ q = manager.get_queue(channel, queue)
155
+ retry_exchange = manager.get_retry_exchange(channel, queue)
156
+
157
+ received = false
158
+
159
+ begin
160
+ delivery_info, payload = manager.pop_message(queue: q)
161
+
162
+ #check that message was received
163
+ if payload != nil
164
+ received = true
165
+ begin
166
+ tag_processing_thread
167
+ process_message(payload, queue, channel, retry_exchange, delivery_info, block)
168
+ ensure
169
+ untag_processing_thread
170
+ end
171
+
172
+ end
173
+
174
+ rescue => e
175
+ EventQ.logger.error("[#{self.class}] - An error occurred attempting to process a message. Error: #{e} | Backtrace: #{e.backtrace}")
176
+ call_on_error_block(error: e)
177
+ end
178
+
179
+ return received
180
+ end
181
+
182
+ def stop
183
+ EventQ.logger.info { "[#{self.class}] - Stopping..." }
184
+ @is_running = false
185
+ @executor.shutdown
186
+ if @connection != nil
187
+ @connection.close if @connection.open?
188
+ end
189
+ return true
190
+ end
191
+
192
+ def on_retry_exceeded(&block)
193
+ @retry_exceeded_block = block
194
+ return nil
195
+ end
196
+
197
+ def on_retry(&block)
198
+ @on_retry_block = block
199
+ return nil
200
+ end
201
+
202
+ def on_error(&block)
203
+ @on_error_block = block
204
+ return nil
205
+ end
206
+
207
+ def running?
208
+ return @is_running
209
+ end
210
+
211
+ def deserialize_message(payload)
212
+ provider = @serialization_provider_manager.get_provider(EventQ::Configuration.serialization_provider)
213
+ return provider.deserialize(payload)
214
+ end
215
+
216
+ def serialize_message(msg)
217
+ provider = @serialization_provider_manager.get_provider(EventQ::Configuration.serialization_provider)
218
+ return provider.serialize(msg)
219
+ end
220
+
221
+ def call_on_retry_exceeded_block(message)
222
+ if @retry_exceeded_block != nil
223
+ EventQ.logger.debug { "[#{self.class}] - Executing on_retry_exceeded block." }
224
+ begin
225
+ @retry_exceeded_block.call(message)
226
+ rescue => e
227
+ EventQ.logger.error("[#{self.class}] - An error occurred executing the on_retry_exceeded block. Error: #{e}")
228
+ end
229
+ else
230
+ EventQ.logger.debug { "[#{self.class}] - No on_retry_exceeded block specified." }
231
+ end
232
+ end
233
+
234
+ def call_on_retry_block(message)
235
+ if @on_retry_block
236
+ EventQ.logger.debug { "[#{self.class}] - Executing on_retry block." }
237
+ begin
238
+ @on_retry_block.call(message, abort)
239
+ rescue => e
240
+ EventQ.logger.error("[#{self.class}] - An error occurred executing the on_retry block. Error: #{e}")
241
+ end
242
+ else
243
+ EventQ.logger.debug { "[#{self.class}] - No on_retry block specified." }
244
+ end
245
+ end
246
+
247
+ def reject_message(channel, message, delivery_tag, retry_exchange, queue, abort)
248
+
249
+ EventQ.logger.info("[#{self.class}] - Message rejected removing from queue.")
250
+ #reject the message to remove from queue
251
+ channel.reject(delivery_tag, false)
252
+
253
+ #check if the message retry limit has been exceeded
254
+ if message.retry_attempts >= queue.max_retry_attempts
255
+
256
+ EventQ.logger.info("[#{self.class}] - Message retry attempt limit exceeded. Msg: #{serialize_message(message)}")
257
+
258
+ call_on_retry_exceeded_block(message)
259
+
260
+ #check if the message is allowed to be retried
261
+ elsif queue.allow_retry
262
+
263
+ EventQ.logger.debug { "[#{self.class}] - Incrementing retry attempts count." }
264
+ message.retry_attempts += 1
265
+
266
+ if queue.allow_retry_back_off == true
267
+ EventQ.logger.debug { "[#{self.class}] - Calculating message back off retry delay. Attempts: #{message.retry_attempts} * Retry Delay: #{queue.retry_delay}" }
268
+ message_ttl = message.retry_attempts * queue.retry_delay
269
+ if (message.retry_attempts * queue.retry_delay) > queue.max_retry_delay
270
+ EventQ.logger.debug { "[#{self.class}] - Max message back off retry delay reached." }
271
+ message_ttl = queue.max_retry_delay
272
+ end
273
+ else
274
+ EventQ.logger.debug { "[#{self.class}] - Setting fixed retry delay for message." }
275
+ message_ttl = queue.retry_delay
276
+ end
277
+
278
+ EventQ.logger.debug { "[#{self.class}] - Sending message for retry. Message TTL: #{message_ttl}" }
279
+ retry_exchange.publish(serialize_message(message), :expiration => message_ttl)
280
+ EventQ.logger.debug { "[#{self.class}] - Published message to retry exchange." }
281
+
282
+ call_on_retry_block(message)
283
+
284
+ end
285
+
286
+ return true
287
+
288
+ end
289
+
290
+ def configure(queue, options = {})
291
+
292
+ @queue = queue
293
+
294
+ #default thread count
295
+ @thread_count = 4
296
+ if options.key?(:thread_count)
297
+ @thread_count = options[:thread_count]
298
+ end
299
+
300
+ #default sleep time in seconds
301
+ @sleep = 15
302
+ if options.key?(:sleep)
303
+ @sleep = options[:sleep]
304
+ end
305
+
306
+ @fork_count = 1
307
+ if options.key?(:fork_count)
308
+ @fork_count = options[:fork_count]
309
+ end
310
+
311
+ @gc_flush_interval = 10
312
+ if options.key?(:gc_flush_interval)
313
+ @gc_flush_interval = options[:gc_flush_interval]
314
+ end
315
+
316
+ EventQ.logger.info("[#{self.class}] - Configuring. Process Count: #{@fork_count} | Thread Count: #{@thread_count} | Interval Sleep: #{@sleep}.")
317
+
318
+ return true
319
+
320
+ end
321
+
322
+ private
323
+
324
+ def process_message(payload, queue, channel, retry_exchange, delivery_tag, block)
325
+ abort = false
326
+ error = false
327
+ message = deserialize_message(payload)
328
+
329
+ EventQ.logger.info("[#{self.class}] - Message received. Retry Attempts: #{message.retry_attempts}")
330
+
331
+ @signature_provider_manager.validate_signature(message: message, queue: queue)
332
+
333
+ message_args = EventQ::MessageArgs.new(type: message.type,
334
+ retry_attempts: message.retry_attempts,
335
+ context: message.context,
336
+ content_type: message.content_type)
337
+
338
+ if(!EventQ::NonceManager.is_allowed?(message.id))
339
+ EventQ.logger.info("[#{self.class}] - Duplicate Message received. Dropping message.")
340
+ channel.acknowledge(delivery_tag, false)
341
+ return false
342
+ end
343
+
344
+ #begin worker block for queue message
345
+ begin
346
+ block.call(message.content, message_args)
347
+
348
+ if message_args.abort == true
349
+ abort = true
350
+ EventQ.logger.info("[#{self.class}] - Message aborted.")
351
+ else
352
+ #accept the message as processed
353
+ channel.acknowledge(delivery_tag, false)
354
+ EventQ.logger.info("[#{self.class}] - Message acknowledged.")
355
+ end
356
+
357
+ rescue => e
358
+ EventQ.logger.error("[#{self.class}] - An unhandled error happened attempting to process a queue message. Error: #{e} | Backtrace: #{e.backtrace}")
359
+ error = true
360
+ call_on_error_block(error: e, message: message)
361
+ end
362
+
363
+ if error || abort
364
+ EventQ::NonceManager.failed(message.id)
365
+ reject_message(channel, message, delivery_tag, retry_exchange, queue, abort)
366
+ else
367
+ EventQ::NonceManager.complete(message.id)
368
+ end
369
+ end
370
+ end
371
+ end
372
+ end
373
+
@@ -98,7 +98,7 @@ module EventQ
98
98
  yield(channel)
99
99
 
100
100
  ensure
101
- channel&.close
101
+ channel&.close if channel.open?
102
102
  connection.close
103
103
  end
104
104
 
@@ -30,7 +30,7 @@ module EventQ
30
30
  :pass => @password,
31
31
  :ssl => @ssl,
32
32
  :read_timeout => 4,
33
- :heartbeat => 20,
33
+ :heartbeat => 8,
34
34
  :continuation_timeout => 5000,
35
35
  :automatically_recover => true,
36
36
  :network_recovery_interval => 1,
@@ -39,7 +39,12 @@ module EventQ
39
39
  end
40
40
 
41
41
  def get_connection
42
- conn = Bunny.new(connection_options)
42
+ if RUBY_PLATFORM =~ /java/
43
+ conn = MarchHare.connect(connection_options)
44
+ else
45
+ conn = Bunny.new(connection_options)
46
+ end
47
+
43
48
  conn.start
44
49
  return conn
45
50
  end
@@ -32,6 +32,22 @@ module EventQ
32
32
  return q
33
33
  end
34
34
 
35
+ def pop_message(queue:)
36
+ if RUBY_PLATFORM =~ /java/
37
+ headers, payload = queue.pop({ :ack => true, :block => true })
38
+ if headers == nil
39
+ return [nil,nil]
40
+ end
41
+ [headers.delivery_tag, payload]
42
+ else
43
+ headers, properties, payload = queue.pop({ :manual_ack => true, :block => true })
44
+ if headers == nil
45
+ return [nil,nil]
46
+ end
47
+ [headers.delivery_tag, payload]
48
+ end
49
+ end
50
+
35
51
  def get_queue_exchange(channel, queue)
36
52
  _exchange_name = EventQ.create_exchange_name(queue.name)
37
53
  channel.direct("#{_exchange_name}.ex")
@@ -87,7 +87,7 @@ module EventQ
87
87
  while true do
88
88
 
89
89
  #check if the worker is still allowed to run and break out of thread loop if not
90
- if !@is_running
90
+ unless running?
91
91
  break
92
92
  end
93
93
 
@@ -104,7 +104,7 @@ module EventQ
104
104
  call_on_error_block(error: e)
105
105
  end
106
106
 
107
- if channel != nil && channel.status != :closed
107
+ if channel != nil && channel.open?
108
108
  channel.close
109
109
  end
110
110
 
@@ -127,7 +127,7 @@ module EventQ
127
127
 
128
128
  if options.key?(:wait) && options[:wait] == true
129
129
  @threads.each { |thr| thr.join }
130
- @connection.close
130
+ @connection.close if @connection.open?
131
131
  end
132
132
 
133
133
  return true
@@ -167,7 +167,7 @@ module EventQ
167
167
  received = false
168
168
 
169
169
  begin
170
- delivery_info, properties, payload = q.pop(:manual_ack => true, :block => true)
170
+ delivery_info, payload = manager.pop_message(queue: q)
171
171
 
172
172
  #check that message was received
173
173
  if payload != nil
@@ -190,11 +190,17 @@ module EventQ
190
190
  end
191
191
 
192
192
  def stop
193
- puts "[#{self.class}] - Stopping..."
193
+ EventQ.logger.info { "[#{self.class}] - Stopping..." }
194
194
  @is_running = false
195
- @threads.each { |thr| thr.join }
195
+ Thread.list.each do |thread|
196
+ thread.exit unless thread == Thread.current
197
+ end
196
198
  if @connection != nil
197
- @connection.close
199
+ begin
200
+ @connection.close if @connection.open?
201
+ rescue Timeout::Error
202
+ EventQ.logger.error { 'Timeout occurred closing connection.' }
203
+ end
198
204
  end
199
205
  return true
200
206
  end
@@ -254,11 +260,11 @@ module EventQ
254
260
  end
255
261
  end
256
262
 
257
- def reject_message(channel, message, delivery_info, retry_exchange, queue, abort)
263
+ def reject_message(channel, message, delivery_tag, retry_exchange, queue, abort)
258
264
 
259
265
  EventQ.logger.info("[#{self.class}] - Message rejected removing from queue.")
260
266
  #reject the message to remove from queue
261
- channel.reject(delivery_info.delivery_tag, false)
267
+ channel.reject(delivery_tag, false)
262
268
 
263
269
  #check if the message retry limit has been exceeded
264
270
  if message.retry_attempts >= queue.max_retry_attempts
@@ -331,7 +337,7 @@ module EventQ
331
337
 
332
338
  private
333
339
 
334
- def process_message(payload, queue, channel, retry_exchange, delivery_info, block)
340
+ def process_message(payload, queue, channel, retry_exchange, delivery_tag, block)
335
341
  abort = false
336
342
  error = false
337
343
  message = deserialize_message(payload)
@@ -347,7 +353,7 @@ module EventQ
347
353
 
348
354
  if(!EventQ::NonceManager.is_allowed?(message.id))
349
355
  EventQ.logger.info("[#{self.class}] - Duplicate Message received. Dropping message.")
350
- channel.acknowledge(delivery_info.delivery_tag, false)
356
+ channel.acknowledge(delivery_tag, false)
351
357
  return false
352
358
  end
353
359
 
@@ -360,7 +366,7 @@ module EventQ
360
366
  EventQ.logger.info("[#{self.class}] - Message aborted.")
361
367
  else
362
368
  #accept the message as processed
363
- channel.acknowledge(delivery_info.delivery_tag, false)
369
+ channel.acknowledge(delivery_tag, false)
364
370
  EventQ.logger.info("[#{self.class}] - Message acknowledged.")
365
371
  end
366
372
 
@@ -372,7 +378,7 @@ module EventQ
372
378
 
373
379
  if error || abort
374
380
  EventQ::NonceManager.failed(message.id)
375
- reject_message(channel, message, delivery_info, retry_exchange, queue, abort)
381
+ reject_message(channel, message, delivery_tag, retry_exchange, queue, abort)
376
382
  else
377
383
  EventQ::NonceManager.complete(message.id)
378
384
  end
@@ -1,3 +1,3 @@
1
1
  module EventqRabbitmq
2
- VERSION = "1.16.1"
2
+ VERSION = "1.17.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: eventq_rabbitmq
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.16.1
4
+ version: 1.17.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - vaughanbrittonsage
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-06-25 00:00:00.000000000 Z
11
+ date: 2017-06-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -53,33 +53,33 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: bunny
56
+ name: eventq_base
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ">="
59
+ - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '0'
61
+ version: '1.15'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ">="
66
+ - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '0'
68
+ version: '1.15'
69
69
  - !ruby/object:Gem::Dependency
70
- name: eventq_base
70
+ name: bunny
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - "~>"
73
+ - - ">="
74
74
  - !ruby/object:Gem::Version
75
- version: '1.14'
75
+ version: '0'
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - "~>"
80
+ - - ">="
81
81
  - !ruby/object:Gem::Version
82
- version: '1.14'
82
+ version: '0'
83
83
  description: This is the rabbitmq implementation for EventQ
84
84
  email:
85
85
  - vaughanbritton@gmail.com
@@ -91,6 +91,7 @@ files:
91
91
  - bin/setup
92
92
  - lib/eventq_rabbitmq.rb
93
93
  - lib/eventq_rabbitmq/default_queue.rb
94
+ - lib/eventq_rabbitmq/jruby/rabbitmq_queue_worker.rb
94
95
  - lib/eventq_rabbitmq/rabbitmq_eventq_client.rb
95
96
  - lib/eventq_rabbitmq/rabbitmq_queue_client.rb
96
97
  - lib/eventq_rabbitmq/rabbitmq_queue_manager.rb