liebre 0.1.3

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