eventhub-processor 0.4.6 → 0.4.7

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: d463cf4444c37c9d735227c2141eabccbbfee9b7
4
- data.tar.gz: 2b7b9c3cc7c79cb6979099e9964ef93af49ea072
3
+ metadata.gz: 42433583bf8e36e1fea7398bafb1300823a2802e
4
+ data.tar.gz: 22ab273d6c45e7749dba4e704c3dddc729d00596
5
5
  SHA512:
6
- metadata.gz: 5af479a23d2449b63b2f517a58f1a6b4ce7cfee740e85eb2118f3b62f56e6e1ae6336b766e4366cdc4eb4aa5f46114c7ec0dd514909304a30aa997c82145c5d4
7
- data.tar.gz: caa453b6bfa522f8edb3da49d960703682f36eb7e7d78d0ab171b4b59a448c5310e79a186e34a768b9b96bf622a193ad3db5444fa59c8c60356195a66370358d
6
+ metadata.gz: fde68540abd486a545855d14f618a6a001ad5ebb6003159a77ab4150de7befbcf19f9a855bf27d854087c52dcb2fe474c29f43ea87aac1cab09427c6d6aabf99
7
+ data.tar.gz: aba07cd1286c02e6a672a55730c2be08e2b25506b45d721f38f655aba07f0e7923941145e62b231ceca49d4695cc98810dd9e3876fa3ee3525cb3621a23e266d
@@ -32,4 +32,4 @@ module EventHub
32
32
  end
33
33
  end
34
34
 
35
- end
35
+ end
@@ -1,2 +1,2 @@
1
1
  class EventHub::BaseException < RuntimeError
2
- end
2
+ end
@@ -7,20 +7,28 @@ module EventHub
7
7
  attr_accessor :data, :folder, :environment
8
8
 
9
9
  def initialize
10
- @data = nil
10
+ @data = {}
11
11
  @environment = 'development'
12
12
  end
13
13
 
14
14
  def load_file(input, env = 'development')
15
- json = JSON.parse(IO.read(input))
15
+ load_string(IO.read(input), env)
16
+ true
17
+ rescue => e
18
+ EventHub.logger.info("Unexpected exception while loading configuration [#{input}]: #{format_string(e.message)}")
19
+ false
20
+ end
21
+
22
+ def load_string(json_string, env = 'development')
23
+ json = JSON.parse(json_string)
16
24
  @data = json[env]
17
25
  @environment = env
18
26
  true
19
27
  rescue => e
20
- EventHub.logger.info("Unexpected exception while loading configuration [#{input}]: #{format_string(e.message)}")
28
+ EventHub.logger.info("JSON configuration parsing failed: #{format_string(e.message)}")
21
29
  false
22
30
  end
23
31
 
24
32
  end
25
33
 
26
- end
34
+ end
@@ -1,16 +1,16 @@
1
1
  module EventHub
2
2
 
3
3
  EH_X_INBOUND = 'event_hub.inbound'
4
-
4
+
5
5
  STATUS_INITIAL = 0 # To be set when dispatcher needs to dispatch to first process step.
6
6
  STATUS_SUCCESS = 200 # To be set to indicate successful processed message. Dispatcher will routes message to the next step.
7
7
  STATUS_RETRY = 300 # To be set to trigger retry cycle controlled by the dispatcher
8
- STATUS_RETRY_PENDING = 301 # Set and used by the dispatcher only.
9
- # Set before putting the message into a retry queue.
10
- # Once message has been retried it will sent do the same step with status.code = STATUS_SUCCESS
11
- STATUS_INVALID = 400 # To be set to indicate invalid message (not json, invalid Event Hub Message).
8
+ STATUS_RETRY_PENDING = 301 # Set and used by the dispatcher only.
9
+ # Set before putting the message into a retry queue.
10
+ # Once message has been retried it will sent do the same step with status.code = STATUS_SUCCESS
11
+ STATUS_INVALID = 400 # To be set to indicate invalid message (not json, invalid Event Hub Message).
12
12
  # Dispatcher will publish message to the invalid queue.
13
- STATUS_DEADLETTER = 500 # To be set by dispatcher, processor or channel adapters to indicate
14
- # that message needs to be dead-lettered. Rejected messages could miss the
13
+ STATUS_DEADLETTER = 500 # To be set by dispatcher, processor or channel adapters to indicate
14
+ # that message needs to be dead-lettered. Rejected messages could miss the
15
15
  # status.code = STATUS_DEADLETTER due to the RabbitMQ deadletter exchange mechanism.
16
- end
16
+ end
@@ -1,48 +1,48 @@
1
1
  module HashExtensions
2
2
 
3
- module ClassMethods
4
- end
3
+ module ClassMethods
4
+ end
5
5
 
6
- module InstanceMethods
7
- # get value from provided key path, e.g. hash.get(%w(event_hub plate.queue1 retry_s))
8
- # "a" => { "b" => { "c" => { "value"}}}
9
- def get(arg)
10
- path = arg.is_a?(String) ? arg.split('.') : arg
11
- path.inject(self,:[])
12
- rescue NoMethodError
13
- return nil
14
- end
6
+ module InstanceMethods
7
+ # get value from provided key path, e.g. hash.get(%w(event_hub plate.queue1 retry_s))
8
+ # "a" => { "b" => { "c" => { "value"}}}
9
+ def get(arg)
10
+ path = arg.is_a?(String) ? arg.split('.') : arg
11
+ path.inject(self,:[])
12
+ rescue NoMethodError
13
+ return nil
14
+ end
15
15
 
16
- # set value from provided key path, e.h. hash.set('a.b.c','new value')
17
- # if overwrite is false, value will be set if it was nil previously
18
- def set(arg,value,overwrite=true)
19
- *key_path, last = arg.is_a?(String) ? arg.split(".") : arg
20
- if overwrite
21
- key_path.inject(self) { |h,key| h.has_key?(key) ? h[key] : h[key]={}} [last] = value
22
- else
23
- key_path.inject(self) { |h,key| h.has_key?(key) ? h[key] : h[key]={}} [last] ||= value
24
- end
25
- end
16
+ # set value from provided key path, e.h. hash.set('a.b.c','new value')
17
+ # if overwrite is false, value will be set if it was nil previously
18
+ def set(arg,value,overwrite=true)
19
+ *key_path, last = arg.is_a?(String) ? arg.split(".") : arg
20
+ if overwrite
21
+ key_path.inject(self) { |h,key| h.has_key?(key) ? h[key] : h[key]={}} [last] = value
22
+ else
23
+ key_path.inject(self) { |h,key| h.has_key?(key) ? h[key] : h[key]={}} [last] ||= value
24
+ end
25
+ end
26
26
 
27
- # get all keys path, { 'a' => 'value1', 'b' => { 'c' => 'value2'}}.all_keys_with_path => ['a','b.c']
28
- def all_keys_with_path(parent=nil)
29
- a = []
30
- each do |k,v|
31
- if v.is_a?(Hash)
32
- a << v.all_keys_with_path([parent,k].compact.join('.'))
33
- else
34
- a << "#{[parent,k].compact.join(".")}"
35
- end
36
- end
37
- a.flatten
38
- end
27
+ # get all keys path, { 'a' => 'value1', 'b' => { 'c' => 'value2'}}.all_keys_with_path => ['a','b.c']
28
+ def all_keys_with_path(parent=nil)
29
+ a = []
30
+ each do |k,v|
31
+ if v.is_a?(Hash)
32
+ a << v.all_keys_with_path([parent,k].compact.join('.'))
33
+ else
34
+ a << "#{[parent,k].compact.join(".")}"
35
+ end
36
+ end
37
+ a.flatten
38
+ end
39
39
 
40
- end
41
-
42
- def self.included(receiver)
43
- receiver.extend ClassMethods
44
- receiver.send :include, InstanceMethods
45
- end
40
+ end
41
+
42
+ def self.included(receiver)
43
+ receiver.extend ClassMethods
44
+ receiver.send :include, InstanceMethods
45
+ end
46
46
  end
47
47
 
48
48
  HashExtensions.included(Hash)
@@ -1,49 +1,49 @@
1
1
  module EventHub
2
2
 
3
- module Helper
4
-
5
- # converts a class like EventHub::PlateStore::MyClassName to an array ['event_hub','plate_store','my_class_name']
6
- def class_to_array(class_name)
7
- class_name.to_s.split("::").map{ |m| m.gsub(/[A-Z]/) { |c| "_#{c}"}.gsub(/^_/,"").downcase }
8
- end
9
-
10
- # replaces CR, LF, CRLF with ";" and cut's string to requied length by adding "..." if string would be longer
11
- def format_string(message,max_characters=80)
12
- max_characters = 5 if max_characters < 5
13
- m = message.gsub(/\r\n|\n|\r/m,";")
14
- return (m[0..max_characters-4] + "...") if m.size > max_characters
15
- return m
16
- end
17
-
18
- def now_stamp(now=nil)
19
- now ||= Time.now
3
+ module Helper
4
+
5
+ # converts a class like EventHub::PlateStore::MyClassName to an array ['event_hub','plate_store','my_class_name']
6
+ def class_to_array(class_name)
7
+ class_name.to_s.split("::").map{ |m| m.gsub(/[A-Z]/) { |c| "_#{c}"}.gsub(/^_/,"").downcase }
8
+ end
9
+
10
+ # replaces CR, LF, CRLF with ";" and cut's string to requied length by adding "..." if string would be longer
11
+ def format_string(message,max_characters=80)
12
+ max_characters = 5 if max_characters < 5
13
+ m = message.gsub(/\r\n|\n|\r/m,";")
14
+ return (m[0..max_characters-4] + "...") if m.size > max_characters
15
+ return m
16
+ end
17
+
18
+ def now_stamp(now=nil)
19
+ now ||= Time.now
20
20
  now.utc.strftime("%Y-%m-%dT%H:%M:%S.#{now.usec}Z")
21
- end
22
-
23
- def duration(difference)
24
- negative = difference < 0
25
- difference = difference.abs
26
-
27
- rest, secs = difference.divmod( 60 ) # self is the time difference t2 - t1
28
- rest, mins = rest.divmod( 60 )
29
- days, hours = rest.divmod( 24 )
30
- secs = secs.truncate
31
- milliseconds = ((difference - difference.truncate)*1000).round
32
-
33
- result = []
34
- result << "#{days} days" if days > 1
35
- result << "#{days} day" if days == 1
36
- result << "#{hours} hours" if hours > 1
37
- result << "#{hours} hour" if hours == 1
38
- result << "#{mins} minutes" if mins > 1
39
- result << "#{mins} minute" if mins == 1
40
- result << "#{secs} seconds" if secs > 1
41
- result << "#{secs} second" if secs == 1
42
- result << "#{milliseconds} milliseconds" if milliseconds > 1
43
- result << "#{milliseconds} millisecond" if milliseconds == 1
44
- return (negative ? "-" : "") + result.join(' ')
45
21
  end
46
22
 
47
- end
23
+ def duration(difference)
24
+ negative = difference < 0
25
+ difference = difference.abs
26
+
27
+ rest, secs = difference.divmod( 60 ) # self is the time difference t2 - t1
28
+ rest, mins = rest.divmod( 60 )
29
+ days, hours = rest.divmod( 24 )
30
+ secs = secs.truncate
31
+ milliseconds = ((difference - difference.truncate)*1000).round
32
+
33
+ result = []
34
+ result << "#{days} days" if days > 1
35
+ result << "#{days} day" if days == 1
36
+ result << "#{hours} hours" if hours > 1
37
+ result << "#{hours} hour" if hours == 1
38
+ result << "#{mins} minutes" if mins > 1
39
+ result << "#{mins} minute" if mins == 1
40
+ result << "#{secs} seconds" if secs > 1
41
+ result << "#{secs} second" if secs == 1
42
+ result << "#{milliseconds} milliseconds" if milliseconds > 1
43
+ result << "#{milliseconds} millisecond" if milliseconds == 1
44
+ return (negative ? "-" : "") + result.join(' ')
45
+ end
46
+
47
+ end
48
48
 
49
49
  end
@@ -1,2 +1,2 @@
1
1
  class EventHub::NoDeadletterException < EventHub::BaseException
2
- end
2
+ end
@@ -1,269 +1,269 @@
1
1
  module EventHub
2
- class Processor
3
- attr_reader :statistics, :name, :pidfile, :exception_writer
4
-
5
- include Helper
6
-
7
- def version
8
- "1.0.0"
9
- end
10
-
11
- def initialize(name=nil)
12
- @name = name || class_to_array(self.class)[1..-1].join(".")
13
- @pidfile = EventHub::Components::Pidfile.new(File.join(Dir.pwd, 'pids', "#{@name}.pid"))
14
- @exception_writer = EventHub::Components::ExceptionWriter.new
15
- @statistics = EventHub::Statistics.new
16
- @heartbeat = EventHub::Heartbeat.new(self)
17
- @message_processor = EventHub::MessageProcessor.new(self)
18
-
19
- @channel_receiver = nil
20
- @channel_sender = nil
21
- @restart = true
22
- end
23
-
24
- def configuration
25
- EventHub::Configuration.instance.data
26
- end
27
-
28
- def server_host
29
- configuration.get('server.host') || 'localhost'
30
- end
31
-
32
- def server_user
33
- configuration.get('server.user') || 'admin'
34
- end
35
-
36
- def server_password
37
- configuration.get('server.password') || 'admin'
38
- end
39
-
40
- def server_management_port
41
- configuration.get('server.management_port') || 15672
42
- end
43
-
44
- def server_vhost
45
- configuration.get('server.vhost') || 'event_hub'
46
- end
47
-
48
- def connection_settings
49
- { user: server_user, password: server_password, host: server_host, vhost: server_vhost }
50
- end
51
-
52
- def listener_queues
53
- Array(
54
- configuration.get('processor.listener_queue') ||
55
- configuration.get('processor.listener_queues') ||
56
- 'undefined_listener_queues'
57
- )
58
- end
59
-
60
- def watchdog_cycle_in_s
61
- configuration.get('processor.watchdog_cycle_is_s') || 15
62
- end
63
-
64
- def restart_in_s
65
- configuration.get('processor.restart_in_s') || 15
66
- end
67
-
68
- def heartbeat_cycle_in_s
69
- configuration.get('processor.heartbeat_cycle_in_s') || 300
70
- end
71
-
72
- def start(detached = false)
73
- daemonize if detached
74
-
75
- EventHub.logger.info("Processor [#{@name}] base folder [#{Dir.pwd}]")
76
-
77
- # use timer here to have last heartbeat message working
78
- Signal.trap("TERM") { EventMachine.add_timer(0) { about_to_stop } }
79
- Signal.trap("INT") { EventMachine.add_timer(0) { about_to_stop } }
80
-
81
- while @restart
82
- begin
83
- handle_start_internal
84
-
85
- # custom post start method to be overwritten
86
- post_start
87
-
88
- rescue => e
89
- id = @exception_writer.write(e)
90
- EventHub.logger.error("Unexpected exception: #{e}, see => #{id}. Trying to restart in #{self.restart_in_s} seconds...")
91
- sleep_break self.restart_in_s
92
- end
93
- end # while
94
-
95
- # custon post stop method to be overwritten
96
- post_stop
97
-
98
- EventHub.logger.info("Processor [#{@name}] has been stopped")
99
- ensure
100
- pidfile.delete
101
- end
102
-
103
- def handle_message(metadata, payload)
104
- raise "Please implement method in derived class"
105
- end
106
-
107
- def watchdog
108
- self.listener_queues.each do |queue_name|
109
- begin
110
- response = RestClient.get "http://#{self.server_user}:#{self.server_password}@#{self.server_host}:#{self.server_management_port}/api/queues/#{self.server_vhost}/#{queue_name}/bindings", { :content_type => :json}
111
- data = JSON.parse(response.body)
112
-
113
- if response.code != 200
114
- EventHub.logger.warn("Watchdog: Server did not answered properly. Trying to restart in #{self.restart_in_s} seconds...")
115
- EventMachine.add_timer(self.restart_in_s) { stop_processor(true) }
116
- elsif data.size == 0
117
- EventHub.logger.warn("Watchdog: Something is wrong with the vhost, queue [#{queue_name}], and/or bindings. Trying to restart in #{self.restart_in_s} seconds...")
118
- EventMachine.add_timer(self.restart_in_s) { stop_processor(true) }
119
- # does it make sence ? Needs maybe more checks in future
120
- else
121
- # Watchdog is happy :-)
122
- # add timer for next check
123
- EventMachine.add_timer(self.watchdog_cycle_in_s) { watchdog }
124
- end
125
-
126
- rescue => e
127
- EventHub.logger.error("Watchdog: Unexpected exception: #{e}. Trying to restart in #{self.restart_in_s} seconds...")
128
- stop_processor
129
- end
130
- end
131
- end
132
-
133
- # send message
134
- def send_message(message, exchange_name = EventHub::EH_X_INBOUND)
135
-
136
- if @channel_sender.nil? || !@channel_sender.open?
137
- @channel_sender = AMQP::Channel.new(@connection, prefetch: 1)
138
-
139
- # use publisher confirm
140
- @channel_sender.confirm_select
141
-
142
- # @channel.on_error { |ch, channel_close| EventHub.logger.error "Oops! a channel-level exception: #{channel_close.reply_text}" }
2
+ class Processor
3
+ attr_reader :statistics, :name, :pidfile, :exception_writer
4
+
5
+ include Helper
6
+
7
+ def version
8
+ '1.0.0'
9
+ end
10
+
11
+ def initialize(name=nil)
12
+ @name = name || class_to_array(self.class)[1..-1].join('.')
13
+ @pidfile = EventHub::Components::Pidfile.new(File.join(Dir.pwd, 'pids', "#{@name}.pid"))
14
+ @exception_writer = EventHub::Components::ExceptionWriter.new
15
+ @statistics = EventHub::Statistics.new
16
+ @heartbeat = EventHub::Heartbeat.new(self)
17
+ @message_processor = EventHub::MessageProcessor.new(self)
18
+
19
+ @channel_receiver = nil
20
+ @channel_sender = nil
21
+ @restart = true
22
+ end
23
+
24
+ def configuration
25
+ EventHub::Configuration.instance.data
26
+ end
27
+
28
+ def server_host
29
+ configuration.get('server.host') || 'localhost'
30
+ end
31
+
32
+ def server_user
33
+ configuration.get('server.user') || 'admin'
34
+ end
35
+
36
+ def server_password
37
+ configuration.get('server.password') || 'admin'
38
+ end
39
+
40
+ def server_management_port
41
+ configuration.get('server.management_port') || 15672
42
+ end
43
+
44
+ def server_vhost
45
+ configuration.get('server.vhost') || 'event_hub'
46
+ end
47
+
48
+ def connection_settings
49
+ { user: server_user, password: server_password, host: server_host, vhost: server_vhost }
50
+ end
51
+
52
+ def listener_queues
53
+ Array(
54
+ configuration.get('processor.listener_queue') ||
55
+ configuration.get('processor.listener_queues') ||
56
+ 'undefined_listener_queues'
57
+ )
58
+ end
59
+
60
+ def watchdog_cycle_in_s
61
+ configuration.get('processor.watchdog_cycle_is_s') || 15
62
+ end
63
+
64
+ def restart_in_s
65
+ configuration.get('processor.restart_in_s') || 15
66
+ end
67
+
68
+ def heartbeat_cycle_in_s
69
+ configuration.get('processor.heartbeat_cycle_in_s') || 300
70
+ end
71
+
72
+ def start(detached = false)
73
+ daemonize if detached
74
+
75
+ EventHub.logger.info("Processor [#{@name}] base folder [#{Dir.pwd}]")
76
+
77
+ # use timer here to have last heartbeat message working
78
+ Signal.trap('TERM') { EventMachine.add_timer(0) { about_to_stop } }
79
+ Signal.trap('INT') { EventMachine.add_timer(0) { about_to_stop } }
80
+
81
+ while @restart
82
+ begin
83
+ handle_start_internal
84
+
85
+ # custom post start method to be overwritten
86
+ post_start
87
+
88
+ rescue => e
89
+ id = @exception_writer.write(e)
90
+ EventHub.logger.error("Unexpected exception: #{e}, see => #{id}. Trying to restart in #{self.restart_in_s} seconds...")
91
+ sleep_break self.restart_in_s
92
+ end
93
+ end # while
94
+
95
+ # custon post stop method to be overwritten
96
+ post_stop
97
+
98
+ EventHub.logger.info("Processor [#{@name}] has been stopped")
99
+ ensure
100
+ pidfile.delete
101
+ end
102
+
103
+ def handle_message(metadata, payload)
104
+ raise 'Please implement method in derived class'
105
+ end
106
+
107
+ def watchdog
108
+ self.listener_queues.each do |queue_name|
109
+ begin
110
+ response = RestClient.get "http://#{self.server_user}:#{self.server_password}@#{self.server_host}:#{self.server_management_port}/api/queues/#{self.server_vhost}/#{queue_name}/bindings", { :content_type => :json}
111
+ data = JSON.parse(response.body)
112
+
113
+ if response.code != 200
114
+ EventHub.logger.warn("Watchdog: Server did not answered properly. Trying to restart in #{self.restart_in_s} seconds...")
115
+ EventMachine.add_timer(self.restart_in_s) { stop_processor(true) }
116
+ elsif data.size == 0
117
+ EventHub.logger.warn("Watchdog: Something is wrong with the vhost, queue [#{queue_name}], and/or bindings. Trying to restart in #{self.restart_in_s} seconds...")
118
+ EventMachine.add_timer(self.restart_in_s) { stop_processor(true) }
119
+ # does it make sence ? Needs maybe more checks in future
120
+ else
121
+ # Watchdog is happy :-)
122
+ # add timer for next check
123
+ EventMachine.add_timer(self.watchdog_cycle_in_s) { watchdog }
124
+ end
125
+
126
+ rescue => e
127
+ EventHub.logger.error("Watchdog: Unexpected exception: #{e}. Trying to restart in #{self.restart_in_s} seconds...")
128
+ stop_processor
129
+ end
130
+ end
131
+ end
132
+
133
+ # send message
134
+ def send_message(message, exchange_name = EventHub::EH_X_INBOUND)
135
+
136
+ if @channel_sender.nil? || !@channel_sender.open?
137
+ @channel_sender = AMQP::Channel.new(@connection, prefetch: 1)
138
+
139
+ # use publisher confirm
140
+ @channel_sender.confirm_select
141
+
142
+ # @channel.on_error { |ch, channel_close| EventHub.logger.error "Oops! a channel-level exception: #{channel_close.reply_text}" }
143
143
  # @channel.on_ack { |basic_ack| EventHub.logger.info "Received basic_ack: multiple = #{basic_ack.multiple}, delivery_tag = #{basic_ack.delivery_tag}" }
144
- end
145
-
146
- exchange = @channel_sender.direct(exchange_name, :durable => true, :auto_delete => false)
147
- exchange.publish(message.to_json, :persistent => true)
148
- end
149
-
150
- def sleep_break(seconds) # breaks after n seconds or after interrupt
151
- while (seconds > 0)
152
- sleep(1)
153
- seconds -= 1
154
- break unless @restart
155
- end
156
- end
157
-
158
- private
159
-
160
- def handle_start_internal
161
- AMQP.start(self.connection_settings) do |connection, open_ok|
162
- @connection = connection
163
-
164
- handle_connection_loss
165
-
166
- # create channel
167
- @channel_receiver = AMQP::Channel.new(@connection, prefetch: 1)
168
-
169
- self.listener_queues.each do |queue_name|
170
-
171
- # connect to queue
172
- queue = @channel_receiver.queue(queue_name, durable: true, auto_delete: false)
173
-
174
- # subscribe to queue
175
- queue.subscribe(:ack => true) do |metadata, payload|
176
- begin
177
- statistics.measure(payload.size) do
178
- messages_to_send = @message_processor.process({ metadata: metadata, queue_name: queue_name}, payload)
179
-
180
- # forward invalid or returned messages to dispatcher
181
- messages_to_send.each do |message|
182
- send_message(message)
183
- end if messages_to_send
184
-
185
- @channel_receiver.acknowledge(metadata.delivery_tag)
186
- end
187
-
188
- rescue EventHub::NoDeadletterException => e
189
- @channel_receiver.reject(metadata.delivery_tag, true)
190
- EventHub.logger.error("Unexpected exception in handle_message method: #{e}. Message will be requeued.")
191
- @exception_writer.write(e)
192
- sleep_break self.restart_in_s
193
- rescue => e
194
- @channel_receiver.reject(metadata.delivery_tag, false)
195
- EventHub.logger.error("Unexpected exception in handle_message method: #{e}. Message dead lettered.")
196
- @exception_writer.write(e)
197
- end
198
- end
199
-
200
- end
201
-
202
- EventHub.logger.info("Processor [#{@name}] is listening to vhost [#{self.server_vhost}], queues [#{self.listener_queues.join(", ")}]")
203
-
204
- register_timers
205
-
206
- # send first heartbeat
207
- heartbeat
208
- end
209
- end
210
-
211
- def handle_connection_loss
212
- @connection.on_tcp_connection_loss do |conn, settings|
213
- EventHub.logger.warn("Processor lost tcp connection. Trying to restart in #{self.restart_in_s} seconds...")
214
- stop_processor(true)
215
- end
216
- end
217
-
218
- def register_timers
219
- EventMachine.add_timer(watchdog_cycle_in_s) { watchdog }
220
- EventMachine.add_periodic_timer(heartbeat_cycle_in_s) { heartbeat }
221
- end
222
-
223
- def heartbeat(action="running")
224
- message = @heartbeat.build_message(action)
225
- message.append_to_execution_history(@name)
226
- send_message(message)
227
- end
228
-
229
- def about_to_stop
230
- heartbeat("stopped")
231
- stop_processor
232
- end
233
-
234
- def stop_processor(restart=false)
235
- @restart = restart
236
-
237
- # close channels
238
- [@channel_receiver,@channel_sender].each do |channel|
239
- if channel
240
- channel.close if channel.open?
241
- end
242
- end
243
-
244
- # stop connection and event loop
245
- if @connection
246
- @connection.disconnect if @connection.connected?
247
- EventMachine.stop if EventMachine.reactor_running?
248
- end
249
- end
250
-
251
- def daemonize
252
- EventHub.logger.info("Processor [#{@name}] is going to start as daemon")
253
-
254
- # daemonize
255
- Process.daemon
256
-
257
- @pidfile.write(Process.pid.to_s)
258
- end
259
-
260
- def post_start
261
- # method which can be overwritten to call a code sequence after reactor start
262
- end
263
-
264
- def post_stop
265
- # method which can be overwritten to call a code sequence after reactor stop
266
- end
267
-
268
- end
144
+ end
145
+
146
+ exchange = @channel_sender.direct(exchange_name, :durable => true, :auto_delete => false)
147
+ exchange.publish(message.to_json, :persistent => true)
148
+ end
149
+
150
+ def sleep_break(seconds) # breaks after n seconds or after interrupt
151
+ while (seconds > 0)
152
+ sleep(1)
153
+ seconds -= 1
154
+ break unless @restart
155
+ end
156
+ end
157
+
158
+ private
159
+
160
+ def handle_start_internal
161
+ AMQP.start(self.connection_settings) do |connection, open_ok|
162
+ @connection = connection
163
+
164
+ handle_connection_loss
165
+
166
+ # create channel
167
+ @channel_receiver = AMQP::Channel.new(@connection, prefetch: 1)
168
+
169
+ self.listener_queues.each do |queue_name|
170
+
171
+ # connect to queue
172
+ queue = @channel_receiver.queue(queue_name, durable: true, auto_delete: false)
173
+
174
+ # subscribe to queue
175
+ queue.subscribe(:ack => true) do |metadata, payload|
176
+ begin
177
+ statistics.measure(payload.size) do
178
+ messages_to_send = @message_processor.process({ metadata: metadata, queue_name: queue_name}, payload)
179
+
180
+ # forward invalid or returned messages to dispatcher
181
+ messages_to_send.each do |message|
182
+ send_message(message)
183
+ end if messages_to_send
184
+
185
+ @channel_receiver.acknowledge(metadata.delivery_tag)
186
+ end
187
+
188
+ rescue EventHub::NoDeadletterException => e
189
+ @channel_receiver.reject(metadata.delivery_tag, true)
190
+ EventHub.logger.error("Unexpected exception in handle_message method: #{e}. Message will be requeued.")
191
+ @exception_writer.write(e)
192
+ sleep_break self.restart_in_s
193
+ rescue => e
194
+ @channel_receiver.reject(metadata.delivery_tag, false)
195
+ EventHub.logger.error("Unexpected exception in handle_message method: #{e}. Message dead lettered.")
196
+ @exception_writer.write(e)
197
+ end
198
+ end
199
+
200
+ end
201
+
202
+ EventHub.logger.info("Processor [#{@name}] is listening to vhost [#{self.server_vhost}], queues [#{self.listener_queues.join(', ')}]")
203
+
204
+ register_timers
205
+
206
+ # send first heartbeat
207
+ heartbeat
208
+ end
209
+ end
210
+
211
+ def handle_connection_loss
212
+ @connection.on_tcp_connection_loss do |conn, settings|
213
+ EventHub.logger.warn("Processor lost tcp connection. Trying to restart in #{self.restart_in_s} seconds...")
214
+ stop_processor(true)
215
+ end
216
+ end
217
+
218
+ def register_timers
219
+ EventMachine.add_timer(watchdog_cycle_in_s) { watchdog }
220
+ EventMachine.add_periodic_timer(heartbeat_cycle_in_s) { heartbeat }
221
+ end
222
+
223
+ def heartbeat(action = 'running')
224
+ message = @heartbeat.build_message(action)
225
+ message.append_to_execution_history(@name)
226
+ send_message(message)
227
+ end
228
+
229
+ def about_to_stop
230
+ heartbeat('stopped')
231
+ stop_processor
232
+ end
233
+
234
+ def stop_processor(restart=false)
235
+ @restart = restart
236
+
237
+ # close channels
238
+ [@channel_receiver, @channel_sender].each do |channel|
239
+ if channel
240
+ channel.close if channel.open?
241
+ end
242
+ end
243
+
244
+ # stop connection and event loop
245
+ if @connection
246
+ @connection.disconnect if @connection.connected?
247
+ EventMachine.stop if EventMachine.reactor_running?
248
+ end
249
+ end
250
+
251
+ def daemonize
252
+ EventHub.logger.info("Processor [#{@name}] is going to start as daemon")
253
+
254
+ # daemonize
255
+ Process.daemon
256
+
257
+ @pidfile.write(Process.pid.to_s)
258
+ end
259
+
260
+ def post_start
261
+ # method which can be overwritten to call a code sequence after reactor start
262
+ end
263
+
264
+ def post_stop
265
+ # method which can be overwritten to call a code sequence after reactor stop
266
+ end
267
+
268
+ end
269
269
  end
@@ -1,3 +1,3 @@
1
1
  module EventHub
2
- VERSION = "0.4.6"
2
+ VERSION = '0.4.7'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: eventhub-processor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.6
4
+ version: 0.4.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thomas Steiner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-12-19 00:00:00.000000000 Z
11
+ date: 2015-01-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -217,3 +217,4 @@ signing_key:
217
217
  specification_version: 4
218
218
  summary: Gem to build Event Hub processors
219
219
  test_files: []
220
+ has_rdoc: