message_queue 0.0.4 → 0.1.0
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 +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
|