freddy 0.3.7 → 0.4.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/Gemfile.lock +4 -2
- data/README.md +12 -2
- data/freddy.gemspec +2 -1
- data/lib/freddy.rb +6 -4
- data/lib/freddy/consumer.rb +21 -11
- data/lib/freddy/request.rb +3 -3
- data/spec/integration/concurrency_spec.rb +16 -14
- metadata +17 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 787721a42efa35a8721680b14d607b17f6b77e84
|
4
|
+
data.tar.gz: e12ccb2fa37c6fcd85bdb32abee295f862884c1f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 23aca1f868ea2871d1b2056d52a9f8264168c7ce7d8d2890ec8d2d51edcbe580aad35f908bd320aa8c6f4f41f05386613e28da71cd5bda4b3cb7586dea43d338
|
7
|
+
data.tar.gz: 18bc2c87a793eb9521112882a6f5353fb9fd467c9079bb5e44ac82696a19f138566bf4f880fef006bf48a0374460ece4c42c0af63a85673cc5fee1fc94ec67f8
|
data/Gemfile.lock
CHANGED
@@ -1,16 +1,17 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
freddy (0.
|
4
|
+
freddy (0.4.0)
|
5
5
|
bunny (= 1.6.3)
|
6
6
|
hamster (~> 1.0.1.pre.rc3)
|
7
7
|
symbolizer
|
8
|
+
thread (~> 0.2)
|
8
9
|
|
9
10
|
GEM
|
10
11
|
remote: https://rubygems.org/
|
11
12
|
specs:
|
12
13
|
amq-protocol (1.9.2)
|
13
|
-
atomic (1.1.
|
14
|
+
atomic (1.1.99)
|
14
15
|
bunny (1.6.3)
|
15
16
|
amq-protocol (>= 1.9.2)
|
16
17
|
coderay (1.1.0)
|
@@ -37,6 +38,7 @@ GEM
|
|
37
38
|
rspec-support (3.1.2)
|
38
39
|
slop (3.6.0)
|
39
40
|
symbolizer (0.0.1)
|
41
|
+
thread (0.2.2)
|
40
42
|
|
41
43
|
PLATFORMS
|
42
44
|
ruby
|
data/README.md
CHANGED
@@ -144,10 +144,20 @@ responder_handler.destroy_destination
|
|
144
144
|
## Notes about concurrency
|
145
145
|
|
146
146
|
The underlying bunny implementation uses 1 responder thread by default. This means that if there is a time-consuming process or a sleep call in a responder then other responders will not receive messages concurrently.
|
147
|
+
To resolve this problem *freddy* uses a thread pool for running concurrent responders.
|
148
|
+
The thread pool is shared between *tap_into* and *respond_to* callbacks and the default size is 4.
|
149
|
+
The thread pool size can be configured by passing the configuration option *max_concurrency*.
|
147
150
|
|
148
|
-
This is especially devious when using `deliver_with_response` in a responder because `deliver_with_response` creates a new anonymous responder which will not receive the response if the parent responder uses a sleep call.
|
149
151
|
|
150
|
-
|
152
|
+
Note that while it is possible to use *deliver_with_response* inside a *respond_to* block,
|
153
|
+
it is not possible to use another *respond_to* block inside a different *respond_to* block.
|
154
|
+
|
155
|
+
|
156
|
+
Note also that other configuration options for freddy users
|
157
|
+
such as pool sizes for DB connections need to match or exceed *max_concurrency*
|
158
|
+
to avoid running out of resources.
|
159
|
+
|
160
|
+
Read more from <http://rubybunny.info/articles/concurrency.html>.
|
151
161
|
|
152
162
|
## Credits
|
153
163
|
|
data/freddy.gemspec
CHANGED
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
6
|
spec.name = "freddy"
|
7
|
-
spec.version = '0.
|
7
|
+
spec.version = '0.4.0'
|
8
8
|
spec.authors = ["Urmas Talimaa"]
|
9
9
|
spec.email = ["urmas.talimaa@gmail.com"]
|
10
10
|
spec.description = %q{Messaging API}
|
@@ -22,4 +22,5 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.add_dependency "bunny", "1.6.3"
|
23
23
|
spec.add_dependency "symbolizer"
|
24
24
|
spec.add_dependency "hamster", "~> 1.0.1.pre.rc3"
|
25
|
+
spec.add_dependency "thread", "~> 0.2"
|
25
26
|
end
|
data/lib/freddy.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'bunny'
|
2
2
|
require 'json'
|
3
3
|
require 'symbolizer'
|
4
|
+
require 'thread/pool'
|
4
5
|
|
5
6
|
require_relative 'freddy/consumer'
|
6
7
|
require_relative 'freddy/producer'
|
@@ -68,16 +69,17 @@ class Freddy
|
|
68
69
|
bunny.start
|
69
70
|
|
70
71
|
channel = bunny.create_channel
|
71
|
-
new(channel, logger)
|
72
|
+
new(channel, logger, bunny_config.fetch(:max_concurrency, 4))
|
72
73
|
end
|
73
74
|
|
74
75
|
attr_reader :channel, :consumer, :producer, :request
|
75
76
|
|
76
|
-
def initialize(channel, logger)
|
77
|
+
def initialize(channel, logger, max_concurrency)
|
77
78
|
@channel = channel
|
78
|
-
@
|
79
|
+
@consume_thread_pool = Thread.pool(max_concurrency)
|
80
|
+
@consumer = Consumer.new channel, logger, @consume_thread_pool
|
79
81
|
@producer = Producer.new channel, logger
|
80
|
-
@request = Request.new channel, logger
|
82
|
+
@request = Request.new channel, logger, @producer, @consumer
|
81
83
|
end
|
82
84
|
|
83
85
|
def respond_to(destination, &callback)
|
data/lib/freddy/consumer.rb
CHANGED
@@ -9,9 +9,11 @@ class Freddy
|
|
9
9
|
class EmptyConsumer < Exception
|
10
10
|
end
|
11
11
|
|
12
|
-
def initialize(channel, logger)
|
12
|
+
def initialize(channel, logger, consume_thread_pool)
|
13
13
|
@channel, @logger = channel, logger
|
14
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
|
15
17
|
end
|
16
18
|
|
17
19
|
def consume(destination, options = {}, &block)
|
@@ -20,21 +22,17 @@ class Freddy
|
|
20
22
|
end
|
21
23
|
|
22
24
|
def consume_from_queue(queue, options = {}, &block)
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
end
|
29
|
-
end
|
30
|
-
@logger.debug "Consuming messages on #{queue.name}"
|
31
|
-
ResponderHandler.new consumer, @channel
|
25
|
+
consume_using_pool(queue, options, @consume_thread_pool, &block)
|
26
|
+
end
|
27
|
+
|
28
|
+
def dedicated_consume(queue, &block)
|
29
|
+
consume_using_pool(queue, {}, @dedicated_thread_pool, &block)
|
32
30
|
end
|
33
31
|
|
34
32
|
def tap_into(pattern, &block)
|
35
33
|
queue = @channel.queue("", exclusive: true).bind(@topic_exchange, routing_key: pattern)
|
36
34
|
consumer = queue.subscribe do |delivery_info, properties, payload|
|
37
|
-
|
35
|
+
@consume_thread_pool.process do
|
38
36
|
block.call parse_payload(payload), delivery_info.routing_key
|
39
37
|
end
|
40
38
|
end
|
@@ -44,6 +42,18 @@ class Freddy
|
|
44
42
|
|
45
43
|
private
|
46
44
|
|
45
|
+
def consume_using_pool(queue, options, pool, &block)
|
46
|
+
consumer = queue.subscribe options do |delivery_info, properties, payload|
|
47
|
+
pool.process do
|
48
|
+
parsed_payload = parse_payload(payload)
|
49
|
+
log_receive_event(queue.name, parsed_payload)
|
50
|
+
block.call parsed_payload, Delivery.new(delivery_info, properties)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
@logger.debug "Consuming messages on #{queue.name}"
|
54
|
+
ResponderHandler.new consumer, @channel
|
55
|
+
end
|
56
|
+
|
47
57
|
def parse_payload(payload)
|
48
58
|
if payload == 'null'
|
49
59
|
{}
|
data/lib/freddy/request.rb
CHANGED
@@ -17,9 +17,9 @@ class Freddy
|
|
17
17
|
class EmptyResponder < Exception
|
18
18
|
end
|
19
19
|
|
20
|
-
def initialize(channel, logger)
|
20
|
+
def initialize(channel, logger, producer, consumer)
|
21
21
|
@channel, @logger = channel, logger
|
22
|
-
@producer, @consumer =
|
22
|
+
@producer, @consumer = producer, consumer
|
23
23
|
@request_map = Hamster.mutable_hash
|
24
24
|
@request_manager = RequestManager.new @request_map, @logger
|
25
25
|
|
@@ -115,7 +115,7 @@ class Freddy
|
|
115
115
|
else
|
116
116
|
ensure_response_queue_exists
|
117
117
|
@request_manager.start
|
118
|
-
@consumer.
|
118
|
+
@consumer.dedicated_consume @response_queue do |payload, delivery|
|
119
119
|
handle_response payload, delivery
|
120
120
|
end
|
121
121
|
@listening_for_responses = true
|
@@ -1,34 +1,36 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe 'Concurrency' do
|
4
|
-
let(:
|
4
|
+
let(:freddy1) { Freddy.build(logger, config) }
|
5
|
+
let(:freddy2) { Freddy.build(logger, config) }
|
6
|
+
let(:freddy3) { Freddy.build(logger, config) }
|
5
7
|
|
6
|
-
it 'supports
|
7
|
-
|
8
|
+
it 'supports multiple requests in #respond_to' do
|
9
|
+
freddy1.respond_to 'Concurrency1' do |payload, msg_handler|
|
8
10
|
begin
|
9
|
-
|
10
|
-
|
11
|
+
freddy1.deliver_with_response 'Concurrency2', msg: 'noop'
|
12
|
+
result2 = freddy1.deliver_with_response 'Concurrency3', msg: 'noop'
|
13
|
+
msg_handler.success(result2)
|
11
14
|
rescue Freddy::InvalidRequestError => e
|
12
15
|
msg_handler.error(e.response)
|
13
16
|
end
|
14
17
|
end
|
15
18
|
|
16
|
-
|
19
|
+
freddy2.respond_to 'Concurrency2' do |payload, msg_handler|
|
17
20
|
begin
|
18
|
-
|
19
|
-
msg_handler.success(result)
|
21
|
+
msg_handler.success({from: 'Concurrency2'})
|
20
22
|
rescue Freddy::InvalidRequestError => e
|
21
23
|
msg_handler.error(e.response)
|
22
24
|
end
|
23
25
|
end
|
24
26
|
|
25
|
-
|
27
|
+
freddy3.respond_to 'Concurrency3' do |payload, msg_handler|
|
26
28
|
msg_handler.success({from: 'Concurrency3'})
|
27
29
|
end
|
28
30
|
|
29
31
|
result =
|
30
32
|
begin
|
31
|
-
|
33
|
+
freddy1.deliver_with_response 'Concurrency1', msg: 'noop'
|
32
34
|
rescue Freddy::InvalidRequestError => e
|
33
35
|
e.response
|
34
36
|
end
|
@@ -40,18 +42,18 @@ describe 'Concurrency' do
|
|
40
42
|
received1 = false
|
41
43
|
received2 = false
|
42
44
|
|
43
|
-
|
44
|
-
result =
|
45
|
+
freddy1.tap_into 'concurrency.*.queue1' do
|
46
|
+
result = freddy1.deliver_with_response 'TapConcurrency', msg: 'noop'
|
45
47
|
expect(result).to eq(from: 'TapConcurrency')
|
46
48
|
received1 = true
|
47
49
|
end
|
48
50
|
|
49
|
-
|
51
|
+
freddy2.respond_to 'TapConcurrency' do |payload, msg_handler|
|
50
52
|
msg_handler.success({from: 'TapConcurrency'})
|
51
53
|
received2 = true
|
52
54
|
end
|
53
55
|
|
54
|
-
|
56
|
+
freddy1.deliver 'concurrency.q.queue1', msg: 'noop'
|
55
57
|
|
56
58
|
wait_for { received1 && received2 }
|
57
59
|
|
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
|
+
version: 0.4.0
|
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-08-
|
11
|
+
date: 2015-08-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -80,6 +80,20 @@ dependencies:
|
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: 1.0.1.pre.rc3
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: thread
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0.2'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0.2'
|
83
97
|
description: Messaging API
|
84
98
|
email:
|
85
99
|
- urmas.talimaa@gmail.com
|
@@ -138,7 +152,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
138
152
|
version: '0'
|
139
153
|
requirements: []
|
140
154
|
rubyforge_project:
|
141
|
-
rubygems_version: 2.
|
155
|
+
rubygems_version: 2.4.8
|
142
156
|
signing_key:
|
143
157
|
specification_version: 4
|
144
158
|
summary: API for inter-application messaging supporting acknowledgements and request-response
|