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 +4 -4
- data/lib/eventhub/argument_parser.rb +1 -1
- data/lib/eventhub/base_exception.rb +1 -1
- data/lib/eventhub/configuration.rb +12 -4
- data/lib/eventhub/constant.rb +8 -8
- data/lib/eventhub/hash_extensions.rb +39 -39
- data/lib/eventhub/helper.rb +42 -42
- data/lib/eventhub/no_deadletter_exception.rb +1 -1
- data/lib/eventhub/processor.rb +266 -266
- data/lib/eventhub/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 42433583bf8e36e1fea7398bafb1300823a2802e
|
|
4
|
+
data.tar.gz: 22ab273d6c45e7749dba4e704c3dddc729d00596
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fde68540abd486a545855d14f618a6a001ad5ebb6003159a77ab4150de7befbcf19f9a855bf27d854087c52dcb2fe474c29f43ea87aac1cab09427c6d6aabf99
|
|
7
|
+
data.tar.gz: aba07cd1286c02e6a672a55730c2be08e2b25506b45d721f38f655aba07f0e7923941145e62b231ceca49d4695cc98810dd9e3876fa3ee3525cb3621a23e266d
|
|
@@ -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 =
|
|
10
|
+
@data = {}
|
|
11
11
|
@environment = 'development'
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def load_file(input, env = 'development')
|
|
15
|
-
|
|
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("
|
|
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
|
data/lib/eventhub/constant.rb
CHANGED
|
@@ -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
|
-
|
|
4
|
-
|
|
3
|
+
module ClassMethods
|
|
4
|
+
end
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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)
|
data/lib/eventhub/helper.rb
CHANGED
|
@@ -1,49 +1,49 @@
|
|
|
1
1
|
module EventHub
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
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
|
data/lib/eventhub/processor.rb
CHANGED
|
@@ -1,269 +1,269 @@
|
|
|
1
1
|
module EventHub
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
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
|
data/lib/eventhub/version.rb
CHANGED
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.
|
|
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:
|
|
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:
|