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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1c9e0020e6933bdc25c5845a3bad630dc56b4ae9
4
- data.tar.gz: 55da31d069e4dd316260d37a0db00965a737039e
3
+ metadata.gz: ec35bbe29d804ef1930904b88277dc9bcd6722e7
4
+ data.tar.gz: 045d21384a3ed02bbe66c41a01f25a5cde723c1e
5
5
  SHA512:
6
- metadata.gz: 78b24b50ec3a7779572c1a45ce1b59d6ce345a058dda2c6781048b65a5312f210cb4595fc678450ea9a73dbf95951a902598def1e1bad3493071b4f903db1e5f
7
- data.tar.gz: 7ee8fd7a118ef27a164becdf4bab34b977ba31dada15040233aff1807c56648d6b0dd1d58ce691a0bf3f563304d0f94a7201ac1b14c625a86b318b2d56a978e5
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/node-freddy.svg?branch=master)](https://travis-ci.org/salemove/node-freddy)
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
@@ -8,7 +8,7 @@ Gem::Specification.new do |spec|
8
8
  else
9
9
  spec.name = "freddy"
10
10
  end
11
- spec.version = '0.4.6'
11
+ spec.version = '0.4.7'
12
12
  spec.authors = ["Urmas Talimaa"]
13
13
  spec.email = ["urmas.talimaa@gmail.com"]
14
14
  spec.description = %q{Messaging API}
@@ -48,7 +48,8 @@ class Freddy
48
48
 
49
49
  def subscribe(&block)
50
50
  @queue.subscribe do |info, properties, payload|
51
- block.call(payload, Delivery.new(properties, info.routing_key))
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
- block.call(payload, Delivery.new(meta, meta.routing_key))
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
 
@@ -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
- @topic_exchange = @channel.topic Freddy::FREDDY_TOPIC_EXCHANGE_NAME
15
- @consume_thread_pool = consume_thread_pool
16
- @dedicated_thread_pool = Thread.pool(1) # used only internally
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 dedicated_consume(queue, &block)
29
- consume_using_pool(queue, {}, @dedicated_thread_pool, &block)
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
- ResponderHandler.new consumer, @consume_thread_pool
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 log_receive_event(queue_name, payload, correlation_id)
62
- if defined?(Logasm) && @logger.is_a?(Logasm)
63
- @logger.debug "Received message", queue: queue_name, payload: payload, correlation_id: correlation_id
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
@@ -1,8 +1,9 @@
1
1
  class Freddy
2
2
  class Delivery
3
- attr_reader :metadata, :routing_key
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 :destination, :correlation_id
3
+ attr_reader :correlation_id
4
4
 
5
5
  def initialize(adapter, delivery)
6
6
  @adapter = adapter
7
- @metadata = delivery.metadata
8
- @correlation_id = @metadata.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(@metadata.reply_to, response)
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(@metadata.reply_to, error)
16
+ @adapter.error(@delivery.reply_to, error)
17
17
  end
18
18
  end
19
19
  end
@@ -23,7 +23,7 @@ class Freddy
23
23
 
24
24
  class OjAdapter
25
25
  def self.parse(payload)
26
- Oj.load(payload, symbol_keys: true)
26
+ Oj.strict_load(payload, symbol_keys: true)
27
27
  end
28
28
 
29
29
  def self.dump(payload)
@@ -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 create_response_queue
83
- @channel.queue("", exclusive: true)
84
- end
61
+ def handle_response(delivery)
62
+ correlation_id = delivery.correlation_id
85
63
 
86
- def handle_response(payload, delivery)
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
- @request_map.delete correlation_id
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
- if @listening_for_responses
112
- true
113
- else
114
- ensure_response_queue_exists
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.metadata.type == 'error'
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
- @request.respond_to destination, &callback
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.consume destination do
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(metadata: metadata) }
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
@@ -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.6
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-26 00:00:00.000000000 Z
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