fleck 1.0.1 → 2.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +11 -10
  3. data/CHANGELOG.md +89 -74
  4. data/Gemfile +6 -4
  5. data/examples/actions.rb +60 -53
  6. data/examples/blocking_consumer.rb +42 -42
  7. data/examples/consumer_initialization.rb +44 -42
  8. data/examples/deprecation.rb +50 -57
  9. data/examples/example.rb +76 -74
  10. data/examples/expired.rb +72 -76
  11. data/examples/fanout.rb +62 -64
  12. data/fleck.gemspec +37 -36
  13. data/lib/fleck/client.rb +124 -124
  14. data/lib/fleck/configuration.rb +147 -144
  15. data/lib/fleck/consumer.rb +7 -287
  16. data/lib/fleck/core/consumer/action_param.rb +106 -0
  17. data/lib/fleck/core/consumer/actions.rb +79 -0
  18. data/lib/fleck/core/consumer/base.rb +111 -0
  19. data/lib/fleck/core/consumer/configuration.rb +69 -0
  20. data/lib/fleck/core/consumer/decorators.rb +77 -0
  21. data/lib/fleck/core/consumer/helpers_definers.rb +56 -0
  22. data/lib/fleck/core/consumer/logger.rb +88 -0
  23. data/lib/fleck/core/consumer/request.rb +100 -0
  24. data/lib/fleck/core/consumer/response.rb +77 -0
  25. data/lib/fleck/core/consumer/response_helpers.rb +81 -0
  26. data/lib/fleck/core/consumer/validation.rb +163 -0
  27. data/lib/fleck/core/consumer.rb +166 -0
  28. data/lib/fleck/core.rb +9 -0
  29. data/lib/fleck/loggable.rb +15 -10
  30. data/lib/fleck/{hash_with_indifferent_access.rb → utilities/hash_with_indifferent_access.rb} +80 -85
  31. data/lib/fleck/utilities/host_rating.rb +104 -0
  32. data/lib/fleck/version.rb +6 -3
  33. data/lib/fleck.rb +82 -72
  34. metadata +35 -24
  35. data/lib/fleck/consumer/request.rb +0 -52
  36. data/lib/fleck/consumer/response.rb +0 -80
  37. data/lib/fleck/host_rating.rb +0 -74
data/lib/fleck/client.rb CHANGED
@@ -1,124 +1,124 @@
1
-
2
- module Fleck
3
- class Client
4
- include Fleck::Loggable
5
-
6
- attr_reader :local_ip, :remote_ip
7
-
8
- def initialize(connection, queue_name = "", exchange_type: :direct, exchange_name: "", multiple_responses: false, concurrency: 1)
9
- @connection = connection
10
- @queue_name = queue_name
11
- @multiple_responses = multiple_responses
12
- @default_timeout = multiple_responses ? 60 : nil
13
- @concurrency = [concurrency.to_i, 1].max
14
- @requests = ThreadSafe::Hash.new
15
- @subscriptions = ThreadSafe::Array.new
16
- @terminated = false
17
- @mutex = Mutex.new
18
- @local_ip = @connection.transport.socket.local_address.ip_address
19
- @remote_ip = @connection.transport.socket.remote_address.ip_address
20
-
21
- @channel = @connection.create_channel
22
- @exchange = Bunny::Exchange.new(@channel, :direct, 'fleck')
23
- @publisher = Bunny::Exchange.new(@connection.create_channel, exchange_type, exchange_name)
24
- @reply_queue = @channel.queue("", exclusive: true, auto_delete: true)
25
- @reply_queue.bind(@exchange, routing_key: @reply_queue.name)
26
-
27
- handle_returned_messages!
28
- @concurrency.times { handle_responses! }
29
-
30
- logger.debug("Client initialized!")
31
-
32
- at_exit do
33
- terminate
34
- end
35
- end
36
-
37
- def request(action: nil, version: nil, headers: {}, params: {}, async: @multiple_responses || false, timeout: @default_timeout, queue: @queue_name, rmq_options: {}, &block)
38
-
39
- if @terminated
40
- return Fleck::Client::Response.new(Oj.dump({status: 503, errors: ['Service Unavailable'], body: nil} , mode: :compat))
41
- end
42
-
43
- request = Fleck::Client::Request.new(
44
- self, queue, @reply_queue.name,
45
- action: action,
46
- version: version,
47
- headers: headers,
48
- params: params,
49
- timeout: timeout,
50
- multiple_responses: @multiple_responses,
51
- rmq_options: rmq_options,
52
- &block
53
- )
54
-
55
- @requests[request.id] = request
56
- request.send!(async)
57
-
58
- return request.response
59
- end
60
-
61
-
62
- def publish(data, options)
63
- return if @terminated
64
- @mutex.synchronize { @publisher.publish(data, options) }
65
- end
66
-
67
-
68
- def remove_request(request_id)
69
- @requests.delete request_id
70
- end
71
-
72
-
73
- def terminate
74
- @terminated = true
75
- logger.info "Unsubscribing from #{@reply_queue.name}"
76
- @subscriptions.map(&:cancel) # stop receiving new messages
77
- logger.info "Canceling pending requests"
78
- # cancel pending requests
79
- while item = @requests.shift do
80
- begin
81
- item[1].cancel!
82
- rescue => e
83
- logger.error e.inspect + "\n" + e.backtrace.join("\n")
84
- end
85
- end
86
- end
87
-
88
-
89
- protected
90
-
91
- def handle_returned_messages!
92
- @exchange.on_return do |return_info, metadata, content|
93
- begin
94
- logger.warn "Request #{metadata[:correlation_id]} returned"
95
- request = @requests[metadata[:correlation_id]]
96
- if request
97
- request.cancel!
98
- end
99
- rescue => e
100
- logger.error e.inspect + "\n" + e.backtrace.join("\n")
101
- end
102
- end
103
- end
104
-
105
- def handle_responses!
106
- @subscriptions << @reply_queue.subscribe do |delivery_info, metadata, payload|
107
- begin
108
- logger.debug "Response received: #{payload}"
109
- request = @requests[metadata[:correlation_id]]
110
- if request
111
- request.response = Fleck::Client::Response.new(payload)
112
- else
113
- logger.warn "Request #{metadata[:correlation_id]} not found!"
114
- end
115
- rescue => e
116
- logger.error e.inspect + "\n" + e.backtrace.join("\n")
117
- end
118
- end
119
- end
120
- end
121
- end
122
-
123
- require "fleck/client/request"
124
- require "fleck/client/response"
1
+
2
+ module Fleck
3
+ class Client
4
+ include Fleck::Loggable
5
+
6
+ attr_reader :local_ip, :remote_ip
7
+
8
+ def initialize(connection, queue_name = "", exchange_type: :direct, exchange_name: "", multiple_responses: false, concurrency: 1)
9
+ @connection = connection
10
+ @queue_name = queue_name
11
+ @multiple_responses = multiple_responses
12
+ @default_timeout = multiple_responses ? 60 : nil
13
+ @concurrency = [concurrency.to_i, 1].max
14
+ @requests = ThreadSafe::Hash.new
15
+ @subscriptions = ThreadSafe::Array.new
16
+ @terminated = false
17
+ @mutex = Mutex.new
18
+ @local_ip = @connection.transport.socket.local_address.ip_address
19
+ @remote_ip = @connection.transport.socket.remote_address.ip_address
20
+
21
+ @channel = @connection.create_channel
22
+ @exchange = Bunny::Exchange.new(@channel, :direct, 'fleck')
23
+ @publisher = Bunny::Exchange.new(@connection.create_channel, exchange_type, exchange_name)
24
+ @reply_queue = @channel.queue("", exclusive: true, auto_delete: true)
25
+ @reply_queue.bind(@exchange, routing_key: @reply_queue.name)
26
+
27
+ handle_returned_messages!
28
+ @concurrency.times { handle_responses! }
29
+
30
+ logger.debug("Client initialized!")
31
+
32
+ at_exit do
33
+ terminate
34
+ end
35
+ end
36
+
37
+ def request(action: nil, version: nil, headers: {}, params: {}, async: @multiple_responses || false, timeout: @default_timeout, queue: @queue_name, rmq_options: {}, &block)
38
+
39
+ if @terminated
40
+ return Fleck::Client::Response.new(Oj.dump({status: 503, errors: ['Service Unavailable'], body: nil} , mode: :compat))
41
+ end
42
+
43
+ request = Fleck::Client::Request.new(
44
+ self, queue, @reply_queue.name,
45
+ action: action,
46
+ version: version,
47
+ headers: headers,
48
+ params: params,
49
+ timeout: timeout,
50
+ multiple_responses: @multiple_responses,
51
+ rmq_options: rmq_options,
52
+ &block
53
+ )
54
+
55
+ @requests[request.id] = request
56
+ request.send!(async)
57
+
58
+ return request.response
59
+ end
60
+
61
+
62
+ def publish(data, options)
63
+ return if @terminated
64
+ @mutex.synchronize { @publisher.publish(data, options) }
65
+ end
66
+
67
+
68
+ def remove_request(request_id)
69
+ @requests.delete request_id
70
+ end
71
+
72
+
73
+ def terminate
74
+ @terminated = true
75
+ logger.info "Unsubscribing from #{@reply_queue.name}"
76
+ @subscriptions.map(&:cancel) # stop receiving new messages
77
+ logger.info "Canceling pending requests"
78
+ # cancel pending requests
79
+ while item = @requests.shift do
80
+ begin
81
+ item[1].cancel!
82
+ rescue => e
83
+ logger.error e.inspect + "\n" + e.backtrace.join("\n")
84
+ end
85
+ end
86
+ end
87
+
88
+
89
+ protected
90
+
91
+ def handle_returned_messages!
92
+ @exchange.on_return do |return_info, metadata, content|
93
+ begin
94
+ logger.warn "Request #{metadata[:correlation_id]} returned"
95
+ request = @requests[metadata[:correlation_id]]
96
+ if request
97
+ request.cancel!
98
+ end
99
+ rescue => e
100
+ logger.error e.inspect + "\n" + e.backtrace.join("\n")
101
+ end
102
+ end
103
+ end
104
+
105
+ def handle_responses!
106
+ @subscriptions << @reply_queue.subscribe do |delivery_info, metadata, payload|
107
+ begin
108
+ logger.debug "Response received: #{payload}"
109
+ request = @requests[metadata[:correlation_id]]
110
+ if request
111
+ request.response = Fleck::Client::Response.new(payload)
112
+ else
113
+ logger.warn "Request #{metadata[:correlation_id]} not found!"
114
+ end
115
+ rescue => e
116
+ logger.error e.inspect + "\n" + e.backtrace.join("\n")
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
122
+
123
+ require "fleck/client/request"
124
+ require "fleck/client/response"
@@ -1,144 +1,147 @@
1
-
2
- module Fleck
3
- class Configuration
4
-
5
- attr_reader :logfile, :loglevel, :progname, :hosts
6
- attr_accessor :default_user, :default_pass, :default_host, :default_port, :default_vhost, :default_queue,
7
- :app_name, :filters
8
-
9
- def initialize
10
- @logfile = STDOUT
11
- @loglevel = ::Logger::INFO
12
- @progname = "Fleck"
13
- @app_name = $0
14
- @default_host = "127.0.0.1"
15
- @default_port = 5672
16
- @default_user = nil
17
- @default_pass = nil
18
- @default_vhost = "/"
19
- @default_queue = "default"
20
- @filters = ["password", "secret", "token"]
21
- @hosts = []
22
- @credentials = {}
23
- end
24
-
25
- def hosts=(*args)
26
- args.flatten.each do |host|
27
- add_host host
28
- end
29
- return @hosts
30
- end
31
-
32
- def add_host(data)
33
- if data.is_a?(String)
34
- host, port = data.split(":")
35
- port = port ? port.to_i : 5672
36
- @hosts << Fleck::HostRating.new(host: host, port: port)
37
- @credentials["#{host}:#{port}"] ||= { user: @default_user, pass: @default_pass }
38
- elsif data.is_a?(Hash)
39
- data = data.to_hash_with_indifferent_access
40
- host = data[:host] || @default_host
41
- port = data[:port] || @default_port
42
- @hosts << Fleck::HostRating.new(host: data[:host] || @default_host, port: data[:port] || @default_port)
43
- @credentials["#{host}:#{port}"] ||= { user: data[:user] || @default_user, pass: data[:pass] || @default_pass }
44
- else
45
- raise ArgumentError.new("Invalid host type #{data.inspect}: String or Hash expected")
46
- end
47
- end
48
-
49
- def default_options
50
- best = @hosts.sort.first
51
- opts = {}
52
-
53
- host = best ? best.host : @default_host
54
- port = best ? best.port : @default_port
55
- credentials = @credentials["#{host}:#{port}"] || {user: @default_user, pass: @default_pass}
56
-
57
- opts[:host] = host
58
- opts[:port] = port
59
- opts[:user] = credentials[:user] || @default_user
60
- opts[:pass] = credentials[:pass] || @default_pass
61
- opts[:vhost] = @default_vhost
62
- opts[:queue] = @default_queue
63
-
64
- return opts
65
- end
66
-
67
- def logfile=(new_logfile)
68
- if @logfile != new_logfile
69
- @logfile = new_logfile
70
- reset_logger
71
- end
72
-
73
- return @logfile
74
- end
75
-
76
- def loglevel=(new_loglevel)
77
- @loglevel = new_loglevel
78
- @logger.level = @loglevel if @logger
79
-
80
- return @loglevel
81
- end
82
-
83
- def progname=(new_progname)
84
- @progname = new_progname
85
- @logger.progname = @progname if @logger
86
-
87
- return @progname
88
- end
89
-
90
- def logger
91
- return @logger || reset_logger
92
- end
93
-
94
- def logger=(new_logger)
95
- if new_logger.nil?
96
- @logger.close if @logger
97
- @logger = ::Logger.new(nil)
98
- else
99
- @logger.close if @logger
100
- @logger = new_logger.clone
101
- @logger.formatter = formatter
102
- @logger.progname = @progname
103
- @logger.level = @loglevel
104
- end
105
-
106
- return @logger
107
- end
108
-
109
- def reset_logger
110
- new_logger = ::Logger.new(@logfile)
111
- new_logger.formatter = formatter
112
- new_logger.progname = @progname
113
- new_logger.level = @loglevel
114
- @logger.close if @logger
115
- @logger = new_logger
116
-
117
- return @logger
118
- end
119
-
120
- def formatter
121
- return @formatter if @formatter
122
-
123
- @formatter = proc do |severity, datetime, progname, msg|
124
- color = :blue
125
- case severity
126
- when 'DEBUG'
127
- color = "#512DA8"
128
- when 'INFO'
129
- color = "#33691E"
130
- when 'WARN'
131
- color = "#E65100"
132
- when 'ERROR', 'FATAL'
133
- color = "#B71C1C"
134
- else
135
- color = "#00BCD4"
136
- end
137
- "[#{datetime.strftime('%F %T.%L')}]".color(:cyan) + "(#{$$})".color(:blue) + "|#{severity}|".color(color) +
138
- (progname ? "<#{progname}>".color(:yellow) : "") + " #{msg}\n"
139
- end
140
-
141
- return @formatter
142
- end
143
- end
144
- end
1
+ # frozen_string_literal: true
2
+
3
+ # Open `Fleck` module to add `Configuration` class implementation.
4
+ module Fleck
5
+ # `Fleck::Configuration` implements a set of methods useful for `Fleck` clients and consumers configuration.
6
+ class Configuration
7
+ attr_reader :logfile, :loglevel, :progname, :hosts
8
+ attr_accessor :default_user, :default_pass, :default_host, :default_port, :default_vhost, :default_queue,
9
+ :app_name, :filters
10
+
11
+ def initialize
12
+ @logfile = $stdout
13
+ @loglevel = ::Logger::INFO
14
+ @progname = 'Fleck'
15
+ @app_name = $PROGRAM_NAME
16
+ @default_host = '127.0.0.1'
17
+ @default_port = 5672
18
+ @default_user = nil
19
+ @default_pass = nil
20
+ @default_vhost = '/'
21
+ @default_queue = 'default'
22
+ @filters = %w[password secret token]
23
+ @hosts = []
24
+ @credentials = {}
25
+ end
26
+
27
+ def hosts=(*args)
28
+ args.flatten.each do |host|
29
+ add_host host
30
+ end
31
+ end
32
+
33
+ def add_host(data)
34
+ case data
35
+ when String then add_host_from_string(data)
36
+ when Hash then add_host_from_hash(data)
37
+ else
38
+ raise ArgumentError, "Invalid host type #{data.inspect}: String or Hash expected"
39
+ end
40
+ end
41
+
42
+ def default_options
43
+ best = @hosts.min
44
+ opts = {}
45
+
46
+ host = best ? best.host : @default_host
47
+ port = best ? best.port : @default_port
48
+ credentials = @credentials["#{host}:#{port}"] || { user: @default_user, pass: @default_pass }
49
+
50
+ opts[:host] = host
51
+ opts[:port] = port
52
+ opts[:user] = credentials[:user] || @default_user
53
+ opts[:pass] = credentials[:pass] || @default_pass
54
+ opts[:vhost] = @default_vhost
55
+ opts[:queue] = @default_queue
56
+
57
+ opts
58
+ end
59
+
60
+ def logfile=(new_logfile)
61
+ return unless @logfile != new_logfile
62
+
63
+ @logfile = new_logfile
64
+ reset_logger
65
+ end
66
+
67
+ def loglevel=(new_loglevel)
68
+ @loglevel = new_loglevel
69
+ @logger.level = @loglevel if @logger
70
+ end
71
+
72
+ def progname=(new_progname)
73
+ @progname = new_progname
74
+ @logger.progname = @progname if @logger
75
+ end
76
+
77
+ def logger
78
+ @logger || reset_logger
79
+ end
80
+
81
+ def logger=(new_logger)
82
+ @logger&.close
83
+
84
+ if new_logger.nil?
85
+ @logger = ::Logger.new(nil)
86
+ else
87
+ @logger = new_logger.clone
88
+ @logger.formatter = formatter
89
+ @logger.progname = @progname
90
+ @logger.level = @loglevel
91
+ end
92
+ end
93
+
94
+ def reset_logger
95
+ new_logger = ::Logger.new(@logfile)
96
+ new_logger.formatter = formatter
97
+ new_logger.progname = @progname
98
+ new_logger.level = @loglevel
99
+ @logger&.close
100
+ @logger = new_logger
101
+
102
+ @logger
103
+ end
104
+
105
+ def formatter
106
+ return @formatter if @formatter
107
+
108
+ @formatter = proc do |severity, datetime, progname, msg|
109
+ color = severity_color(severity)
110
+ "[#{datetime.strftime('%F %T.%L')}]".color(:cyan) +
111
+ "(#{$PID})".color(:blue) +
112
+ "|#{severity}|".color(color) +
113
+ (progname ? "<#{progname}>".color(:yellow) : '') +
114
+ " #{msg}\n"
115
+ end
116
+
117
+ @formatter
118
+ end
119
+
120
+ private
121
+
122
+ def add_host_from_string(data)
123
+ host, port = data.split(':')
124
+ port = port ? port.to_i : 5672
125
+ @hosts << Fleck::HostRating.new(host: host, port: port)
126
+ @credentials["#{host}:#{port}"] ||= { user: @default_user, pass: @default_pass }
127
+ end
128
+
129
+ def add_host_from_hash(data)
130
+ data = data.to_hash_with_indifferent_access
131
+ host = data[:host] || @default_host
132
+ port = data[:port] || @default_port
133
+ @hosts << Fleck::HostRating.new(host: host, port: port)
134
+ @credentials["#{host}:#{port}"] ||= { user: data[:user] || @default_user, pass: data[:pass] || @default_pass }
135
+ end
136
+
137
+ def severity_color(severity)
138
+ case severity
139
+ when 'DEBUG' then '#512DA8'
140
+ when 'INFO' then '#33691E'
141
+ when 'WARN' then '#E65100'
142
+ when 'ERROR', 'FATAL' then '#B71C1C'
143
+ else '#00BCD4'
144
+ end
145
+ end
146
+ end
147
+ end