eventhub-processor2 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,158 @@
1
+ # EventHub module
2
+ module EventHub
3
+ # Message class
4
+ class Message
5
+ include Helper
6
+
7
+ VERSION = '1.0.0'.freeze
8
+
9
+ # Headers that are required (value can be nil) in order to pass valid?
10
+ REQUIRED_HEADERS = [
11
+ 'message_id',
12
+ 'version',
13
+ 'created_at',
14
+ 'origin.module_id',
15
+ 'origin.type',
16
+ 'origin.site_id',
17
+ 'process.name',
18
+ 'process.step_position',
19
+ 'process.execution_id',
20
+ 'status.retried_count',
21
+ 'status.code',
22
+ 'status.message'
23
+ ].freeze
24
+
25
+ attr_accessor :header, :body, :raw, :vhost, :routing_key
26
+
27
+ # Build accessors for all required headers
28
+ REQUIRED_HEADERS.each do |header|
29
+ name = header.tr('.', '_')
30
+
31
+ define_method(name) do
32
+ self.header.get(header)
33
+ end
34
+
35
+ define_method("#{name}=") do |value|
36
+ self.header.set(header, value)
37
+ end
38
+ end
39
+
40
+ def self.from_json(raw)
41
+ data = JSON.parse(raw)
42
+ Message.new(data.get('header'), data.get('body'), raw)
43
+ rescue => e
44
+ Message.new(
45
+ {
46
+ 'status' =>
47
+ {
48
+ 'code' => STATUS_INVALID,
49
+ 'message' => "JSON parse error: #{e}"
50
+ }
51
+ },
52
+ {
53
+ 'original_message_base64_encoded' => Base64.encode64(raw)
54
+ },
55
+ raw
56
+ )
57
+ end
58
+
59
+ def initialize(header = nil, body = nil, raw = nil)
60
+ @header = header || {}
61
+ @body = body || {}
62
+ @raw = raw
63
+
64
+ # set message defaults, that we have required headers
65
+ @header.set('message_id', UUIDTools::UUID.timestamp_create.to_s, false)
66
+ @header.set('version', VERSION, false)
67
+ @header.set('created_at', now_stamp, false)
68
+
69
+ @header.set('origin.module_id', 'undefined', false)
70
+ @header.set('origin.type', 'undefined', false)
71
+ @header.set('origin.site_id', 'undefined', false)
72
+
73
+ @header.set('process.name', 'undefined', false)
74
+ @header.set('process.execution_id',
75
+ UUIDTools::UUID.timestamp_create.to_s, false)
76
+ @header.set('process.step_position', 0, false)
77
+
78
+ @header.set('status.retried_count', 0, false)
79
+ @header.set('status.code', STATUS_INITIAL, false)
80
+ @header.set('status.message', '', false)
81
+ end
82
+
83
+ def valid?
84
+ # check for existence and defined value
85
+ REQUIRED_HEADERS.all? do |key|
86
+ @header.all_keys_with_path.include?(key) &&
87
+ !send(key.tr('.', '_').to_sym).nil?
88
+ end
89
+ end
90
+
91
+ def success?
92
+ status_code == STATUS_SUCCESS
93
+ end
94
+
95
+ def retry?
96
+ status_code == STATUS_RETRY
97
+ end
98
+
99
+ def initial?
100
+ status_code == STATUS_INITIAL
101
+ end
102
+
103
+ def retry_pending?
104
+ status_code == STATUS_RETRY_PENDING
105
+ end
106
+
107
+ def invalid?
108
+ status_code == STATUS_INVALID
109
+ end
110
+
111
+ def schedule?
112
+ status_code == STATUS_SCHEDULE
113
+ end
114
+
115
+ def schedule_retry?
116
+ status_code == STATUS_SCHEDULE_RETRY
117
+ end
118
+
119
+ def schedule_pending?
120
+ status_code == STATUS_SCHEDULE_PENDING
121
+ end
122
+
123
+ def to_json
124
+ { 'header' => header, 'body' => body }.to_json
125
+ end
126
+
127
+ def to_s
128
+ 'Msg: process '\
129
+ "[#{process_name}, #{process_step_position}, #{process_execution_id}]"\
130
+ ", status [#{status_code},#{status_message},#{status_retried_count}]"
131
+ end
132
+
133
+ # copies the message and set's provided status code (default: success),
134
+ # actual stamp, and a new message id
135
+ def copy(status_code = STATUS_SUCCESS)
136
+ # use Marshal dump and load to make a deep object copy
137
+ copied_header = Marshal.load(Marshal.dump(header))
138
+ copied_body = Marshal.load(Marshal.dump(body))
139
+
140
+ copied_header.set('message_id', UUIDTools::UUID.timestamp_create.to_s)
141
+ copied_header.set('created_at', now_stamp)
142
+ copied_header.set('status.code', status_code)
143
+
144
+ Message.new(copied_header, copied_body)
145
+ end
146
+
147
+ def append_to_execution_history(processor_name)
148
+ header.set('execution_history', []) unless \
149
+ header.get('execution_history')
150
+ header.get('execution_history') << \
151
+ { 'processor' => processor_name, 'timestamp' => now_stamp }
152
+ end
153
+
154
+ def self.translate_status_code(code)
155
+ STATUS_CODE_TRANSLATION[code]
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,121 @@
1
+ # EventHub module
2
+ module EventHub
3
+ # Processor2 class
4
+ class Processor2
5
+ include Helper
6
+
7
+ SIGNALS_FOR_TERMINATION = [:INT, :TERM, :QUIT]
8
+ SIGNALS_FOR_RELOAD_CONFIG = [:HUP]
9
+ ALL_SIGNALS = SIGNALS_FOR_TERMINATION + SIGNALS_FOR_RELOAD_CONFIG
10
+
11
+ attr_reader :started_at, :statistics
12
+
13
+ def initialize(args = {})
14
+ # Set processor name
15
+ EventHub::Configuration.name = args[:name] ||
16
+ get_name_from_class(self)
17
+
18
+ # Parse comand line options
19
+ EventHub::Configuration.parse_options
20
+
21
+ # Load configuration file
22
+ EventHub::Configuration.load!(args)
23
+
24
+ @command_queue = []
25
+
26
+ @started_at = Time.now
27
+ @statistics = EventHub::Statistics.new
28
+ end
29
+
30
+ def start
31
+ EventHub.logger.info("#{Configuration.name} (#{version}): has been started")
32
+
33
+ before_start
34
+ main_event_loop
35
+ after_stop
36
+
37
+ EventHub.logger.info("#{Configuration.name} (#{version}): has been stopped")
38
+ rescue => ex
39
+ EventHub.logger.error("Unexpected error in Processor2.start: #{ex}")
40
+ end
41
+
42
+ def stop
43
+ # used by rspec
44
+ @command_queue << :TERM
45
+ end
46
+
47
+ def version
48
+ EventHub::VERSION
49
+ end
50
+
51
+ # get message as EventHub::Message class instance
52
+ # args contain :queue_name, :content_type, :priority, :delivery_tag
53
+ def handle_message(_message, _args = {})
54
+ raise 'need to be implemented in derived class'
55
+ end
56
+
57
+ # pass message: '{ "header": ... , "body": { .. }}'
58
+ # and optionally exchange_name: 'your exchange name'
59
+ def publish(args = {})
60
+ Celluloid::Actor[:actor_listener].publish(args)
61
+ end
62
+
63
+ def before_start
64
+ # can be implemented in derived class
65
+ end
66
+
67
+ def after_stop
68
+ # can be implemented in derived class
69
+ end
70
+
71
+ private
72
+
73
+ def setup_signal_handler
74
+ # have a re-entrant signal handler by just using a simple array
75
+ # https://www.sitepoint.com/the-self-pipe-trick-explained/
76
+ ALL_SIGNALS.each do |signal|
77
+ Signal.trap(signal) { @command_queue << signal }
78
+ end
79
+ end
80
+
81
+ def start_supervisor
82
+ @config = Celluloid::Supervision::Configuration.define([
83
+ {type: ActorHeartbeat, as: :actor_heartbeat, args: [ self ]},
84
+ {type: ActorListener, as: :actor_listener, args: [ self ]}
85
+ ])
86
+
87
+ @config.injection!(:before_restart, proc do
88
+ EventHub.logger.info('Restarting in 10 seconds...')
89
+ sleep 10
90
+ end )
91
+
92
+ @config.deploy
93
+ end
94
+
95
+ def main_event_loop
96
+ setup_signal_handler
97
+ start_supervisor
98
+
99
+ loop do
100
+ command = @command_queue.pop
101
+ case
102
+ when SIGNALS_FOR_TERMINATION.include?(command)
103
+ EventHub.logger.info("Command [#{command}] received")
104
+ break
105
+ when SIGNALS_FOR_RELOAD_CONFIG.include?(command)
106
+ EventHub::Configuration.load!
107
+ EventHub.logger.info('Configuration file reloaded')
108
+ Celluloid::Actor[:actor_listener].async.restart
109
+ else
110
+ sleep 0.5
111
+ end
112
+ end
113
+
114
+ Celluloid.shutdown
115
+ # make sure all actors are gone
116
+ while Celluloid.running?
117
+ sleep 0.1
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,53 @@
1
+ class EventHub::Statistics
2
+ attr_reader :messages_successful, :messages_unsuccessful, :messages_average_size, :messages_average_process_time
3
+
4
+ def initialize
5
+ @messages_successful = 0
6
+ @messages_unsuccessful = 0
7
+ @messages_average_size = 0
8
+ @messages_average_process_time = 0
9
+ @messages_total_process_time = 0
10
+ @mutex = Mutex.new
11
+ end
12
+
13
+
14
+ def measure(size, &block)
15
+ begin
16
+ start = Time.now
17
+ yield
18
+ success(Time.now - start, size)
19
+ rescue
20
+ failure
21
+ raise
22
+ end
23
+ end
24
+
25
+ def success(process_time, size)
26
+ @mutex.lock
27
+ @messages_total_process_time += process_time
28
+ @messages_average_process_time = (messages_total_process_time + process_time) / (messages_successful + 1).to_f
29
+ @messages_average_size = (messages_total_size + size) / (messages_successful + 1).to_f
30
+ @messages_successful += 1
31
+ ensure
32
+ @mutex.unlock
33
+ end
34
+
35
+ def failure
36
+ @mutex.lock
37
+ @messages_unsuccessful += 1
38
+ ensure
39
+ @mutex.unlock
40
+ end
41
+
42
+ def messages_total
43
+ messages_unsuccessful + messages_successful
44
+ end
45
+
46
+ def messages_total_process_time
47
+ messages_average_process_time * messages_successful
48
+ end
49
+
50
+ def messages_total_size
51
+ messages_average_size * messages_successful
52
+ end
53
+ end
@@ -0,0 +1,3 @@
1
+ module EventHub
2
+ VERSION = '1.0.0'.freeze
3
+ end
data/lib/eventhub.rb ADDED
@@ -0,0 +1,25 @@
1
+ require 'uuidtools'
2
+ require 'json'
3
+ require 'base64'
4
+
5
+ require 'eventhub/components'
6
+ require 'logstash-logger'
7
+ require 'bunny'
8
+ require 'celluloid/current'
9
+
10
+ require_relative 'eventhub/version'
11
+ require_relative 'eventhub/constant'
12
+ require_relative 'eventhub/logger'
13
+ require_relative 'eventhub/helper'
14
+ require_relative 'eventhub/hash_extensions'
15
+ require_relative 'eventhub/configuration'
16
+ require_relative 'eventhub/message'
17
+ require_relative 'eventhub/statistics'
18
+ require_relative 'eventhub/consumer'
19
+ require_relative 'eventhub/actor_heartbeat'
20
+ require_relative 'eventhub/actor_watchdog'
21
+ require_relative 'eventhub/actor_listener'
22
+ require_relative 'eventhub/processor2'
23
+
24
+ Celluloid.logger = nil
25
+ Celluloid.exception_handler { |ex| EventHub.logger.info "Exception occured: #{ex}" }
metadata ADDED
@@ -0,0 +1,190 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: eventhub-processor2
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Steiner, Thomas
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-12-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: celluloid
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.17'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.17'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bunny
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.7'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.7'
41
+ - !ruby/object:Gem::Dependency
42
+ name: eventhub-components
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.2'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.2'
55
+ - !ruby/object:Gem::Dependency
56
+ name: uuidtools
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2.1'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2.1'
69
+ - !ruby/object:Gem::Dependency
70
+ name: bundler
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.16'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.16'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '12.2'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '12.2'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '3.7'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '3.7'
111
+ - !ruby/object:Gem::Dependency
112
+ name: simplecov
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '0.15'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '0.15'
125
+ description: Next generation gem to build ruby based eventhub processor
126
+ email:
127
+ - thomas.steiner@ikey.ch
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - ".gitignore"
133
+ - ".rspec"
134
+ - ".travis.yml"
135
+ - Gemfile
136
+ - Gemfile.lock
137
+ - LICENSE.txt
138
+ - README.md
139
+ - Rakefile
140
+ - bin/console
141
+ - bin/setup
142
+ - docker/Dockerfile
143
+ - docker/README.md
144
+ - docker/definitions.json
145
+ - docker/rabbitmq.config
146
+ - eventhub-processor2.gemspec
147
+ - example/README.md
148
+ - example/config/example.json
149
+ - example/crasher.rb
150
+ - example/example.rb
151
+ - example/publisher.rb
152
+ - lib/eventhub.rb
153
+ - lib/eventhub/actor_heartbeat.rb
154
+ - lib/eventhub/actor_listener.rb
155
+ - lib/eventhub/actor_watchdog.rb
156
+ - lib/eventhub/configuration.rb
157
+ - lib/eventhub/constant.rb
158
+ - lib/eventhub/consumer.rb
159
+ - lib/eventhub/hash_extensions.rb
160
+ - lib/eventhub/helper.rb
161
+ - lib/eventhub/logger.rb
162
+ - lib/eventhub/message.rb
163
+ - lib/eventhub/processor2.rb
164
+ - lib/eventhub/statistics.rb
165
+ - lib/eventhub/version.rb
166
+ homepage: https://github.com/thomis/eventhub-processor2
167
+ licenses:
168
+ - MIT
169
+ metadata: {}
170
+ post_install_message:
171
+ rdoc_options: []
172
+ require_paths:
173
+ - lib
174
+ required_ruby_version: !ruby/object:Gem::Requirement
175
+ requirements:
176
+ - - ">="
177
+ - !ruby/object:Gem::Version
178
+ version: '0'
179
+ required_rubygems_version: !ruby/object:Gem::Requirement
180
+ requirements:
181
+ - - ">="
182
+ - !ruby/object:Gem::Version
183
+ version: '0'
184
+ requirements: []
185
+ rubyforge_project:
186
+ rubygems_version: 2.6.12
187
+ signing_key:
188
+ specification_version: 4
189
+ summary: Next generation gem to build ruby based eventhub processor
190
+ test_files: []