liebre 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|