freddy 0.4.6 → 0.4.7
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 +1 -1
- data/freddy.gemspec +1 -1
- data/lib/freddy/adapters/bunny_adapter.rb +2 -1
- data/lib/freddy/adapters/march_hare_adapter.rb +2 -1
- data/lib/freddy/consumer.rb +14 -51
- data/lib/freddy/consumers/respond_to_consumer.rb +59 -0
- data/lib/freddy/consumers/response_consumer.rb +34 -0
- data/lib/freddy/consumers/tap_into_consumer.rb +33 -0
- data/lib/freddy/delivery.rb +11 -2
- data/lib/freddy/message_handler.rb +5 -5
- data/lib/freddy/payload.rb +1 -1
- data/lib/freddy/request.rb +10 -46
- data/lib/freddy/sync_response_container.rb +1 -1
- data/lib/freddy.rb +2 -2
- data/spec/freddy/consumer_spec.rb +1 -5
- data/spec/freddy/message_handler_spec.rb +1 -3
- data/spec/freddy/request_spec.rb +0 -4
- data/spec/integration/logging_spec.rb +0 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ec35bbe29d804ef1930904b88277dc9bcd6722e7
|
4
|
+
data.tar.gz: 045d21384a3ed02bbe66c41a01f25a5cde723c1e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cc2ec82ebe76f48b5e78ac3a695d71d16d49ca42bc08762c4b8c2b35c9c66a86c48fb8e743601bfb31c3c580e4906404f60122fa44e0269de51c4c9485af43d9
|
7
|
+
data.tar.gz: fdb43cf87cfdd043f785b827957b9309562a79637af82e9dccbacf62cc62f6f622e1b5b28019c55dda586fdcd2cc3803cbe101b7b81780267f3a6ae7addeddde
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Messaging API supporting acknowledgements and request-response
|
2
2
|
|
3
|
-
[](https://travis-ci.org/salemove/freddy)
|
4
4
|
[](https://codeclimate.com/github/salemove/freddy)
|
5
5
|
|
6
6
|
## Setup
|
data/freddy.gemspec
CHANGED
@@ -48,7 +48,8 @@ class Freddy
|
|
48
48
|
|
49
49
|
def subscribe(&block)
|
50
50
|
@queue.subscribe do |info, properties, payload|
|
51
|
-
|
51
|
+
parsed_payload = Payload.parse(payload)
|
52
|
+
block.call(Delivery.new(parsed_payload, properties, info.routing_key))
|
52
53
|
end
|
53
54
|
end
|
54
55
|
|
@@ -49,7 +49,8 @@ class Freddy
|
|
49
49
|
|
50
50
|
def subscribe(&block)
|
51
51
|
@queue.subscribe do |meta, payload|
|
52
|
-
|
52
|
+
parsed_payload = Payload.parse(payload)
|
53
|
+
block.call(Delivery.new(parsed_payload, meta, meta.routing_key))
|
53
54
|
end
|
54
55
|
end
|
55
56
|
|
data/lib/freddy/consumer.rb
CHANGED
@@ -1,69 +1,32 @@
|
|
1
1
|
require_relative 'responder_handler'
|
2
2
|
require_relative 'message_handler'
|
3
|
-
require_relative 'request'
|
4
3
|
require_relative 'delivery'
|
4
|
+
require_relative 'consumers/tap_into_consumer'
|
5
|
+
require_relative 'consumers/respond_to_consumer'
|
6
|
+
require_relative 'consumers/response_consumer'
|
5
7
|
|
6
8
|
class Freddy
|
7
9
|
class Consumer
|
8
|
-
|
9
|
-
class EmptyConsumer < Exception
|
10
|
-
end
|
11
|
-
|
12
|
-
def initialize(channel, logger, consume_thread_pool)
|
10
|
+
def initialize(channel, logger, consume_thread_pool, producer)
|
13
11
|
@channel, @logger = channel, logger
|
14
|
-
@
|
15
|
-
@
|
16
|
-
@
|
17
|
-
end
|
18
|
-
|
19
|
-
def consume(destination, options = {}, &block)
|
20
|
-
raise EmptyConsumer unless block
|
21
|
-
consume_from_queue create_queue(destination), options, &block
|
22
|
-
end
|
23
|
-
|
24
|
-
def consume_from_queue(queue, options = {}, &block)
|
25
|
-
consume_using_pool(queue, options, @consume_thread_pool, &block)
|
12
|
+
@tap_into_consumer = Consumers::TapIntoConsumer.new(consume_thread_pool, channel)
|
13
|
+
@respond_to_consumer = Consumers::RespondToConsumer.new(consume_thread_pool, channel, producer, @logger)
|
14
|
+
@response_consumer = Consumers::ResponseConsumer.new(@logger)
|
26
15
|
end
|
27
16
|
|
28
|
-
def
|
29
|
-
|
17
|
+
def response_consume(queue, &block)
|
18
|
+
@logger.debug "Consuming messages on #{queue.name}"
|
19
|
+
@response_consumer.consume(queue, &block)
|
30
20
|
end
|
31
21
|
|
32
22
|
def tap_into(pattern, &block)
|
33
|
-
queue = create_queue('', exclusive: true).bind(@topic_exchange, routing_key: pattern)
|
34
|
-
consumer = queue.subscribe do |payload, delivery|
|
35
|
-
@consume_thread_pool.process do
|
36
|
-
block.call Payload.parse(payload), delivery.routing_key
|
37
|
-
end
|
38
|
-
end
|
39
23
|
@logger.debug "Tapping into messages that match #{pattern}"
|
40
|
-
|
41
|
-
end
|
42
|
-
|
43
|
-
private
|
44
|
-
|
45
|
-
def consume_using_pool(queue, options, pool, &block)
|
46
|
-
consumer = queue.subscribe do |payload, delivery|
|
47
|
-
pool.process do
|
48
|
-
parsed_payload = Payload.parse(payload)
|
49
|
-
log_receive_event(queue.name, parsed_payload, delivery.correlation_id)
|
50
|
-
block.call parsed_payload, delivery
|
51
|
-
end
|
52
|
-
end
|
53
|
-
@logger.debug "Consuming messages on #{queue.name}"
|
54
|
-
ResponderHandler.new consumer, pool
|
55
|
-
end
|
56
|
-
|
57
|
-
def create_queue(destination, options={})
|
58
|
-
@channel.queue(destination, options)
|
24
|
+
@tap_into_consumer.consume(pattern, &block)
|
59
25
|
end
|
60
26
|
|
61
|
-
def
|
62
|
-
|
63
|
-
|
64
|
-
else
|
65
|
-
@logger.debug "Received message on #{queue_name} with payload #{payload} with correlation_id #{correlation_id}"
|
66
|
-
end
|
27
|
+
def respond_to(destination, &block)
|
28
|
+
@logger.info "Listening for requests on #{destination}"
|
29
|
+
@respond_to_consumer.consume(destination, &block)
|
67
30
|
end
|
68
31
|
end
|
69
32
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
class Freddy
|
2
|
+
module Consumers
|
3
|
+
class RespondToConsumer
|
4
|
+
def initialize(consume_thread_pool, channel, producer, logger)
|
5
|
+
@consume_thread_pool = consume_thread_pool
|
6
|
+
@channel = channel
|
7
|
+
@producer = producer
|
8
|
+
@logger = logger
|
9
|
+
@response_queue_lock = Mutex.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def consume(destination, &block)
|
13
|
+
ensure_response_queue_exists
|
14
|
+
|
15
|
+
consumer = consume_from_destination(destination) do |delivery|
|
16
|
+
log_receive_event(destination, delivery)
|
17
|
+
|
18
|
+
handler_class = MessageHandlers.for_type(delivery.type)
|
19
|
+
handler = handler_class.new(@producer, destination, @logger)
|
20
|
+
|
21
|
+
msg_handler = MessageHandler.new(handler, delivery)
|
22
|
+
handler.handle_message delivery.payload, msg_handler, &block
|
23
|
+
end
|
24
|
+
|
25
|
+
ResponderHandler.new(consumer, @consume_thread_pool)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def consume_from_destination(destination, &block)
|
31
|
+
@channel.queue(destination).subscribe do |delivery|
|
32
|
+
process_message(delivery, &block)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def process_message(delivery, &block)
|
37
|
+
@consume_thread_pool.process do
|
38
|
+
block.call(delivery)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def log_receive_event(destination, delivery)
|
43
|
+
if defined?(Logasm) && @logger.is_a?(Logasm)
|
44
|
+
@logger.debug "Received message", queue: destination, payload: delivery.payload, correlation_id: delivery.correlation_id
|
45
|
+
else
|
46
|
+
@logger.debug "Received message on #{destination} with payload #{delivery.payload} with correlation_id #{delivery.correlation_id}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def ensure_response_queue_exists
|
51
|
+
return @response_queue if defined?(@response_queue)
|
52
|
+
|
53
|
+
@response_queue_lock.synchronize do
|
54
|
+
@response_queue = @channel.queue('', exclusive: true)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class Freddy
|
2
|
+
module Consumers
|
3
|
+
class ResponseConsumer
|
4
|
+
def initialize(logger)
|
5
|
+
@logger = logger
|
6
|
+
@dedicated_thread_pool = Thread.pool(1)
|
7
|
+
end
|
8
|
+
|
9
|
+
def consume(queue, &block)
|
10
|
+
consumer = queue.subscribe do |delivery|
|
11
|
+
process_message(queue, delivery, &block)
|
12
|
+
end
|
13
|
+
ResponderHandler.new(consumer, @dedicated_thread_pool)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def process_message(queue, delivery, &block)
|
19
|
+
@dedicated_thread_pool.process do
|
20
|
+
log_receive_event(queue.name, delivery)
|
21
|
+
block.call(delivery)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def log_receive_event(queue_name, delivery)
|
26
|
+
if defined?(Logasm) && @logger.is_a?(Logasm)
|
27
|
+
@logger.debug "Received message", queue: queue_name, payload: delivery.payload, correlation_id: delivery.correlation_id
|
28
|
+
else
|
29
|
+
@logger.debug "Received message on #{queue_name} with payload #{delivery.payload} with correlation_id #{delivery.correlation_id}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class Freddy
|
2
|
+
module Consumers
|
3
|
+
class TapIntoConsumer
|
4
|
+
def initialize(consume_thread_pool, channel)
|
5
|
+
@consume_thread_pool = consume_thread_pool
|
6
|
+
@channel = channel
|
7
|
+
@topic_exchange = @channel.topic(Freddy::FREDDY_TOPIC_EXCHANGE_NAME)
|
8
|
+
end
|
9
|
+
|
10
|
+
def consume(pattern, &block)
|
11
|
+
consumer = create_queue(pattern).subscribe do |delivery|
|
12
|
+
process_message(delivery, &block)
|
13
|
+
end
|
14
|
+
|
15
|
+
ResponderHandler.new(consumer, @consume_thread_pool)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def create_queue(pattern)
|
21
|
+
@channel
|
22
|
+
.queue('', exclusive: true)
|
23
|
+
.bind(@topic_exchange, routing_key: pattern)
|
24
|
+
end
|
25
|
+
|
26
|
+
def process_message(delivery, &block)
|
27
|
+
@consume_thread_pool.process do
|
28
|
+
block.call delivery.payload, delivery.routing_key
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/freddy/delivery.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
class Freddy
|
2
2
|
class Delivery
|
3
|
-
attr_reader :
|
3
|
+
attr_reader :routing_key, :payload
|
4
4
|
|
5
|
-
def initialize(metadata, routing_key)
|
5
|
+
def initialize(payload, metadata, routing_key)
|
6
|
+
@payload = payload
|
6
7
|
@metadata = metadata
|
7
8
|
@routing_key = routing_key
|
8
9
|
end
|
@@ -10,5 +11,13 @@ class Freddy
|
|
10
11
|
def correlation_id
|
11
12
|
@metadata.correlation_id
|
12
13
|
end
|
14
|
+
|
15
|
+
def type
|
16
|
+
@metadata.type
|
17
|
+
end
|
18
|
+
|
19
|
+
def reply_to
|
20
|
+
@metadata.reply_to
|
21
|
+
end
|
13
22
|
end
|
14
23
|
end
|
@@ -1,19 +1,19 @@
|
|
1
1
|
class Freddy
|
2
2
|
class MessageHandler
|
3
|
-
attr_reader :
|
3
|
+
attr_reader :correlation_id
|
4
4
|
|
5
5
|
def initialize(adapter, delivery)
|
6
6
|
@adapter = adapter
|
7
|
-
@
|
8
|
-
@correlation_id = @
|
7
|
+
@delivery = delivery
|
8
|
+
@correlation_id = @delivery.correlation_id
|
9
9
|
end
|
10
10
|
|
11
11
|
def success(response = nil)
|
12
|
-
@adapter.success(@
|
12
|
+
@adapter.success(@delivery.reply_to, response)
|
13
13
|
end
|
14
14
|
|
15
15
|
def error(error = {error: "Couldn't process message"})
|
16
|
-
@adapter.error(@
|
16
|
+
@adapter.error(@delivery.reply_to, error)
|
17
17
|
end
|
18
18
|
end
|
19
19
|
end
|
data/lib/freddy/payload.rb
CHANGED
data/lib/freddy/request.rb
CHANGED
@@ -11,12 +11,6 @@ class Freddy
|
|
11
11
|
class Request
|
12
12
|
NO_ROUTE = 312
|
13
13
|
|
14
|
-
class EmptyRequest < Exception
|
15
|
-
end
|
16
|
-
|
17
|
-
class EmptyResponder < Exception
|
18
|
-
end
|
19
|
-
|
20
14
|
def initialize(channel, logger, producer, consumer)
|
21
15
|
@channel, @logger = channel, logger
|
22
16
|
@producer, @consumer = producer, consumer
|
@@ -30,7 +24,6 @@ class Freddy
|
|
30
24
|
end
|
31
25
|
|
32
26
|
@listening_for_responses_lock = Mutex.new
|
33
|
-
@response_queue_lock = Mutex.new
|
34
27
|
end
|
35
28
|
|
36
29
|
def sync_request(destination, payload, opts)
|
@@ -63,33 +56,14 @@ class Freddy
|
|
63
56
|
)
|
64
57
|
end
|
65
58
|
|
66
|
-
def respond_to(destination, &block)
|
67
|
-
raise EmptyResponder unless block
|
68
|
-
|
69
|
-
ensure_response_queue_exists
|
70
|
-
@logger.info "Listening for requests on #{destination}"
|
71
|
-
responder_handler = @consumer.consume destination do |payload, delivery|
|
72
|
-
handler = MessageHandlers.for_type(delivery.metadata.type).new(@producer, destination, @logger)
|
73
|
-
|
74
|
-
msg_handler = MessageHandler.new(handler, delivery)
|
75
|
-
handler.handle_message payload, msg_handler, &block
|
76
|
-
end
|
77
|
-
responder_handler
|
78
|
-
end
|
79
|
-
|
80
59
|
private
|
81
60
|
|
82
|
-
def
|
83
|
-
|
84
|
-
end
|
61
|
+
def handle_response(delivery)
|
62
|
+
correlation_id = delivery.correlation_id
|
85
63
|
|
86
|
-
|
87
|
-
correlation_id = delivery.metadata.correlation_id
|
88
|
-
request = @request_map[correlation_id]
|
89
|
-
if request
|
64
|
+
if request = @request_map.delete(correlation_id)
|
90
65
|
@logger.debug "Got response for request to #{request[:destination]} with correlation_id #{correlation_id}"
|
91
|
-
|
92
|
-
request[:callback].call payload, delivery
|
66
|
+
request[:callback].call delivery.payload, delivery
|
93
67
|
else
|
94
68
|
@logger.warn "Got rpc response for correlation_id #{correlation_id} but there is no requester"
|
95
69
|
Utils.notify 'NoRequesterForResponse', "Got rpc response but there is no requester", correlation_id: correlation_id
|
@@ -100,24 +74,14 @@ class Freddy
|
|
100
74
|
Utils.notify_exception(e, destination: request[:destination], correlation_id: correlation_id)
|
101
75
|
end
|
102
76
|
|
103
|
-
def ensure_response_queue_exists
|
104
|
-
@response_queue_lock.synchronize do
|
105
|
-
@response_queue ||= create_response_queue
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
77
|
def ensure_listening_to_responses
|
78
|
+
return @listening_for_responses if defined?(@listening_for_responses)
|
79
|
+
|
110
80
|
@listening_for_responses_lock.synchronize do
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
@request_manager.start
|
116
|
-
@consumer.dedicated_consume @response_queue do |payload, delivery|
|
117
|
-
handle_response payload, delivery
|
118
|
-
end
|
119
|
-
@listening_for_responses = true
|
120
|
-
end
|
81
|
+
@response_queue ||= @channel.queue("", exclusive: true)
|
82
|
+
@request_manager.start
|
83
|
+
@consumer.response_consume(@response_queue, &method(:handle_response))
|
84
|
+
@listening_for_responses = true
|
121
85
|
end
|
122
86
|
end
|
123
87
|
end
|
@@ -23,7 +23,7 @@ class Freddy
|
|
23
23
|
raise Timeout::Error, 'execution expired'
|
24
24
|
elsif @response[:error] == 'RequestTimeout'
|
25
25
|
raise TimeoutError.new(@response)
|
26
|
-
elsif !@delivery || @delivery.
|
26
|
+
elsif !@delivery || @delivery.type == 'error'
|
27
27
|
raise InvalidRequestError.new(@response)
|
28
28
|
else
|
29
29
|
@response
|
data/lib/freddy.rb
CHANGED
@@ -41,14 +41,14 @@ class Freddy
|
|
41
41
|
@connection = connection
|
42
42
|
@channel = connection.create_channel
|
43
43
|
@consume_thread_pool = Thread.pool(max_concurrency)
|
44
|
-
@consumer = Consumer.new channel, logger, @consume_thread_pool
|
45
44
|
@producer = Producer.new channel, logger
|
45
|
+
@consumer = Consumer.new channel, logger, @consume_thread_pool, @producer
|
46
46
|
@request = Request.new channel, logger, @producer, @consumer
|
47
47
|
end
|
48
48
|
private :initialize
|
49
49
|
|
50
50
|
def respond_to(destination, &callback)
|
51
|
-
@
|
51
|
+
@consumer.respond_to destination, &callback
|
52
52
|
end
|
53
53
|
|
54
54
|
def tap_into(pattern, &callback)
|
@@ -10,12 +10,8 @@ describe Freddy::Consumer do
|
|
10
10
|
|
11
11
|
after { freddy.close }
|
12
12
|
|
13
|
-
it 'raises exception when no consumer is provided' do
|
14
|
-
expect { consumer.consume destination }.to raise_error described_class::EmptyConsumer
|
15
|
-
end
|
16
|
-
|
17
13
|
it "doesn't call passed block without any messages" do
|
18
|
-
consumer.
|
14
|
+
consumer.respond_to destination do
|
19
15
|
@message_received = true
|
20
16
|
end
|
21
17
|
default_sleep
|
@@ -4,9 +4,7 @@ describe Freddy::MessageHandler do
|
|
4
4
|
subject(:handler) { described_class.new(adapter, delivery) }
|
5
5
|
|
6
6
|
let(:adapter) { double }
|
7
|
-
let(:delivery) { double(
|
8
|
-
let(:metadata) { double(reply_to: reply_to, correlation_id: 'abc') }
|
9
|
-
|
7
|
+
let(:delivery) { double(reply_to: reply_to, correlation_id: 'abc') }
|
10
8
|
let(:reply_to) { double }
|
11
9
|
|
12
10
|
describe '#success' do
|
data/spec/freddy/request_spec.rb
CHANGED
@@ -10,10 +10,6 @@ describe Freddy::Request do
|
|
10
10
|
|
11
11
|
after { freddy.close }
|
12
12
|
|
13
|
-
it 'raises empty responder exception when responding without callback' do
|
14
|
-
expect {@responder = request.respond_to destination }.to raise_error described_class::EmptyResponder
|
15
|
-
end
|
16
|
-
|
17
13
|
context 'requesting from multiple threads' do
|
18
14
|
let(:nr_of_threads) { 50 }
|
19
15
|
|
@@ -23,7 +23,6 @@ describe 'Logging' do
|
|
23
23
|
|
24
24
|
it 'logs all consumed messages' do
|
25
25
|
expect(logger1).to have_received(:info).with(/Listening for requests on \S+/)
|
26
|
-
expect(logger1).to have_received(:debug).with(/Consuming messages on \S+/)
|
27
26
|
expect(logger1).to have_received(:debug).with(/Received message on \S+ with payload {:pay=>"load"}/)
|
28
27
|
end
|
29
28
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: freddy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Urmas Talimaa
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-12-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -117,6 +117,9 @@ files:
|
|
117
117
|
- lib/freddy/adapters/bunny_adapter.rb
|
118
118
|
- lib/freddy/adapters/march_hare_adapter.rb
|
119
119
|
- lib/freddy/consumer.rb
|
120
|
+
- lib/freddy/consumers/respond_to_consumer.rb
|
121
|
+
- lib/freddy/consumers/response_consumer.rb
|
122
|
+
- lib/freddy/consumers/tap_into_consumer.rb
|
120
123
|
- lib/freddy/delivery.rb
|
121
124
|
- lib/freddy/error_response.rb
|
122
125
|
- lib/freddy/invalid_request_error.rb
|