message_queue 0.0.4 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +87 -2
- data/examples/{publisher.rb → producer.rb} +4 -3
- data/examples/producible_consumable.rb +28 -0
- data/lib/message_queue/adapter.rb +4 -0
- data/lib/message_queue/adapters/bunny/connection.rb +26 -28
- data/lib/message_queue/adapters/bunny/consumer.rb +21 -11
- data/lib/message_queue/adapters/bunny/{publisher.rb → producer.rb} +12 -12
- data/lib/message_queue/adapters/memory/connection.rb +12 -0
- data/lib/message_queue/adapters/memory/consumer.rb +34 -0
- data/lib/message_queue/adapters/memory/producer.rb +11 -0
- data/lib/message_queue/adapters/memory.rb +11 -0
- data/lib/message_queue/connection.rb +64 -0
- data/lib/message_queue/consumable.rb +83 -0
- data/lib/message_queue/consumable_runner.rb +28 -0
- data/lib/message_queue/consumer.rb +18 -0
- data/lib/message_queue/error_handlers/airbrake.rb +19 -0
- data/lib/message_queue/error_handlers/logger.rb +16 -0
- data/lib/message_queue/logging.rb +30 -0
- data/lib/message_queue/message.rb +14 -0
- data/lib/message_queue/options_helper.rb +26 -0
- data/lib/message_queue/producer.rb +29 -0
- data/lib/message_queue/producible.rb +49 -0
- data/lib/message_queue/rails.rb +19 -0
- data/lib/message_queue/serializer.rb +4 -0
- data/lib/message_queue/serializers/json.rb +4 -0
- data/lib/message_queue/serializers/message_pack.rb +4 -0
- data/lib/message_queue/serializers/plain.rb +4 -0
- data/lib/message_queue/version.rb +1 -1
- data/lib/message_queue.rb +118 -1
- data/test/adapters/bunny_test.rb +47 -32
- data/test/adapters/memory_test.rb +26 -0
- data/test/consumable_test.rb +45 -0
- data/test/message_queue_test.rb +22 -0
- data/test/options_helper_test.rb +23 -0
- data/test/producible_test.rb +41 -0
- data/test/support/message_queue.yml +1 -0
- metadata +29 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6e91039cfe1a4655089a9d9fe51b6f5f40ac0040
|
4
|
+
data.tar.gz: 399c152cae5cb9af53a1831bf6f7e9b86c0ff3f9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 41fd54adc323d25ee56b597da2c8b25b8cd72177af4fae1d9f79b31412e953caf1c5d738c0f767fe684cd758bc6902f93d8f8f30fd7da1849c05dadf19d71f13
|
7
|
+
data.tar.gz: 62e935dde186e86ee8c0df12ae81d8e82b7e40e0ae0a35323d9f040c99f827291d054bff14f74c3da425f4f0f402d3c80643a90ed55d83e260bde7642f30ca56
|
data/README.md
CHANGED
@@ -18,9 +18,15 @@ Or install it yourself as:
|
|
18
18
|
|
19
19
|
## Usage
|
20
20
|
|
21
|
+
### with_connection
|
22
|
+
|
23
|
+
`with_connection` initializes a connection using the specified adapter
|
24
|
+
and serializer, connects to the message queue, runs a block of code and
|
25
|
+
disconnects.
|
26
|
+
|
21
27
|
```ruby
|
22
28
|
MessageQueue.with_connection(:adapter => :bunny, :serializer => :message_pack) do |conn|
|
23
|
-
|
29
|
+
producer = conn.new_producer(
|
24
30
|
:exchange => {
|
25
31
|
:name => "time",
|
26
32
|
:type => :topic
|
@@ -44,16 +50,95 @@ MessageQueue.with_connection(:adapter => :bunny, :serializer => :message_pack) d
|
|
44
50
|
puts "Received message: #{payload}"
|
45
51
|
end
|
46
52
|
|
47
|
-
|
53
|
+
producer.publish Time.now.to_s
|
48
54
|
|
49
55
|
sleep 1
|
50
56
|
end
|
51
57
|
```
|
52
58
|
|
59
|
+
You could maintain a global connection by using the `connect` method on
|
60
|
+
`MessageQueue`.
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
MessageQueue.connect(:adater => :bunny, :serializer => :json)
|
64
|
+
puts MessageQueue.connected? # => true
|
65
|
+
|
66
|
+
producer = MessageQueue.new_producer(
|
67
|
+
:exchange => {
|
68
|
+
:name => "time",
|
69
|
+
:type => :topic
|
70
|
+
},
|
71
|
+
:message => {
|
72
|
+
:routing_key => "time.now"
|
73
|
+
}
|
74
|
+
)
|
75
|
+
|
76
|
+
consumer = MessageQueue.new_consumer(
|
77
|
+
:queue => {
|
78
|
+
:name => "print_time_now"
|
79
|
+
},
|
80
|
+
:exchange => {
|
81
|
+
:name => "time",
|
82
|
+
:routing_key => "time.#"
|
83
|
+
}
|
84
|
+
)
|
85
|
+
|
86
|
+
consumer.subscribe do |delivery_info, metadata, payload|
|
87
|
+
puts "Received message: #{payload}"
|
88
|
+
end
|
89
|
+
|
90
|
+
producer.publish Time.now.to_s
|
91
|
+
|
92
|
+
sleep 1
|
93
|
+
|
94
|
+
MessageQueue.disconnect
|
95
|
+
puts MessageQueue.connected? # => false
|
96
|
+
```
|
97
|
+
|
98
|
+
You could also mix in the `MessageQueue::Producible` module and the
|
99
|
+
`MessageQueue::Consumable` module to your producer class and consumer
|
100
|
+
class respectively. The consumer class needs to implement a `process`
|
101
|
+
method which will be passed a `MessageQueue::Message` instance when it
|
102
|
+
receives a message.
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
class Producer
|
106
|
+
include MessageQueue::Producible
|
107
|
+
|
108
|
+
exchange :name => "time", :type => :topic
|
109
|
+
message :routing_key => "time.now", :mandatory => true
|
110
|
+
end
|
111
|
+
|
112
|
+
class Consumer
|
113
|
+
include MessageQueue::Consumable
|
114
|
+
|
115
|
+
queue :name => "print_time_now"
|
116
|
+
exchange :name => "time", :routing_key => "time.#"
|
117
|
+
|
118
|
+
def process(message)
|
119
|
+
puts "Received message #{message.payload}"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
MessageQueue.connect(:adater => :bunny, :serializer => :json)
|
124
|
+
Producer.new.publish(Time.now.to_s)
|
125
|
+
|
126
|
+
sleep 1
|
127
|
+
|
128
|
+
MessageQueue.disconnect
|
129
|
+
```
|
130
|
+
|
53
131
|
## Examples
|
54
132
|
|
55
133
|
See [examples](https://github.com/jingweno/message_queue/tree/master/examples).
|
56
134
|
|
135
|
+
## Rails
|
136
|
+
|
137
|
+
For Rails, `message_queue` automatically loads settings from
|
138
|
+
`RAILS_ROOT/config/message_queue.yml`. If the file doesn't exist, it
|
139
|
+
initializes the queue in
|
140
|
+
[memory](https://github.com/jingweno/message_queue/tree/master/lib/message_queue/adapters/memory) mode.
|
141
|
+
|
57
142
|
## Contributing
|
58
143
|
|
59
144
|
1. Fork it
|
@@ -1,15 +1,16 @@
|
|
1
1
|
require_relative "../lib/message_queue"
|
2
2
|
|
3
3
|
MessageQueue.with_connection(:adapter => :bunny, :serializer => :message_pack) do |conn|
|
4
|
-
|
4
|
+
producer = conn.new_producer(
|
5
5
|
:exchange => {
|
6
6
|
:name => "time",
|
7
7
|
:type => :topic
|
8
8
|
},
|
9
9
|
:message => {
|
10
|
-
:routing_key => "time.now"
|
10
|
+
:routing_key => "time.now",
|
11
|
+
:mandatory => true
|
11
12
|
}
|
12
13
|
)
|
13
14
|
|
14
|
-
|
15
|
+
producer.publish Time.now.to_s
|
15
16
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require_relative "../lib/message_queue"
|
2
|
+
|
3
|
+
class Producer
|
4
|
+
include MessageQueue::Producible
|
5
|
+
|
6
|
+
exchange :name => "time", :type => :topic
|
7
|
+
message :routing_key => "time.now", :mandatory => true
|
8
|
+
end
|
9
|
+
|
10
|
+
class Consumer
|
11
|
+
include MessageQueue::Consumable
|
12
|
+
|
13
|
+
queue :name => "print_time_now"
|
14
|
+
exchange :name => "time", :routing_key => "time.#"
|
15
|
+
|
16
|
+
def process(message)
|
17
|
+
puts "Received message #{message.payload}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
MessageQueue.connect(:adapter => :bunny, :serializer => :json)
|
22
|
+
MessageQueue.run_consumables
|
23
|
+
|
24
|
+
Producer.new.publish(Time.now.to_s)
|
25
|
+
|
26
|
+
sleep 1
|
27
|
+
|
28
|
+
MessageQueue.disconnect
|
@@ -1,25 +1,13 @@
|
|
1
|
-
class MessageQueue::Adapters::Bunny::Connection
|
2
|
-
attr_reader :
|
3
|
-
|
4
|
-
# Public: Initialize a new Bunny connection.
|
5
|
-
#
|
6
|
-
# serializer - The Serializer for dumping and loading payload.
|
7
|
-
#
|
8
|
-
# settings - The Hash settings used to connect with Bunny.
|
9
|
-
# Details in http://rubybunny.info/articles/connecting.html.
|
10
|
-
#
|
11
|
-
# Returns a Connection wrapper for Bunny.
|
12
|
-
def initialize(serializer, settings)
|
13
|
-
@serializer = serializer
|
14
|
-
@settings = settings
|
15
|
-
end
|
1
|
+
class MessageQueue::Adapters::Bunny::Connection < MessageQueue::Connection
|
2
|
+
attr_reader :connection
|
16
3
|
|
17
4
|
# Public: Connect to RabbitMQ
|
18
5
|
#
|
19
6
|
# Returns the Bunny instance
|
20
7
|
def connect
|
21
8
|
@connection ||= begin
|
22
|
-
|
9
|
+
super
|
10
|
+
bunny = ::Bunny.new(bunny_settings)
|
23
11
|
bunny.start
|
24
12
|
bunny
|
25
13
|
end
|
@@ -30,27 +18,23 @@ class MessageQueue::Adapters::Bunny::Connection
|
|
30
18
|
# Returns nothing
|
31
19
|
def disconnect
|
32
20
|
if @connection
|
21
|
+
super
|
33
22
|
@connection.close if @connection.open?
|
34
23
|
@connection = nil
|
35
24
|
end
|
36
25
|
end
|
37
26
|
|
38
|
-
# Public:
|
27
|
+
# Public: Check if it's connected to the message queue
|
39
28
|
#
|
40
|
-
# Returns
|
41
|
-
def
|
42
|
-
|
43
|
-
connect
|
44
|
-
block.call(self)
|
45
|
-
ensure
|
46
|
-
disconnect
|
47
|
-
end
|
29
|
+
# Returns true if it's connected
|
30
|
+
def connected?
|
31
|
+
@connection.open? if @connection
|
48
32
|
end
|
49
33
|
|
50
|
-
def
|
34
|
+
def new_producer(options)
|
51
35
|
raise "No connection to RabbitMQ" unless connection
|
52
36
|
|
53
|
-
|
37
|
+
Producer.new(self, options)
|
54
38
|
end
|
55
39
|
|
56
40
|
def new_consumer(options)
|
@@ -58,7 +42,21 @@ class MessageQueue::Adapters::Bunny::Connection
|
|
58
42
|
|
59
43
|
Consumer.new(self, options)
|
60
44
|
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def bunny_settings
|
49
|
+
default_bunny_settings.merge(settings)
|
50
|
+
end
|
51
|
+
|
52
|
+
def default_bunny_settings
|
53
|
+
{
|
54
|
+
:heartbeat => 1,
|
55
|
+
:automatically_recover => true,
|
56
|
+
:network_recovery_interval => 1
|
57
|
+
}
|
58
|
+
end
|
61
59
|
end
|
62
60
|
|
63
|
-
require "message_queue/adapters/bunny/
|
61
|
+
require "message_queue/adapters/bunny/producer"
|
64
62
|
require "message_queue/adapters/bunny/consumer"
|
@@ -1,5 +1,4 @@
|
|
1
|
-
class MessageQueue::Adapters::Bunny::Connection::Consumer
|
2
|
-
attr_reader :connection
|
1
|
+
class MessageQueue::Adapters::Bunny::Connection::Consumer < MessageQueue::Consumer
|
3
2
|
attr_reader :queue_options, :queue_name
|
4
3
|
attr_reader :exchange_options, :exchange_name, :exchange_routing_key
|
5
4
|
attr_reader :subscribe_options
|
@@ -25,27 +24,34 @@ class MessageQueue::Adapters::Bunny::Connection::Consumer
|
|
25
24
|
#
|
26
25
|
# Returns a Consumer.
|
27
26
|
def initialize(connection, options = {})
|
28
|
-
|
27
|
+
super
|
29
28
|
|
30
|
-
|
31
|
-
|
32
|
-
@queue_options = options.fetch(:queue)
|
29
|
+
@queue_options = self.options.fetch(:queue)
|
33
30
|
@queue_name = queue_options.delete(:name) || (raise "Missing queue name")
|
34
31
|
|
35
|
-
@exchange_options = options.fetch(:exchange)
|
32
|
+
@exchange_options = self.options.fetch(:exchange)
|
36
33
|
@exchange_name = exchange_options.delete(:name) || (raise "Missing exchange name")
|
37
34
|
@exchange_routing_key = exchange_options.delete(:routing_key) || queue_name
|
38
35
|
|
39
|
-
@subscribe_options = options.fetch(:subscribe, {})
|
36
|
+
@subscribe_options = self.options.fetch(:subscribe, {}).merge(:ack => true)
|
40
37
|
end
|
41
38
|
|
42
39
|
def subscribe(options = {}, &block)
|
43
40
|
@subscription = queue.subscribe(subscribe_options.merge(options)) do |delivery_info, metadata, payload|
|
44
|
-
|
41
|
+
begin
|
42
|
+
message = MessageQueue::Message.new(:message_id => metadata[:message_id],
|
43
|
+
:type => metadata[:type],
|
44
|
+
:timestamp => metadata[:timestamp],
|
45
|
+
:routing_key => delivery_info[:routing_key],
|
46
|
+
:payload => load_object(payload))
|
47
|
+
block.call(message)
|
48
|
+
ensure
|
49
|
+
ack(delivery_info.delivery_tag)
|
50
|
+
end
|
45
51
|
end
|
46
52
|
end
|
47
53
|
|
48
|
-
def unsubscribe
|
54
|
+
def unsubscribe(options = {})
|
49
55
|
@subscription.cancel if @subscription
|
50
56
|
end
|
51
57
|
|
@@ -53,9 +59,13 @@ class MessageQueue::Adapters::Bunny::Connection::Consumer
|
|
53
59
|
@queue ||= channel.queue(queue_name, queue_options).bind(exchange_name, :routing_key => exchange_routing_key)
|
54
60
|
end
|
55
61
|
|
62
|
+
def ack(delivery_tag)
|
63
|
+
channel.ack(delivery_tag, false)
|
64
|
+
end
|
65
|
+
|
56
66
|
private
|
57
67
|
|
58
68
|
def channel
|
59
|
-
connection.connection.create_channel
|
69
|
+
@channel ||= connection.connection.create_channel
|
60
70
|
end
|
61
71
|
end
|
@@ -1,13 +1,13 @@
|
|
1
|
-
class MessageQueue::Adapters::Bunny::Connection::
|
2
|
-
attr_reader :
|
1
|
+
class MessageQueue::Adapters::Bunny::Connection::Producer < MessageQueue::Producer
|
2
|
+
attr_reader :exchange
|
3
3
|
attr_reader :exchange_options, :exchange_name, :exchange_type
|
4
4
|
attr_reader :message_options
|
5
5
|
|
6
|
-
# Public: Initialize a new Bunny
|
6
|
+
# Public: Initialize a new Bunny producer.
|
7
7
|
#
|
8
8
|
# connection - The Bunny Connection.
|
9
9
|
# options - The Hash options used to initialize the exchange
|
10
|
-
# of a
|
10
|
+
# of a producer:
|
11
11
|
# :exchange -
|
12
12
|
# :name - The String exchange name.
|
13
13
|
# :type - The Symbol exchange type.
|
@@ -21,20 +21,20 @@ class MessageQueue::Adapters::Bunny::Connection::Publisher
|
|
21
21
|
#
|
22
22
|
# Returns a Publisher.
|
23
23
|
def initialize(connection, options = {})
|
24
|
-
|
24
|
+
super
|
25
25
|
|
26
|
-
|
27
|
-
|
28
|
-
@exchange_options = options.fetch(:exchange)
|
26
|
+
@exchange_options = self.options.fetch(:exchange)
|
29
27
|
@exchange_name = exchange_options.delete(:name) || (raise "Missing exchange name")
|
30
28
|
@exchange_type = exchange_options.delete(:type) || (raise "Missing exchange type")
|
31
29
|
|
32
|
-
@message_options = options.fetch(:message)
|
30
|
+
@message_options = self.options.fetch(:message)
|
33
31
|
|
34
|
-
@exchange = connection.connection.
|
32
|
+
@exchange = connection.connection.default_channel.send(exchange_type, exchange_name, exchange_options)
|
35
33
|
end
|
36
34
|
|
37
|
-
def publish(
|
38
|
-
|
35
|
+
def publish(object, options = {})
|
36
|
+
options = message_options.merge(default_options).merge(options)
|
37
|
+
object = dump_object(object)
|
38
|
+
exchange.publish(object, options)
|
39
39
|
end
|
40
40
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class MessageQueue::Adapters::Memory::Connection < MessageQueue::Connection
|
2
|
+
def new_producer(options = {})
|
3
|
+
Producer.new(self, options)
|
4
|
+
end
|
5
|
+
|
6
|
+
def new_consumer(options = {})
|
7
|
+
Consumer.new(self, options)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
require "message_queue/adapters/memory/producer"
|
12
|
+
require "message_queue/adapters/memory/consumer"
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class MessageQueue::Adapters::Memory::Connection::Consumer < MessageQueue::Consumer
|
2
|
+
attr_reader :queue, :block
|
3
|
+
|
4
|
+
def initialize(*args)
|
5
|
+
super
|
6
|
+
@queue = []
|
7
|
+
end
|
8
|
+
|
9
|
+
def subscribe(options = {}, &block)
|
10
|
+
producer = options.fetch(:producer)
|
11
|
+
producer.add_observer(self)
|
12
|
+
@block = block
|
13
|
+
end
|
14
|
+
|
15
|
+
def unsubscribe(options = {})
|
16
|
+
producer = options.fetch(:producer)
|
17
|
+
producer.delete_observer(self)
|
18
|
+
@block = nil
|
19
|
+
end
|
20
|
+
|
21
|
+
def update(object, options)
|
22
|
+
object = load_object(object)
|
23
|
+
message = MessageQueue::Message.new(:message_id => options[:message_id],
|
24
|
+
:type => options[:type],
|
25
|
+
:timestamp => options[:timestamp],
|
26
|
+
:routing_key => options[:routing_key],
|
27
|
+
:payload => load_object(object))
|
28
|
+
if block
|
29
|
+
block.call(message)
|
30
|
+
else
|
31
|
+
queue << message
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require "observer"
|
2
|
+
|
3
|
+
class MessageQueue::Adapters::Memory::Connection::Producer < MessageQueue::Producer
|
4
|
+
include Observable
|
5
|
+
|
6
|
+
def publish(object, options = {})
|
7
|
+
changed
|
8
|
+
notify_observers(dump_object(object), default_options.merge(options))
|
9
|
+
true
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require "message_queue/logging"
|
2
|
+
|
3
|
+
class MessageQueue::Connection
|
4
|
+
include MessageQueue::Logging
|
5
|
+
|
6
|
+
attr_reader :serializer, :settings
|
7
|
+
|
8
|
+
# Public: Initialize a new Bunny connection.
|
9
|
+
#
|
10
|
+
# serializer - The Serializer for dumping and loading payload.
|
11
|
+
#
|
12
|
+
# settings - The Hash settings used to connect.
|
13
|
+
#
|
14
|
+
#
|
15
|
+
# Returns a Connection wrapper for Bunny.
|
16
|
+
def initialize(serializer, settings)
|
17
|
+
@serializer = serializer
|
18
|
+
@settings = settings
|
19
|
+
end
|
20
|
+
|
21
|
+
# Public: Connect to the message queue
|
22
|
+
#
|
23
|
+
# Returns nothing
|
24
|
+
def connect
|
25
|
+
logger.info("Connecting to message queue with adapter #{self.class} and settings #{settings}")
|
26
|
+
end
|
27
|
+
|
28
|
+
# Public: Disconnect from the message queue
|
29
|
+
#
|
30
|
+
# Returns nothing
|
31
|
+
def disconnect
|
32
|
+
logger.info("Disconnecting from message queue")
|
33
|
+
end
|
34
|
+
|
35
|
+
# Public: Check if it's connected to the message queue
|
36
|
+
#
|
37
|
+
# Returns true if it's connected
|
38
|
+
def connected?
|
39
|
+
false
|
40
|
+
end
|
41
|
+
|
42
|
+
# Public: Connect to the message, execute the block and disconnect
|
43
|
+
#
|
44
|
+
# Returns nothing
|
45
|
+
def with_connection(&block)
|
46
|
+
begin
|
47
|
+
connect
|
48
|
+
block.call(self)
|
49
|
+
ensure
|
50
|
+
disconnect
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def new_producer(options = {})
|
55
|
+
Producer.new(self, options)
|
56
|
+
end
|
57
|
+
|
58
|
+
def new_consumer(options = {})
|
59
|
+
Consumer.new(self, options)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
require "message_queue/producer"
|
64
|
+
require "message_queue/consumer"
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require "message_queue/logging"
|
2
|
+
|
3
|
+
module MessageQueue
|
4
|
+
# A module to mix in a consumer class, for example:
|
5
|
+
#
|
6
|
+
# class Consumer
|
7
|
+
# include MessageQueue::Consumable
|
8
|
+
#
|
9
|
+
# queue :name => "print_time_now"
|
10
|
+
# exchange :name => "time", :routing_key => "time.#"
|
11
|
+
#
|
12
|
+
# def process(message)
|
13
|
+
# ...
|
14
|
+
# end
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# The consumer class needs to implement the process method which will be passed
|
18
|
+
# a MessageQueue::Message instance when it receives a message.
|
19
|
+
module Consumable
|
20
|
+
include Logging
|
21
|
+
|
22
|
+
def self.included(base)
|
23
|
+
base.extend(ClassMethods)
|
24
|
+
MessageQueue.register_consumable(base)
|
25
|
+
end
|
26
|
+
|
27
|
+
module ClassMethods
|
28
|
+
def queue(options = {})
|
29
|
+
queue_options.merge!(options)
|
30
|
+
end
|
31
|
+
|
32
|
+
def exchange(options = {})
|
33
|
+
exchange_options.merge!(options)
|
34
|
+
end
|
35
|
+
|
36
|
+
def subscribe(options = {})
|
37
|
+
subscribe_options.merge!(options)
|
38
|
+
end
|
39
|
+
|
40
|
+
def queue_options
|
41
|
+
@queue_options ||= {}
|
42
|
+
end
|
43
|
+
|
44
|
+
def exchange_options
|
45
|
+
@exchange_options ||= {}
|
46
|
+
end
|
47
|
+
|
48
|
+
def subscribe_options
|
49
|
+
@subscribe_options ||= {}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def initialize
|
54
|
+
@consumer = MessageQueue.new_consumer(:queue => self.class.queue_options,
|
55
|
+
:exchange => self.class.exchange_options,
|
56
|
+
:subscribe => self.class.subscribe_options)
|
57
|
+
end
|
58
|
+
|
59
|
+
def subscribe(options = {})
|
60
|
+
@consumer.subscribe(options) do |message|
|
61
|
+
begin
|
62
|
+
logger.info("Message(#{message.message_id || '-'}): " +
|
63
|
+
"routing key: #{message.routing_key}, " +
|
64
|
+
"type: #{message.type}, " +
|
65
|
+
"timestamp: #{message.timestamp}, " +
|
66
|
+
"consumer: #{@consumer.class}, " +
|
67
|
+
"payload: #{message.payload}")
|
68
|
+
process(message)
|
69
|
+
rescue StandardError => ex
|
70
|
+
handle_error(message, consumer, ex)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def handle_error(message, consumer, ex)
|
78
|
+
MessageQueue.error_handlers.each do |handler|
|
79
|
+
handler.handle(message, consumer, ex)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module MessageQueue
|
2
|
+
class ConsumableRunner
|
3
|
+
include Logging
|
4
|
+
|
5
|
+
attr_reader :consumables
|
6
|
+
|
7
|
+
def initialize(consumables)
|
8
|
+
@consumables = consumables
|
9
|
+
end
|
10
|
+
|
11
|
+
def run(options = {})
|
12
|
+
begin
|
13
|
+
block = !!options[:block]
|
14
|
+
consumables.each_with_index do |consumable, index|
|
15
|
+
# Blocks the last consumer
|
16
|
+
opts = if index < consumables.size - 1
|
17
|
+
{}
|
18
|
+
else
|
19
|
+
{ :block => block }
|
20
|
+
end
|
21
|
+
consumable.new.subscribe(opts)
|
22
|
+
end
|
23
|
+
rescue SignalException => ex
|
24
|
+
logger.info "Received Signal #{ex}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require "message_queue/options_helper"
|
2
|
+
|
3
|
+
module MessageQueue
|
4
|
+
class Consumer
|
5
|
+
include OptionsHelper
|
6
|
+
|
7
|
+
attr_reader :connection, :options
|
8
|
+
|
9
|
+
def initialize(connection, options = {})
|
10
|
+
@connection = connection
|
11
|
+
@options = deep_clone(options)
|
12
|
+
end
|
13
|
+
|
14
|
+
def load_object(object)
|
15
|
+
connection.serializer.load(object)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
begin
|
2
|
+
require "airbrake"
|
3
|
+
rescue LoadError
|
4
|
+
end
|
5
|
+
|
6
|
+
if defined?(Airbrake)
|
7
|
+
module MessageQueue
|
8
|
+
module ErrorHandlers
|
9
|
+
class Airbrake
|
10
|
+
def handle(message, consumer, ex)
|
11
|
+
params = message.attributes.merge(:pid => Process.pid, :consumer => consumer.inspect)
|
12
|
+
::Airbrake.notify_or_ignore(ex, :parameters => params)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
MessageQueue.register_error_handler MessageQueue::ErrorHandlers::Airbrake.new
|
19
|
+
end
|