freddy 0.4.6 → 0.4.7
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
[![Build Status](https://travis-ci.org/salemove/
|
3
|
+
[![Build Status](https://travis-ci.org/salemove/freddy.svg?branch=master)](https://travis-ci.org/salemove/freddy)
|
4
4
|
[![Code Climate](https://codeclimate.com/github/salemove/freddy/badges/gpa.svg)](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
|