eventq_rabbitmq 1.16.1 → 1.17.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
  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