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.
Files changed (38) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +51 -0
  3. data/Gemfile +4 -0
  4. data/Gemfile.lock +47 -0
  5. data/LICENSE +22 -0
  6. data/README.md +268 -0
  7. data/lib/liebre.rb +24 -0
  8. data/lib/liebre/common.rb +7 -0
  9. data/lib/liebre/common/utils.rb +24 -0
  10. data/lib/liebre/config.rb +48 -0
  11. data/lib/liebre/connection_manager.rb +64 -0
  12. data/lib/liebre/publisher.rb +88 -0
  13. data/lib/liebre/runner.rb +59 -0
  14. data/lib/liebre/runner/consumers.rb +36 -0
  15. data/lib/liebre/runner/starter.rb +40 -0
  16. data/lib/liebre/runner/starter/consumer.rb +91 -0
  17. data/lib/liebre/runner/starter/consumer/handler.rb +35 -0
  18. data/lib/liebre/runner/starter/resources.rb +39 -0
  19. data/lib/liebre/runner/starter/resources/queue_builder.rb +58 -0
  20. data/lib/liebre/runner/starter/rpc.rb +45 -0
  21. data/lib/liebre/tasks.rb +12 -0
  22. data/lib/liebre/version.rb +5 -0
  23. data/liebre.gemspec +26 -0
  24. data/spec/config/liebre.yml +47 -0
  25. data/spec/config/rabbitmq.yml +35 -0
  26. data/spec/integration_spec.rb +73 -0
  27. data/spec/liebre/config_spec.rb +62 -0
  28. data/spec/liebre/connection_manager_spec.rb +40 -0
  29. data/spec/liebre/publisher_spec.rb +86 -0
  30. data/spec/liebre/runner/consumers_spec.rb +59 -0
  31. data/spec/liebre/runner/starter/consumer_spec.rb +140 -0
  32. data/spec/liebre/runner/starter/resources/queue_builder_spec.rb +69 -0
  33. data/spec/liebre/runner/starter/resources_spec.rb +36 -0
  34. data/spec/liebre/runner/starter/rpc_spec.rb +92 -0
  35. data/spec/liebre/runner/starter_spec.rb +59 -0
  36. data/spec/liebre/runner_spec.rb +50 -0
  37. data/spec/spec_helper.rb +23 -0
  38. 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