liebre 0.1.3
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 +7 -0
- data/.gitignore +51 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +47 -0
- data/LICENSE +22 -0
- data/README.md +268 -0
- data/lib/liebre.rb +24 -0
- data/lib/liebre/common.rb +7 -0
- data/lib/liebre/common/utils.rb +24 -0
- data/lib/liebre/config.rb +48 -0
- data/lib/liebre/connection_manager.rb +64 -0
- data/lib/liebre/publisher.rb +88 -0
- data/lib/liebre/runner.rb +59 -0
- data/lib/liebre/runner/consumers.rb +36 -0
- data/lib/liebre/runner/starter.rb +40 -0
- data/lib/liebre/runner/starter/consumer.rb +91 -0
- data/lib/liebre/runner/starter/consumer/handler.rb +35 -0
- data/lib/liebre/runner/starter/resources.rb +39 -0
- data/lib/liebre/runner/starter/resources/queue_builder.rb +58 -0
- data/lib/liebre/runner/starter/rpc.rb +45 -0
- data/lib/liebre/tasks.rb +12 -0
- data/lib/liebre/version.rb +5 -0
- data/liebre.gemspec +26 -0
- data/spec/config/liebre.yml +47 -0
- data/spec/config/rabbitmq.yml +35 -0
- data/spec/integration_spec.rb +73 -0
- data/spec/liebre/config_spec.rb +62 -0
- data/spec/liebre/connection_manager_spec.rb +40 -0
- data/spec/liebre/publisher_spec.rb +86 -0
- data/spec/liebre/runner/consumers_spec.rb +59 -0
- data/spec/liebre/runner/starter/consumer_spec.rb +140 -0
- data/spec/liebre/runner/starter/resources/queue_builder_spec.rb +69 -0
- data/spec/liebre/runner/starter/resources_spec.rb +36 -0
- data/spec/liebre/runner/starter/rpc_spec.rb +92 -0
- data/spec/liebre/runner/starter_spec.rb +59 -0
- data/spec/liebre/runner_spec.rb +50 -0
- data/spec/spec_helper.rb +23 -0
- metadata +172 -0
@@ -0,0 +1,48 @@
|
|
1
|
+
require "yaml"
|
2
|
+
|
3
|
+
module Liebre
|
4
|
+
class Config
|
5
|
+
|
6
|
+
CONFIG_PATH = File.expand_path("config/liebre.yml")
|
7
|
+
CONNECTION_PATH = File.expand_path("config/rabbitmq.yml")
|
8
|
+
DEFAULT_LOGGER = Logger.new STDOUT
|
9
|
+
DEFAULT_RPC_TIMEOUT = 5
|
10
|
+
|
11
|
+
class << self
|
12
|
+
attr_accessor :env
|
13
|
+
attr_writer :config_path, :connection_path, :logger
|
14
|
+
|
15
|
+
def config_path
|
16
|
+
@config_path || CONFIG_PATH
|
17
|
+
end
|
18
|
+
|
19
|
+
def connection_path
|
20
|
+
@connection_path || CONNECTION_PATH
|
21
|
+
end
|
22
|
+
|
23
|
+
def logger
|
24
|
+
@logger || DEFAULT_LOGGER
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
def consumers
|
30
|
+
config.fetch 'consumers', {}
|
31
|
+
end
|
32
|
+
|
33
|
+
def publishers
|
34
|
+
config.fetch 'publishers', {}
|
35
|
+
end
|
36
|
+
|
37
|
+
def rpc_request_timeout
|
38
|
+
config.fetch 'rpc_request_timeout', DEFAULT_RPC_TIMEOUT
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def config
|
44
|
+
@config ||= YAML.load_file self.class.config_path
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require "yaml"
|
2
|
+
require "bunny"
|
3
|
+
|
4
|
+
module Liebre
|
5
|
+
class ConnectionManager
|
6
|
+
|
7
|
+
def initialize path = Liebre::Config.connection_path
|
8
|
+
@path = path
|
9
|
+
@connections = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def start
|
13
|
+
initialize_connections
|
14
|
+
connections.each do |_, bunny|
|
15
|
+
bunny.start
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def ensure_started
|
20
|
+
all_open = !@connections.empty? and @connections.all? do |_, bunny|
|
21
|
+
bunny.open?
|
22
|
+
end
|
23
|
+
restart unless all_open
|
24
|
+
end
|
25
|
+
|
26
|
+
def restart
|
27
|
+
stop
|
28
|
+
start
|
29
|
+
end
|
30
|
+
|
31
|
+
def get connection_name
|
32
|
+
connections[connection_name.to_sym]
|
33
|
+
end
|
34
|
+
|
35
|
+
def stop
|
36
|
+
connections.each do |_, bunny|
|
37
|
+
if bunny and bunny.open?
|
38
|
+
bunny.close
|
39
|
+
end
|
40
|
+
end
|
41
|
+
connections.clear
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def initialize_connections
|
47
|
+
config.each do |name, conf|
|
48
|
+
@connections[name.to_sym] = connection_for(conf)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def connection_for config
|
53
|
+
Bunny.new(config)
|
54
|
+
end
|
55
|
+
|
56
|
+
def config
|
57
|
+
result = YAML.load_file(path)
|
58
|
+
Liebre.env ? result.fetch(Liebre.env) : result
|
59
|
+
end
|
60
|
+
|
61
|
+
attr_reader :path, :connections
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module Liebre
|
2
|
+
class Publisher
|
3
|
+
|
4
|
+
def initialize publisher_name
|
5
|
+
@publisher_name = publisher_name
|
6
|
+
end
|
7
|
+
|
8
|
+
def enqueue message, options = {}
|
9
|
+
with_connection do
|
10
|
+
exchange.publish message, options
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def enqueue_and_wait message, options = {}
|
15
|
+
result = nil
|
16
|
+
with_connection do
|
17
|
+
begin
|
18
|
+
correlation_id = options[:correlation_id] ||= generate_uuid
|
19
|
+
reply_queue = reply_queue correlation_id
|
20
|
+
options[:reply_to] = reply_queue.name
|
21
|
+
exchange.publish message, options
|
22
|
+
Timeout::timeout(Liebre.config.rpc_request_timeout) do
|
23
|
+
reply_queue.subscribe(:block => true) do |delivery_info, meta, payload|
|
24
|
+
if meta[:correlation_id] == correlation_id
|
25
|
+
result = payload
|
26
|
+
channel.consumers[delivery_info.consumer_tag].cancel
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
rescue Timeout::Error
|
31
|
+
#do nothing
|
32
|
+
ensure
|
33
|
+
reply_queue.delete
|
34
|
+
end
|
35
|
+
end
|
36
|
+
result
|
37
|
+
end
|
38
|
+
|
39
|
+
alias_method :rpc, :enqueue_and_wait
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def with_connection
|
44
|
+
connection_manager.ensure_started
|
45
|
+
yield
|
46
|
+
end
|
47
|
+
|
48
|
+
def reply_queue correlation_id
|
49
|
+
queue_name = "#{publisher_name}_callback_#{correlation_id}"
|
50
|
+
channel.queue queue_name, :exclusive => true
|
51
|
+
end
|
52
|
+
|
53
|
+
def exchange
|
54
|
+
Liebre::Common::Utils.create_exchange channel, exchange_config
|
55
|
+
end
|
56
|
+
|
57
|
+
def channel
|
58
|
+
@channel ||= connection_manager.get(connection_name).create_channel
|
59
|
+
end
|
60
|
+
|
61
|
+
def publishers
|
62
|
+
Liebre.config.publishers
|
63
|
+
end
|
64
|
+
|
65
|
+
def exchange_config
|
66
|
+
config.fetch("exchange")
|
67
|
+
end
|
68
|
+
|
69
|
+
def config
|
70
|
+
publishers.fetch publisher_name
|
71
|
+
end
|
72
|
+
|
73
|
+
def connection_name
|
74
|
+
config.fetch('connection_name', 'default').to_sym
|
75
|
+
end
|
76
|
+
|
77
|
+
def connection_manager
|
78
|
+
@connection_manager ||= ConnectionManager.new
|
79
|
+
end
|
80
|
+
|
81
|
+
def generate_uuid
|
82
|
+
SecureRandom.uuid
|
83
|
+
end
|
84
|
+
|
85
|
+
attr_reader :publisher_name
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Liebre
|
2
|
+
class Runner
|
3
|
+
|
4
|
+
autoload :Consumers, 'liebre/runner/consumers'
|
5
|
+
autoload :Starter, 'liebre/runner/starter'
|
6
|
+
|
7
|
+
RETRY_INTERVAL = 5
|
8
|
+
|
9
|
+
def initialize retry_interval = RETRY_INTERVAL
|
10
|
+
@retry_interval = retry_interval
|
11
|
+
end
|
12
|
+
|
13
|
+
def start
|
14
|
+
setup_shutdown
|
15
|
+
conn_manager.restart
|
16
|
+
start_consumers
|
17
|
+
sleep
|
18
|
+
rescue StandardError => e
|
19
|
+
log_and_wait(e)
|
20
|
+
retry
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def setup_shutdown
|
26
|
+
Signal.trap("TERM") { do_shutdown; exit }
|
27
|
+
end
|
28
|
+
|
29
|
+
def do_shutdown
|
30
|
+
Thread.start do
|
31
|
+
logger.info("Closing AMQP connection...")
|
32
|
+
conn_manager.stop
|
33
|
+
logger.info("AMQP connection closed")
|
34
|
+
end.join
|
35
|
+
end
|
36
|
+
|
37
|
+
def start_consumers
|
38
|
+
consumers = Consumers.new(conn_manager)
|
39
|
+
consumers.start_all
|
40
|
+
end
|
41
|
+
|
42
|
+
def log_and_wait e
|
43
|
+
logger.warn(e)
|
44
|
+
sleep(retry_interval)
|
45
|
+
logger.warn("Retrying connection")
|
46
|
+
end
|
47
|
+
|
48
|
+
def logger
|
49
|
+
Liebre.logger
|
50
|
+
end
|
51
|
+
|
52
|
+
def conn_manager
|
53
|
+
@conn_manager ||= ConnectionManager.new
|
54
|
+
end
|
55
|
+
|
56
|
+
attr_reader :retry_interval
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Liebre
|
2
|
+
class Runner
|
3
|
+
class Consumers
|
4
|
+
|
5
|
+
def initialize connection_manager
|
6
|
+
@connection_manager = connection_manager
|
7
|
+
end
|
8
|
+
|
9
|
+
def consumer_names
|
10
|
+
consumers.keys
|
11
|
+
end
|
12
|
+
|
13
|
+
def start_all
|
14
|
+
consumer_names.each { |name| start(name) }
|
15
|
+
end
|
16
|
+
|
17
|
+
def start name
|
18
|
+
params = consumers.fetch(name)
|
19
|
+
num_threads = params.fetch("num_threads", 1)
|
20
|
+
num_threads.times do
|
21
|
+
starter = Starter.new(connection_manager, params)
|
22
|
+
starter.call
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def consumers
|
29
|
+
Liebre.config.consumers
|
30
|
+
end
|
31
|
+
|
32
|
+
attr_reader :connection_manager
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Liebre
|
2
|
+
class Runner
|
3
|
+
class Starter
|
4
|
+
|
5
|
+
autoload :Consumer, 'liebre/runner/starter/consumer'
|
6
|
+
autoload :Resources, 'liebre/runner/starter/resources'
|
7
|
+
autoload :RPC, 'liebre/runner/starter/rpc'
|
8
|
+
|
9
|
+
def initialize connection_manager, config
|
10
|
+
@connection_manager = connection_manager
|
11
|
+
@config = config
|
12
|
+
end
|
13
|
+
|
14
|
+
def call
|
15
|
+
consumer_class.new(connection, config).call
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def consumer_class
|
21
|
+
is_rpc? ? RPC : Consumer
|
22
|
+
end
|
23
|
+
|
24
|
+
def is_rpc?
|
25
|
+
config.fetch("rpc", false)
|
26
|
+
end
|
27
|
+
|
28
|
+
def connection
|
29
|
+
connection_manager.get connection_name
|
30
|
+
end
|
31
|
+
|
32
|
+
def connection_name
|
33
|
+
config.fetch('connection_name', 'default').to_sym
|
34
|
+
end
|
35
|
+
|
36
|
+
attr_reader :connection_manager, :config
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module Liebre
|
2
|
+
class Runner
|
3
|
+
class Starter
|
4
|
+
class Consumer
|
5
|
+
|
6
|
+
autoload :Handler, "liebre/runner/starter/consumer/handler"
|
7
|
+
|
8
|
+
def initialize connection, config
|
9
|
+
@connection = connection
|
10
|
+
@config = config
|
11
|
+
end
|
12
|
+
|
13
|
+
def call
|
14
|
+
initialize_error_queue
|
15
|
+
initialize_queue
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def initialize_queue
|
21
|
+
queue.subscribe(:manual_ack => true) do |info, meta, payload|
|
22
|
+
begin
|
23
|
+
logger.debug "Received message for #{klass.name}: #{payload} - #{meta}"
|
24
|
+
consumer = klass.new(payload, meta)
|
25
|
+
response = consumer.call
|
26
|
+
handler.respond response, info
|
27
|
+
rescue => e
|
28
|
+
logger.error e.inspect
|
29
|
+
logger.error e.backtrace.join("\n")
|
30
|
+
handler.respond :error, info
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def initialize_error_queue
|
36
|
+
Resources.new(connection, error_config).queue
|
37
|
+
end
|
38
|
+
|
39
|
+
def klass
|
40
|
+
@klass ||= Kernel.const_get config.fetch("class_name")
|
41
|
+
end
|
42
|
+
|
43
|
+
def handler
|
44
|
+
@handler ||= Handler.new(channel)
|
45
|
+
end
|
46
|
+
|
47
|
+
def channel
|
48
|
+
resources.channel
|
49
|
+
end
|
50
|
+
|
51
|
+
def exchange
|
52
|
+
resources.exchange
|
53
|
+
end
|
54
|
+
|
55
|
+
def queue
|
56
|
+
resources.queue
|
57
|
+
end
|
58
|
+
|
59
|
+
def resources
|
60
|
+
@resources ||= Resources.new(connection, parse_config)
|
61
|
+
end
|
62
|
+
|
63
|
+
def parse_config
|
64
|
+
result = clone_hash config
|
65
|
+
result['queue']['opts']['arguments'] ||= {}
|
66
|
+
result['queue']['opts']['arguments']['x-dead-letter-exchange'] = result['exchange']['name'] + "-error"
|
67
|
+
result
|
68
|
+
end
|
69
|
+
|
70
|
+
def error_config
|
71
|
+
result = clone_hash config
|
72
|
+
result['exchange']['name'] += "-error"
|
73
|
+
result['queue']['name'] += "-error"
|
74
|
+
result['queue']['opts']['exclusive'] = true
|
75
|
+
result
|
76
|
+
end
|
77
|
+
|
78
|
+
def logger
|
79
|
+
Liebre::Config.logger
|
80
|
+
end
|
81
|
+
|
82
|
+
def clone_hash hash
|
83
|
+
Marshal.load(Marshal.dump(hash))
|
84
|
+
end
|
85
|
+
|
86
|
+
attr_reader :connection, :config
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Liebre
|
2
|
+
class Runner
|
3
|
+
class Starter
|
4
|
+
class Consumer
|
5
|
+
class Handler
|
6
|
+
|
7
|
+
def initialize channel
|
8
|
+
@channel = channel
|
9
|
+
end
|
10
|
+
|
11
|
+
def respond action, meta
|
12
|
+
send(action, meta.delivery_tag)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def ack delivery_tag
|
18
|
+
channel.acknowledge delivery_tag
|
19
|
+
end
|
20
|
+
|
21
|
+
def reject delivery_tag
|
22
|
+
channel.reject delivery_tag, true
|
23
|
+
end
|
24
|
+
|
25
|
+
def error delivery_tag
|
26
|
+
channel.reject delivery_tag, false
|
27
|
+
end
|
28
|
+
|
29
|
+
attr_reader :channel
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|