eventhub-processor 0.4.6 → 0.4.7

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: 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: