fleck 1.0.1 → 2.0.0
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/.gitignore +11 -10
- data/CHANGELOG.md +89 -74
- data/Gemfile +6 -4
- data/examples/actions.rb +59 -53
- data/examples/blocking_consumer.rb +42 -42
- data/examples/consumer_initialization.rb +44 -42
- data/examples/deprecation.rb +50 -57
- data/examples/example.rb +76 -74
- data/examples/expired.rb +72 -76
- data/examples/fanout.rb +62 -64
- data/fleck.gemspec +37 -36
- data/lib/fleck/client.rb +124 -124
- data/lib/fleck/configuration.rb +149 -144
- data/lib/fleck/consumer.rb +7 -287
- data/lib/fleck/core/consumer/action_param.rb +106 -0
- data/lib/fleck/core/consumer/actions.rb +76 -0
- data/lib/fleck/core/consumer/base.rb +111 -0
- data/lib/fleck/core/consumer/configuration.rb +69 -0
- data/lib/fleck/core/consumer/decorators.rb +77 -0
- data/lib/fleck/core/consumer/helpers_definers.rb +55 -0
- data/lib/fleck/core/consumer/logger.rb +88 -0
- data/lib/fleck/core/consumer/request.rb +89 -0
- data/lib/fleck/core/consumer/response.rb +77 -0
- data/lib/fleck/core/consumer/response_helpers.rb +81 -0
- data/lib/fleck/core/consumer/validation.rb +163 -0
- data/lib/fleck/core/consumer.rb +166 -0
- data/lib/fleck/core.rb +9 -0
- data/lib/fleck/loggable.rb +15 -10
- data/lib/fleck/{hash_with_indifferent_access.rb → utilities/hash_with_indifferent_access.rb} +80 -85
- data/lib/fleck/utilities/host_rating.rb +104 -0
- data/lib/fleck/version.rb +6 -3
- data/lib/fleck.rb +81 -72
- metadata +35 -24
- data/lib/fleck/consumer/request.rb +0 -52
- data/lib/fleck/consumer/response.rb +0 -80
- data/lib/fleck/host_rating.rb +0 -74
data/lib/fleck/consumer.rb
CHANGED
@@ -1,287 +1,7 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
def self.inherited(subclass)
|
9
|
-
super
|
10
|
-
init_consumer(subclass)
|
11
|
-
autostart(subclass)
|
12
|
-
Fleck.register_consumer(subclass)
|
13
|
-
end
|
14
|
-
|
15
|
-
def self.configure(opts = {})
|
16
|
-
self.configs.merge!(opts)
|
17
|
-
logger.debug "Consumer configurations updated."
|
18
|
-
end
|
19
|
-
|
20
|
-
def self.actions(*args)
|
21
|
-
args.each do |item|
|
22
|
-
case item
|
23
|
-
when Hash
|
24
|
-
item.each do |k,v|
|
25
|
-
self.register_action(k.to_s, v.to_s)
|
26
|
-
end
|
27
|
-
else
|
28
|
-
self.register_action(item.to_s, item.to_s)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def self.register_action(action, method_name)
|
34
|
-
raise ArgumentError.new("Cannot use `:#{method_name}` method as an action, because it is reserved for Fleck::Consumer internal stuff!") if Fleck::Consumer.instance_methods.include?(method_name.to_s.to_sym)
|
35
|
-
self.actions_map[action.to_s] = method_name.to_s
|
36
|
-
end
|
37
|
-
|
38
|
-
def self.initialize(&block)
|
39
|
-
self.initialize_block = block
|
40
|
-
end
|
41
|
-
|
42
|
-
def self.start(block: false)
|
43
|
-
self.consumers.each do |consumer|
|
44
|
-
consumer.start(block: block)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
def self.init_consumer(subclass)
|
49
|
-
subclass.logger = Fleck.logger.clone
|
50
|
-
subclass.logger.progname = subclass.to_s
|
51
|
-
|
52
|
-
subclass.logger.debug "Setting defaults for #{subclass.to_s.color(:yellow)} consumer"
|
53
|
-
|
54
|
-
subclass.configs = Fleck.config.default_options
|
55
|
-
subclass.configs[:autostart] = true if subclass.configs[:autostart].nil?
|
56
|
-
subclass.actions_map = {}
|
57
|
-
subclass.consumers = []
|
58
|
-
end
|
59
|
-
|
60
|
-
def self.autostart(subclass)
|
61
|
-
# Use TracePoint to autostart the consumer when ready
|
62
|
-
trace = TracePoint.new(:end) do |tp|
|
63
|
-
if tp.self == subclass
|
64
|
-
# disable tracing when we reach the end of the subclass
|
65
|
-
trace.disable
|
66
|
-
# create a new instance of the subclass, in order to start the consumer
|
67
|
-
[subclass.configs[:concurrency].to_i, 1].max.times do |i|
|
68
|
-
subclass.consumers << subclass.new(i)
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
trace.enable
|
73
|
-
end
|
74
|
-
|
75
|
-
def initialize(thread_id = nil)
|
76
|
-
@__thread_id = thread_id
|
77
|
-
@__connection = nil
|
78
|
-
@__consumer_tag = nil
|
79
|
-
@__request = nil
|
80
|
-
@__response = nil
|
81
|
-
@__lock = Mutex.new
|
82
|
-
@__lounger = ConditionVariable.new
|
83
|
-
|
84
|
-
@__host = configs[:host]
|
85
|
-
@__port = configs[:port]
|
86
|
-
@__user = configs[:user] || 'guest'
|
87
|
-
@__pass = configs[:password] || configs[:pass]
|
88
|
-
@__vhost = configs[:vhost] || "/"
|
89
|
-
@__exchange_type = configs[:exchange_type] || :direct
|
90
|
-
@__exchange_name = configs[:exchange_name] || ""
|
91
|
-
@__queue_name = configs[:queue]
|
92
|
-
@__autostart = configs[:autostart]
|
93
|
-
@__prefetch = (configs[:prefetch] || 100).to_i
|
94
|
-
@__mandatory = !!configs[:mandatory]
|
95
|
-
|
96
|
-
if self.class.initialize_block
|
97
|
-
self.instance_eval(&self.class.initialize_block)
|
98
|
-
end
|
99
|
-
|
100
|
-
logger.info "Launching #{self.class.to_s.color(:yellow)} consumer ..."
|
101
|
-
|
102
|
-
start if @__autostart
|
103
|
-
|
104
|
-
at_exit do
|
105
|
-
terminate
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
def start(block: false)
|
110
|
-
connect!
|
111
|
-
create_channel!
|
112
|
-
subscribe!
|
113
|
-
@__lock.synchronize{ @__lounger.wait(@__lock) } if block
|
114
|
-
end
|
115
|
-
|
116
|
-
def on_message(request, response)
|
117
|
-
method_name = actions[request.action.to_s]
|
118
|
-
if method_name
|
119
|
-
self.send(method_name)
|
120
|
-
else
|
121
|
-
response.not_found
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
def terminate
|
126
|
-
@__lock.synchronize { @__lounger.signal }
|
127
|
-
pause
|
128
|
-
unless channel.nil? || channel.closed?
|
129
|
-
channel.close
|
130
|
-
logger.info "Consumer successfully terminated."
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
def logger
|
135
|
-
return @logger if @logger
|
136
|
-
@logger = self.class.logger.clone
|
137
|
-
@logger.progname = "#{self.class.name}" + (configs[:concurrency].to_i <= 1 ? "" : "[#{@__thread_id}]")
|
138
|
-
|
139
|
-
@logger
|
140
|
-
end
|
141
|
-
|
142
|
-
def configs
|
143
|
-
@configs ||= self.class.configs
|
144
|
-
end
|
145
|
-
|
146
|
-
def actions
|
147
|
-
@actions ||= self.class.actions_map
|
148
|
-
end
|
149
|
-
|
150
|
-
def connection
|
151
|
-
return @__connection
|
152
|
-
end
|
153
|
-
|
154
|
-
def channel
|
155
|
-
return @__channel
|
156
|
-
end
|
157
|
-
|
158
|
-
def queue
|
159
|
-
return @__queue
|
160
|
-
end
|
161
|
-
|
162
|
-
def exchange
|
163
|
-
return @__exchange
|
164
|
-
end
|
165
|
-
|
166
|
-
def publisher
|
167
|
-
return @__publisher
|
168
|
-
end
|
169
|
-
|
170
|
-
def subscription
|
171
|
-
return @__subscription
|
172
|
-
end
|
173
|
-
|
174
|
-
def pause
|
175
|
-
if subscription
|
176
|
-
cancel_ok = subscription.cancel
|
177
|
-
@__consumer_tag = cancel_ok.consumer_tag
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
def resume
|
182
|
-
subscribe!
|
183
|
-
end
|
184
|
-
|
185
|
-
def request
|
186
|
-
@__request
|
187
|
-
end
|
188
|
-
|
189
|
-
def response
|
190
|
-
@__response
|
191
|
-
end
|
192
|
-
|
193
|
-
def deprecated!
|
194
|
-
logger.warn("DEPRECATION: the method `#{caller_locations(1,1)[0].label}` is going to be deprecated. Please, consider using a newer version of this method.")
|
195
|
-
@__response.deprecated! if @__response
|
196
|
-
end
|
197
|
-
|
198
|
-
protected
|
199
|
-
|
200
|
-
def connect!
|
201
|
-
@__connection = Fleck.connection(host: @__host, port: @__port, user: @__user, pass: @__pass, vhost: @__vhost)
|
202
|
-
end
|
203
|
-
|
204
|
-
def create_channel!
|
205
|
-
if @__channel && !@__channel.closed?
|
206
|
-
logger.info("Closing the opened channel...")
|
207
|
-
@__channel.close
|
208
|
-
end
|
209
|
-
|
210
|
-
logger.debug "Creating a new channel for #{self.class.to_s.color(:yellow)} consumer"
|
211
|
-
@__channel = @__connection.create_channel
|
212
|
-
@__channel.prefetch(@__prefetch) # consume messages in batches
|
213
|
-
@__publisher = Bunny::Exchange.new(@__connection.create_channel, :direct, 'fleck')
|
214
|
-
if @__exchange_type == :direct && @__exchange_name == ""
|
215
|
-
@__queue = @__channel.queue(@__queue_name, auto_delete: false)
|
216
|
-
else
|
217
|
-
@__exchange = Bunny::Exchange.new(@__channel, @__exchange_type, @__exchange_name)
|
218
|
-
@__queue = @__channel.queue("", exclusive: true, auto_delete: true).bind(@__exchange, routing_key: @__queue_name)
|
219
|
-
end
|
220
|
-
end
|
221
|
-
|
222
|
-
def subscribe!
|
223
|
-
logger.debug "Consuming from queue: #{@__queue_name.color(:green)}"
|
224
|
-
|
225
|
-
options = { manual_ack: true }
|
226
|
-
options[:consumer_tag] = @__consumer_tag if @__consumer_tag
|
227
|
-
|
228
|
-
@__subscription = @__queue.subscribe(options) do |delivery_info, metadata, payload|
|
229
|
-
started_at = Time.now.to_f
|
230
|
-
@__response = Fleck::Consumer::Response.new(metadata.correlation_id)
|
231
|
-
begin
|
232
|
-
@__request = Fleck::Consumer::Request.new(metadata, payload, delivery_info)
|
233
|
-
if @__request.errors.empty?
|
234
|
-
on_message(@__request, @__response)
|
235
|
-
else
|
236
|
-
@__response.status = @__request.status
|
237
|
-
@__response.errors += @__request.errors
|
238
|
-
end
|
239
|
-
rescue => e
|
240
|
-
logger.error e.inspect + "\n" + e.backtrace.join("\n")
|
241
|
-
@__response.status = 500
|
242
|
-
@__response.errors << 'Internal Server Error'
|
243
|
-
end
|
244
|
-
|
245
|
-
if @__response.rejected?
|
246
|
-
@__channel.reject(delivery_info.delivery_tag, @__response.requeue?)
|
247
|
-
else
|
248
|
-
logger.debug "Sending response: #{@__response}"
|
249
|
-
if @__channel.closed?
|
250
|
-
logger.warn "Channel already closed! The response #{metadata.correlation_id} is going to be dropped."
|
251
|
-
else
|
252
|
-
@__publisher.publish(@__response.to_json, routing_key: metadata.reply_to, correlation_id: metadata.correlation_id, mandatory: @__mandatory)
|
253
|
-
@__channel.ack(delivery_info.delivery_tag)
|
254
|
-
end
|
255
|
-
end
|
256
|
-
|
257
|
-
exec_time = ((Time.now.to_f - started_at) * 1000).round(2)
|
258
|
-
ex_type = @__exchange_type.to_s[0].upcase
|
259
|
-
ex_name = @__exchange_name.to_s == "" ? "".inspect : @__exchange_name
|
260
|
-
status = @__response.status
|
261
|
-
status = 406 if @__response.rejected?
|
262
|
-
status = 503 if @__channel.closed?
|
263
|
-
|
264
|
-
message = "#{@__request.ip} #{metadata[:app_id]} => "
|
265
|
-
message += "(#{@__exchange_name.to_s.inspect}|#{ex_type}|#{@__queue_name}) "
|
266
|
-
message += "##{@__request.id} \"#{@__request.action} /#{@__request.version || 'v1'}\" #{status} "
|
267
|
-
message += "(#{exec_time}ms) #{'DEPRECATED!' if @__response.deprecated?}"
|
268
|
-
|
269
|
-
if status >= 500
|
270
|
-
logger.error message
|
271
|
-
elsif status >= 400 || @__response.deprecated?
|
272
|
-
logger.warn message
|
273
|
-
else
|
274
|
-
logger.info message
|
275
|
-
end
|
276
|
-
end
|
277
|
-
end
|
278
|
-
|
279
|
-
def restart!
|
280
|
-
create_channel!
|
281
|
-
subscribe!
|
282
|
-
end
|
283
|
-
end
|
284
|
-
end
|
285
|
-
|
286
|
-
require "fleck/consumer/request"
|
287
|
-
require "fleck/consumer/response"
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Fleck
|
4
|
+
# A shorthand class for custom consumers definitions.
|
5
|
+
class Consumer < Fleck::Core::Consumer
|
6
|
+
end
|
7
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Fleck
|
4
|
+
module Core
|
5
|
+
class Consumer
|
6
|
+
# Stores data about an action parameter, which will be used for automatic parameters validation.
|
7
|
+
class ActionParam
|
8
|
+
AVAILABLE_TYPES = %w[string number boolean object array].freeze
|
9
|
+
TYPE_ALIASES = {
|
10
|
+
'text' => 'string',
|
11
|
+
'integer' => 'number',
|
12
|
+
'float' => 'number',
|
13
|
+
'hash' => 'object'
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
attr_reader :name, :type, :options
|
17
|
+
|
18
|
+
def initialize(name, type, options = {})
|
19
|
+
@name = name
|
20
|
+
@type = type
|
21
|
+
@options = options
|
22
|
+
|
23
|
+
check_options!
|
24
|
+
end
|
25
|
+
|
26
|
+
def string?
|
27
|
+
@type == 'string'
|
28
|
+
end
|
29
|
+
|
30
|
+
def required?
|
31
|
+
options[:required]
|
32
|
+
end
|
33
|
+
|
34
|
+
def validate(value)
|
35
|
+
Validation.new(name, type, value, options)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def check_options!
|
41
|
+
check_type!
|
42
|
+
check_required!
|
43
|
+
check_default!
|
44
|
+
check_min_max!
|
45
|
+
check_format!
|
46
|
+
check_clamp!
|
47
|
+
end
|
48
|
+
|
49
|
+
def check_type!
|
50
|
+
@type = @type.to_s.strip.downcase
|
51
|
+
|
52
|
+
@type = TYPE_ALIASES[@type] unless TYPE_ALIASES[@type].nil?
|
53
|
+
|
54
|
+
valid_type = AVAILABLE_TYPES.include?(@type)
|
55
|
+
raise "Invalid param type: #{@type.inspect}" unless valid_type
|
56
|
+
end
|
57
|
+
|
58
|
+
def check_required!
|
59
|
+
options[:required] = (options[:required] == true)
|
60
|
+
end
|
61
|
+
|
62
|
+
def check_default!
|
63
|
+
return if options[:default].nil?
|
64
|
+
|
65
|
+
# TODO: check default value type
|
66
|
+
end
|
67
|
+
|
68
|
+
def check_min_max!
|
69
|
+
check_min!
|
70
|
+
check_max!
|
71
|
+
|
72
|
+
return if options[:min].nil? || options[:max].nil?
|
73
|
+
|
74
|
+
raise 'Invalid min-max range' unless options[:min] <= options[:max]
|
75
|
+
end
|
76
|
+
|
77
|
+
def check_min!
|
78
|
+
min = options[:min]
|
79
|
+
return if min.nil?
|
80
|
+
|
81
|
+
raise 'Invalid minimum' unless min.is_a?(Integer) || min.is_a?(Float)
|
82
|
+
end
|
83
|
+
|
84
|
+
def check_max!
|
85
|
+
max = options[:max]
|
86
|
+
return if max.nil?
|
87
|
+
|
88
|
+
raise 'Invalid maximum' unless max.is_a?(Integer) || max.is_a?(Float)
|
89
|
+
end
|
90
|
+
|
91
|
+
def check_format!
|
92
|
+
return if options[:format].nil?
|
93
|
+
|
94
|
+
raise 'Invalid format' unless options[:format].is_a?(Regexp)
|
95
|
+
end
|
96
|
+
|
97
|
+
def check_clamp!
|
98
|
+
return if options[:clamp].nil?
|
99
|
+
|
100
|
+
raise 'Invalid clamp' unless options[:clamp].is_a?(Array)
|
101
|
+
raise 'Invalid clamp range' unless options[:clamp].first.to_i < options[:clamp].last.to_i
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Fleck
|
4
|
+
module Core
|
5
|
+
class Consumer
|
6
|
+
# `Fleck::Core::Consumer::Actions` module implements the logic for consumer actions
|
7
|
+
# registration, so that this information could be used when a request is received.
|
8
|
+
# This mechanism will allow to process the request with the appropriate consumer method.
|
9
|
+
module Actions
|
10
|
+
def self.included(base)
|
11
|
+
base.extend ClassMethods
|
12
|
+
base.send :include, InstanceMethods
|
13
|
+
end
|
14
|
+
|
15
|
+
# Defines class methods to import when `Actions` module is imported.
|
16
|
+
module ClassMethods
|
17
|
+
attr_accessor :actions_map
|
18
|
+
|
19
|
+
def actions(*args)
|
20
|
+
args.each do |item|
|
21
|
+
case item
|
22
|
+
when Hash then item.each { |k, v| register_action(k.to_s, v.to_s) }
|
23
|
+
else register_action(item.to_s, item.to_s)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def register_action(action, method_name, options = {})
|
29
|
+
if Fleck::Consumer.instance_methods.include?(method_name.to_s.to_sym)
|
30
|
+
raise ArgumentError, "Cannot use `:#{method_name}` method as an action, " \
|
31
|
+
'because it is reserved for Fleck::Consumer internal stuff!'
|
32
|
+
end
|
33
|
+
|
34
|
+
options[:method_name] = method_name.to_s
|
35
|
+
options[:params] ||= {}
|
36
|
+
actions_map[action.to_s] = options
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Defines instance methods to import when `Actions` module is imported.
|
41
|
+
module InstanceMethods
|
42
|
+
def actions
|
43
|
+
@actions ||= self.class.actions_map
|
44
|
+
end
|
45
|
+
|
46
|
+
protected
|
47
|
+
|
48
|
+
def execute_action!
|
49
|
+
action_name = request.action.to_s
|
50
|
+
action = actions[action_name]
|
51
|
+
unless action
|
52
|
+
message = "Action #{action_name.inspect} not found!"
|
53
|
+
not_found! error: message, body: [
|
54
|
+
{ type: 'action', name: action_name, value: action_name, error: 'not_found', message: message }
|
55
|
+
]
|
56
|
+
end
|
57
|
+
|
58
|
+
# iterate over action params and use param options to validate incoming request params.
|
59
|
+
action[:params].each { |_, param| validate_action_param!(param) }
|
60
|
+
|
61
|
+
send(action[:method_name])
|
62
|
+
end
|
63
|
+
|
64
|
+
def validate_action_param!(param)
|
65
|
+
validation = param.validate(request.params[param.name])
|
66
|
+
unless validation.valid?
|
67
|
+
bad_request! error: "Invalid param value: #{param.name} = #{validation.value.inspect}",
|
68
|
+
body: validation.errors
|
69
|
+
end
|
70
|
+
request.params[param.name] = validation.value
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Fleck
|
4
|
+
module Core
|
5
|
+
class Consumer
|
6
|
+
# Base methods for consumer setup, start and termination.
|
7
|
+
module Base
|
8
|
+
def self.included(base)
|
9
|
+
base.extend ClassMethods
|
10
|
+
base.send :include, InstanceMethods
|
11
|
+
end
|
12
|
+
|
13
|
+
# Defines class methods to import when `Autostart` module is imported.
|
14
|
+
module ClassMethods
|
15
|
+
attr_accessor :consumers, :initialize_block, :lock, :condition
|
16
|
+
|
17
|
+
def inherited(subclass)
|
18
|
+
super
|
19
|
+
return if subclass == Fleck::Consumer
|
20
|
+
|
21
|
+
init_consumer(subclass)
|
22
|
+
autostart(subclass)
|
23
|
+
Fleck.register_consumer(subclass)
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(&block)
|
27
|
+
self.initialize_block = block
|
28
|
+
end
|
29
|
+
|
30
|
+
def start(block: false)
|
31
|
+
consumers.each(&:start)
|
32
|
+
wait_termination if block
|
33
|
+
end
|
34
|
+
|
35
|
+
def wait_termination
|
36
|
+
lock.synchronize { condition.wait(lock) }
|
37
|
+
end
|
38
|
+
|
39
|
+
def on_terminate(consumer)
|
40
|
+
consumers.delete consumer
|
41
|
+
terminate if consumers.empty?
|
42
|
+
end
|
43
|
+
|
44
|
+
def terminate
|
45
|
+
consumers.each(&:terminate)
|
46
|
+
lock.synchronize { condition.signal }
|
47
|
+
end
|
48
|
+
|
49
|
+
protected
|
50
|
+
|
51
|
+
def init_consumer(subclass)
|
52
|
+
configure_logger(subclass)
|
53
|
+
|
54
|
+
subclass.lock = Mutex.new
|
55
|
+
subclass.condition = ConditionVariable.new
|
56
|
+
|
57
|
+
subclass.configs = Fleck.config.default_options
|
58
|
+
subclass.actions_map = {}
|
59
|
+
subclass.consumers = []
|
60
|
+
end
|
61
|
+
|
62
|
+
def configure_logger(subclass)
|
63
|
+
subclass.logger = Fleck.logger.clone
|
64
|
+
subclass.logger.progname = subclass.to_s
|
65
|
+
subclass.logger.debug "Setting defaults for #{subclass.to_s.color(:yellow)} consumer"
|
66
|
+
end
|
67
|
+
|
68
|
+
def autostart(subclass)
|
69
|
+
# Use TracePoint to autostart the consumer when ready
|
70
|
+
trace = TracePoint.new(:end) do |tp|
|
71
|
+
if tp.self == subclass
|
72
|
+
# disable tracing when we reach the end of the subclass
|
73
|
+
trace.disable
|
74
|
+
# create a new instance of the subclass, in order to start the consumer
|
75
|
+
[subclass.configs[:concurrency].to_i, 1].max.times do |i|
|
76
|
+
subclass.consumers << subclass.new(i)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
trace.enable
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Defines instance methods to import when `Autostart` module is imported.
|
85
|
+
module InstanceMethods
|
86
|
+
def autostart?
|
87
|
+
configs[:autostart].nil? || configs[:autostart]
|
88
|
+
end
|
89
|
+
|
90
|
+
def start
|
91
|
+
logger.info "Launching #{self.class.to_s.color(:yellow)} consumer ..."
|
92
|
+
connect!
|
93
|
+
create_channel!
|
94
|
+
subscribe!
|
95
|
+
end
|
96
|
+
|
97
|
+
def terminate
|
98
|
+
pause
|
99
|
+
|
100
|
+
return if channel.nil? || channel.closed?
|
101
|
+
|
102
|
+
channel.close
|
103
|
+
|
104
|
+
logger.info 'Consumer successfully terminated.'
|
105
|
+
self.class.on_terminate(self)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Fleck
|
2
|
+
module Core
|
3
|
+
class Consumer
|
4
|
+
module Configuration
|
5
|
+
def self.included(base)
|
6
|
+
base.extend ClassMethods
|
7
|
+
base.send :include, InstanceMethods
|
8
|
+
end
|
9
|
+
|
10
|
+
# Defines class methods to import when `Configuration` module is imported.
|
11
|
+
module ClassMethods
|
12
|
+
attr_accessor :configs
|
13
|
+
|
14
|
+
def configure(opts = {})
|
15
|
+
configs.merge!(opts)
|
16
|
+
logger.debug 'Consumer configurations updated.'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Defines instance methods to import when `Configuration` module is imported.
|
21
|
+
module InstanceMethods
|
22
|
+
def configs
|
23
|
+
@configs ||= self.class.configs
|
24
|
+
end
|
25
|
+
|
26
|
+
def rmq_host
|
27
|
+
@rmq_host ||= configs[:host]
|
28
|
+
end
|
29
|
+
|
30
|
+
def rmq_port
|
31
|
+
@rmq_port ||= configs[:port]
|
32
|
+
end
|
33
|
+
|
34
|
+
def rmq_user
|
35
|
+
@rmq_user ||= configs.fetch(:user, 'guest')
|
36
|
+
end
|
37
|
+
|
38
|
+
def rmq_pass
|
39
|
+
@rmq_pass ||= configs.fetch(:password, configs[:pass])
|
40
|
+
end
|
41
|
+
|
42
|
+
def rmq_vhost
|
43
|
+
@rmq_vhost ||= configs.fetch(:vhost, '/')
|
44
|
+
end
|
45
|
+
|
46
|
+
def queue_name
|
47
|
+
@queue_name ||= configs[:queue]
|
48
|
+
end
|
49
|
+
|
50
|
+
def rmq_exchange_type
|
51
|
+
@rmq_exchange_type ||= configs.fetch(:exchange_type, :direct)
|
52
|
+
end
|
53
|
+
|
54
|
+
def rmq_exchange_name
|
55
|
+
@rmq_exchange_name ||= configs.fetch(:exchange_name, '')
|
56
|
+
end
|
57
|
+
|
58
|
+
def ack_mandatory?
|
59
|
+
@ack_mandatory ||= !configs[:mandatory].nil?
|
60
|
+
end
|
61
|
+
|
62
|
+
def prefetch_size
|
63
|
+
@prefetch_size ||= configs.fetch(:prefetch, 100).to_i
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|