action_subscriber 2.5.0.pre2 → 3.0.0.pre1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/action_subscriber.gemspec +1 -0
- data/lib/action_subscriber/babou.rb +2 -29
- data/lib/action_subscriber/base.rb +0 -4
- data/lib/action_subscriber/bunny/subscriber.rb +14 -4
- data/lib/action_subscriber/configuration.rb +1 -11
- data/lib/action_subscriber/default_routing.rb +6 -4
- data/lib/action_subscriber/march_hare/subscriber.rb +14 -4
- data/lib/action_subscriber/message_retry.rb +1 -1
- data/lib/action_subscriber/middleware/env.rb +3 -1
- data/lib/action_subscriber/middleware/error_handler.rb +11 -4
- data/lib/action_subscriber/rabbit_connection.rb +23 -34
- data/lib/action_subscriber/route.rb +5 -1
- data/lib/action_subscriber/route_set.rb +12 -11
- data/lib/action_subscriber/router.rb +13 -2
- data/lib/action_subscriber/version.rb +1 -1
- data/lib/action_subscriber.rb +11 -24
- data/spec/integration/around_filters_spec.rb +1 -1
- data/spec/integration/at_least_once_spec.rb +1 -1
- data/spec/integration/at_most_once_spec.rb +1 -1
- data/spec/integration/automatic_reconnect_spec.rb +3 -4
- data/spec/integration/basic_subscriber_spec.rb +2 -2
- data/spec/integration/custom_actions_spec.rb +1 -1
- data/spec/integration/custom_headers_spec.rb +2 -2
- data/spec/integration/decoding_payloads_spec.rb +2 -2
- data/spec/integration/manual_acknowledgement_spec.rb +1 -1
- data/spec/integration/multiple_connections_spec.rb +36 -0
- data/spec/integration/multiple_threadpools_spec.rb +3 -3
- data/spec/lib/action_subscriber/configuration_spec.rb +1 -5
- data/spec/spec_helper.rb +7 -4
- metadata +18 -14
- data/lib/action_subscriber/publisher/async/in_memory_adapter.rb +0 -153
- data/lib/action_subscriber/publisher/async.rb +0 -31
- data/lib/action_subscriber/publisher.rb +0 -46
- data/lib/action_subscriber/synchronizer.rb +0 -15
- data/spec/integration/inferred_routes_spec.rb +0 -53
- data/spec/lib/action_subscriber/publisher/async/in_memory_adapter_spec.rb +0 -135
- data/spec/lib/action_subscriber/publisher/async_spec.rb +0 -40
- data/spec/lib/action_subscriber/publisher_spec.rb +0 -35
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b0a848297f91f557f43e6240545866aaad495d17
|
4
|
+
data.tar.gz: 0fd512e453740a0ad0e34f1f42ddef7ab5ac240f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6d30b59daffa7e89a4c48eb3044a65af90804d91b3c0991e3dd0899b9a20944532878bc6327684577d05c11b9017edfb233bbd251daa42dff9a50d614f4701da
|
7
|
+
data.tar.gz: df4cf38fe9cbfed26adcc53e43823348726176cc387ccaa46d22274099182198aa1c2d1fcd1c7e341d87dd6e4f1a88e24702788a41c9955e8a2161a880459d56
|
data/action_subscriber.gemspec
CHANGED
@@ -30,6 +30,7 @@ Gem::Specification.new do |spec|
|
|
30
30
|
spec.add_dependency 'middleware'
|
31
31
|
spec.add_dependency 'thor'
|
32
32
|
|
33
|
+
spec.add_development_dependency "active_publisher", "~> 0.1.5"
|
33
34
|
spec.add_development_dependency "activerecord", ">= 3.2"
|
34
35
|
spec.add_development_dependency "bundler", ">= 1.6"
|
35
36
|
spec.add_development_dependency "pry-nav"
|
@@ -7,7 +7,7 @@ module ActionSubscriber
|
|
7
7
|
def self.auto_pop!
|
8
8
|
@pop_mode = true
|
9
9
|
reload_active_record
|
10
|
-
|
10
|
+
::ActionSubscriber.setup_default_connection!
|
11
11
|
sleep_time = ::ActionSubscriber.configuration.pop_interval.to_i / 1000.0
|
12
12
|
|
13
13
|
::ActionSubscriber.start_queues
|
@@ -30,7 +30,7 @@ module ActionSubscriber
|
|
30
30
|
def self.start_subscribers
|
31
31
|
@prowl_mode = true
|
32
32
|
reload_active_record
|
33
|
-
|
33
|
+
::ActionSubscriber.setup_default_connection!
|
34
34
|
|
35
35
|
::ActionSubscriber.start_subscribers
|
36
36
|
logger.info "Action Subscriber connected"
|
@@ -45,29 +45,6 @@ module ActionSubscriber
|
|
45
45
|
!!@prowl_mode
|
46
46
|
end
|
47
47
|
|
48
|
-
def self.load_subscribers
|
49
|
-
subscription_paths = ["subscriptions", "subscribers"]
|
50
|
-
path_prefixes = ["lib", "app"]
|
51
|
-
cloned_paths = subscription_paths.dup
|
52
|
-
|
53
|
-
path_prefixes.each do |prefix|
|
54
|
-
cloned_paths.each { |path| subscription_paths << "#{prefix}/#{path}" }
|
55
|
-
end
|
56
|
-
|
57
|
-
absolute_subscription_paths = subscription_paths.map{ |path| ::File.expand_path(path) }
|
58
|
-
absolute_subscription_paths.each do |path|
|
59
|
-
if ::File.exists?("#{path}.rb")
|
60
|
-
load("#{path}.rb")
|
61
|
-
end
|
62
|
-
|
63
|
-
if ::File.directory?(path)
|
64
|
-
::Dir[::File.join(path, "**", "*.rb")].sort.each do |file|
|
65
|
-
load file
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
48
|
def self.logger
|
72
49
|
::ActionSubscriber::Logging.logger
|
73
50
|
end
|
@@ -108,9 +85,5 @@ module ActionSubscriber
|
|
108
85
|
|
109
86
|
puts "threadpool empty. Shutting down"
|
110
87
|
end
|
111
|
-
|
112
|
-
def self.subscribers_loaded?
|
113
|
-
!::ActionSubscriber::Base.inherited_classes.empty?
|
114
|
-
end
|
115
88
|
end
|
116
89
|
end
|
@@ -20,7 +20,9 @@ module ActionSubscriber
|
|
20
20
|
# of times we will pop each time we poll the broker
|
21
21
|
times_to_pop = [::ActionSubscriber::Threadpool.ready_size, ::ActionSubscriber.config.times_to_pop].min
|
22
22
|
times_to_pop.times do
|
23
|
-
|
23
|
+
subscriptions.each do |subscription|
|
24
|
+
route = subscription[:route]
|
25
|
+
queue = subscription[:queue]
|
24
26
|
# Handle busy checks on a per threadpool basis
|
25
27
|
next if route.threadpool.busy?
|
26
28
|
|
@@ -29,7 +31,6 @@ module ActionSubscriber
|
|
29
31
|
::ActiveSupport::Notifications.instrument "popped_event.action_subscriber", :payload_size => encoded_payload.bytesize, :queue => queue.name
|
30
32
|
properties = {
|
31
33
|
:action => route.action,
|
32
|
-
:channel => queue.channel,
|
33
34
|
:content_type => properties[:content_type],
|
34
35
|
:delivery_tag => delivery_info.delivery_tag,
|
35
36
|
:exchange => delivery_info.exchange,
|
@@ -45,7 +46,9 @@ module ActionSubscriber
|
|
45
46
|
end
|
46
47
|
|
47
48
|
def auto_subscribe!
|
48
|
-
|
49
|
+
subscriptions.each do |subscription|
|
50
|
+
route = subscription[:route]
|
51
|
+
queue = subscription[:queue]
|
49
52
|
channel = queue.channel
|
50
53
|
channel.prefetch(route.prefetch) if route.acknowledgements?
|
51
54
|
consumer = ::Bunny::Consumer.new(channel, queue, channel.generate_consumer_tag, !route.acknowledgements?)
|
@@ -63,7 +66,7 @@ module ActionSubscriber
|
|
63
66
|
:queue => queue.name,
|
64
67
|
}
|
65
68
|
env = ::ActionSubscriber::Middleware::Env.new(route.subscriber, encoded_payload, properties)
|
66
|
-
|
69
|
+
run_env(env)
|
67
70
|
end
|
68
71
|
bunny_consumers << consumer
|
69
72
|
queue.subscribe_with(consumer)
|
@@ -80,6 +83,13 @@ module ActionSubscriber
|
|
80
83
|
end
|
81
84
|
end
|
82
85
|
end
|
86
|
+
|
87
|
+
def run_env(env)
|
88
|
+
logger.info "RECEIVED #{env.message_id} from #{env.queue}"
|
89
|
+
::ActiveSupport::Notifications.instrument "process_event.action_subscriber", :subscriber => env.subscriber.to_s, :routing_key => env.routing_key, :queue => env.queue do
|
90
|
+
::ActionSubscriber.config.middleware.call(env)
|
91
|
+
end
|
92
|
+
end
|
83
93
|
end
|
84
94
|
end
|
85
95
|
end
|
@@ -4,10 +4,6 @@ require "action_subscriber/uri"
|
|
4
4
|
module ActionSubscriber
|
5
5
|
class Configuration
|
6
6
|
attr_accessor :allow_low_priority_methods,
|
7
|
-
:async_publisher,
|
8
|
-
:async_publisher_drop_messages_when_queue_full,
|
9
|
-
:async_publisher_max_queue_size,
|
10
|
-
:async_publisher_supervisor_interval,
|
11
7
|
:decoder,
|
12
8
|
:default_exchange,
|
13
9
|
:error_handler,
|
@@ -19,7 +15,6 @@ module ActionSubscriber
|
|
19
15
|
:pop_interval,
|
20
16
|
:port,
|
21
17
|
:prefetch,
|
22
|
-
:publisher_confirms,
|
23
18
|
:seconds_to_wait_for_graceful_shutdown,
|
24
19
|
:username,
|
25
20
|
:threadpool_size,
|
@@ -31,10 +26,6 @@ module ActionSubscriber
|
|
31
26
|
|
32
27
|
DEFAULTS = {
|
33
28
|
:allow_low_priority_methods => false,
|
34
|
-
:async_publisher => 'memory',
|
35
|
-
:async_publisher_drop_messages_when_queue_full => false,
|
36
|
-
:async_publisher_max_queue_size => 1_000_000,
|
37
|
-
:async_publisher_supervisor_interval => 200, # in milliseconds
|
38
29
|
:default_exchange => 'events',
|
39
30
|
:heartbeat => 5,
|
40
31
|
:host => 'localhost',
|
@@ -42,8 +33,7 @@ module ActionSubscriber
|
|
42
33
|
:mode => 'subscribe',
|
43
34
|
:pop_interval => 100, # in milliseconds
|
44
35
|
:port => 5672,
|
45
|
-
:prefetch =>
|
46
|
-
:publisher_confirms => false,
|
36
|
+
:prefetch => 2,
|
47
37
|
:seconds_to_wait_for_graceful_shutdown => 30,
|
48
38
|
:threadpool_size => 8,
|
49
39
|
:timeout => 1,
|
@@ -1,19 +1,21 @@
|
|
1
1
|
module ActionSubscriber
|
2
2
|
module DefaultRouting
|
3
|
-
def routes
|
3
|
+
def routes(route_settings)
|
4
4
|
@routes ||= begin
|
5
5
|
routes = []
|
6
6
|
exchange_names.each do |exchange_name|
|
7
7
|
subscribable_methods.each do |method_name|
|
8
|
-
|
8
|
+
settings = {
|
9
9
|
acknowledgements: acknowledge_messages?,
|
10
10
|
action: method_name,
|
11
|
-
durable: false,
|
11
|
+
durable: false,
|
12
12
|
exchange: exchange_name,
|
13
13
|
routing_key: routing_key_name_for_method(method_name),
|
14
14
|
subscriber: self,
|
15
15
|
queue: queue_name_for_method(method_name),
|
16
|
-
}
|
16
|
+
}
|
17
|
+
settings.merge!(route_settings)
|
18
|
+
routes << ActionSubscriber::Route.new(settings)
|
17
19
|
end
|
18
20
|
end
|
19
21
|
routes
|
@@ -18,7 +18,9 @@ module ActionSubscriber
|
|
18
18
|
# of times we will pop each time we poll the broker
|
19
19
|
times_to_pop = [::ActionSubscriber::Threadpool.ready_size, ::ActionSubscriber.config.times_to_pop].min
|
20
20
|
times_to_pop.times do
|
21
|
-
|
21
|
+
subscriptions.each do |subscription|
|
22
|
+
route = subscription[:route]
|
23
|
+
queue = subscription[:queue]
|
22
24
|
# Handle busy checks on a per threadpool basis
|
23
25
|
next if route.threadpool.busy?
|
24
26
|
|
@@ -27,7 +29,6 @@ module ActionSubscriber
|
|
27
29
|
::ActiveSupport::Notifications.instrument "popped_event.action_subscriber", :payload_size => encoded_payload.bytesize, :queue => queue.name
|
28
30
|
properties = {
|
29
31
|
:action => route.action,
|
30
|
-
:channel => queue.channel,
|
31
32
|
:content_type => metadata.content_type,
|
32
33
|
:delivery_tag => metadata.delivery_tag,
|
33
34
|
:exchange => metadata.exchange,
|
@@ -46,7 +47,9 @@ module ActionSubscriber
|
|
46
47
|
end
|
47
48
|
|
48
49
|
def auto_subscribe!
|
49
|
-
|
50
|
+
subscriptions.each do |subscription|
|
51
|
+
route = subscription[:route]
|
52
|
+
queue = subscription[:queue]
|
50
53
|
queue.channel.prefetch = route.prefetch if route.acknowledgements?
|
51
54
|
consumer = queue.subscribe(route.queue_subscription_options) do |metadata, encoded_payload|
|
52
55
|
::ActiveSupport::Notifications.instrument "received_event.action_subscriber", :payload_size => encoded_payload.bytesize, :queue => queue.name
|
@@ -62,7 +65,7 @@ module ActionSubscriber
|
|
62
65
|
:queue => queue.name,
|
63
66
|
}
|
64
67
|
env = ::ActionSubscriber::Middleware::Env.new(route.subscriber, encoded_payload, properties)
|
65
|
-
|
68
|
+
run_env(env)
|
66
69
|
end
|
67
70
|
|
68
71
|
march_hare_consumers << consumer
|
@@ -84,6 +87,13 @@ module ActionSubscriber
|
|
84
87
|
end
|
85
88
|
end
|
86
89
|
|
90
|
+
def run_env(env)
|
91
|
+
logger.info "RECEIVED #{env.message_id} from #{env.queue}"
|
92
|
+
::ActiveSupport::Notifications.instrument "process_event.action_subscriber", :subscriber => env.subscriber.to_s, :routing_key => env.routing_key, :queue => env.queue do
|
93
|
+
::ActionSubscriber.config.middleware.call(env)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
87
97
|
def _normalized_headers(metadata)
|
88
98
|
return {} unless metadata.headers
|
89
99
|
metadata.headers.each_with_object({}) do |(header,value), hash|
|
@@ -46,7 +46,7 @@ module ActionSubscriber
|
|
46
46
|
end
|
47
47
|
|
48
48
|
def self.with_exchange(env, ttl, retry_queue_name)
|
49
|
-
channel = RabbitConnection.
|
49
|
+
channel = RabbitConnection.with_connection(:default){|connection| connection.create_channel}
|
50
50
|
begin
|
51
51
|
channel.confirm_select
|
52
52
|
# an empty string is the default exchange [see bunny docs](http://rubybunny.info/articles/exchanges.html#default_exchange)
|
@@ -28,7 +28,7 @@ module ActionSubscriber
|
|
28
28
|
# :routing_key => String
|
29
29
|
def initialize(subscriber, encoded_payload, properties)
|
30
30
|
@action = properties.fetch(:action)
|
31
|
-
@channel = properties
|
31
|
+
@channel = properties[:channel]
|
32
32
|
@content_type = properties.fetch(:content_type)
|
33
33
|
@delivery_tag = properties.fetch(:delivery_tag)
|
34
34
|
@encoded_payload = encoded_payload
|
@@ -41,12 +41,14 @@ module ActionSubscriber
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def acknowledge
|
44
|
+
fail ::RuntimeError, "you can't acknowledge messages under the polling API" unless @channel
|
44
45
|
acknowledge_multiple_messages = false
|
45
46
|
@channel.ack(@delivery_tag, acknowledge_multiple_messages)
|
46
47
|
true
|
47
48
|
end
|
48
49
|
|
49
50
|
def reject
|
51
|
+
fail ::RuntimeError, "you can't acknowledge messages under the polling API" unless @channel
|
50
52
|
requeue_message = true
|
51
53
|
@channel.reject(@delivery_tag, requeue_message)
|
52
54
|
true
|
@@ -8,10 +8,17 @@ module ActionSubscriber
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def call(env)
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
# This insulates the connection thread from errors that are raised by the error_handle or
|
12
|
+
# exceptions that don't fall under StandardError (which are not caught by `rescue => error`)
|
13
|
+
new_thread = ::Thread.new do
|
14
|
+
begin
|
15
|
+
@app.call(env)
|
16
|
+
rescue => error
|
17
|
+
logger.error "FAILED #{env.message_id}"
|
18
|
+
::ActionSubscriber.configuration.error_handler.call(error, env.to_h)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
::Thread.pass while new_thread.alive?
|
15
22
|
end
|
16
23
|
end
|
17
24
|
end
|
@@ -3,57 +3,40 @@ require 'thread'
|
|
3
3
|
module ActionSubscriber
|
4
4
|
module RabbitConnection
|
5
5
|
SUBSCRIBER_CONNECTION_MUTEX = ::Mutex.new
|
6
|
-
PUBLISHER_CONNECTION_MUTEX = ::Mutex.new
|
7
6
|
NETWORK_RECOVERY_INTERVAL = 1.freeze
|
8
7
|
|
9
|
-
def self.
|
10
|
-
publisher_connection.try(:connected?)
|
11
|
-
end
|
12
|
-
|
13
|
-
def self.publisher_connection
|
8
|
+
def self.setup_connection(name, settings)
|
14
9
|
SUBSCRIBER_CONNECTION_MUTEX.synchronize do
|
15
|
-
|
16
|
-
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def self.publisher_disconnect!
|
21
|
-
SUBSCRIBER_CONNECTION_MUTEX.synchronize do
|
22
|
-
if @publisher_connection && @publisher_connection.connected?
|
23
|
-
@publisher_connection.close
|
24
|
-
end
|
25
|
-
|
26
|
-
@publisher_connection = nil
|
10
|
+
fail ArgumentError, "a #{name} connection already exists" if subscriber_connections[name]
|
11
|
+
subscriber_connections[name] = create_connection(settings)
|
27
12
|
end
|
28
13
|
end
|
29
14
|
|
30
15
|
def self.subscriber_connected?
|
31
|
-
|
16
|
+
subscriber_connections.all?{|_name, connection| connection.connected?}
|
32
17
|
end
|
33
18
|
|
34
|
-
def self.
|
19
|
+
def self.subscriber_disconnect!
|
35
20
|
SUBSCRIBER_CONNECTION_MUTEX.synchronize do
|
36
|
-
|
37
|
-
@
|
21
|
+
subscriber_connections.each{|_name, connection| connection.close}
|
22
|
+
@subscriber_connections = []
|
38
23
|
end
|
39
24
|
end
|
40
25
|
|
41
|
-
def self.
|
26
|
+
def self.with_connection(name)
|
42
27
|
SUBSCRIBER_CONNECTION_MUTEX.synchronize do
|
43
|
-
|
44
|
-
|
45
|
-
end
|
46
|
-
|
47
|
-
@subscriber_connection = nil
|
28
|
+
fail ArgumentError, "there is no connection named #{name}" unless subscriber_connections[name]
|
29
|
+
yield(subscriber_connections[name])
|
48
30
|
end
|
49
31
|
end
|
50
32
|
|
51
33
|
# Private API
|
52
|
-
def self.create_connection
|
34
|
+
def self.create_connection(settings)
|
35
|
+
options = connection_options.merge(settings)
|
53
36
|
if ::RUBY_PLATFORM == "java"
|
54
|
-
connection = ::MarchHare.connect(
|
37
|
+
connection = ::MarchHare.connect(options)
|
55
38
|
else
|
56
|
-
connection = ::Bunny.new(
|
39
|
+
connection = ::Bunny.new(options)
|
57
40
|
connection.start
|
58
41
|
connection
|
59
42
|
end
|
@@ -62,18 +45,24 @@ module ActionSubscriber
|
|
62
45
|
|
63
46
|
def self.connection_options
|
64
47
|
{
|
48
|
+
:automatically_recover => true,
|
65
49
|
:continuation_timeout => ::ActionSubscriber.configuration.timeout * 1_000.0, #convert sec to ms
|
66
50
|
:heartbeat => ::ActionSubscriber.configuration.heartbeat,
|
67
51
|
:hosts => ::ActionSubscriber.configuration.hosts,
|
52
|
+
:network_recovery_interval => NETWORK_RECOVERY_INTERVAL,
|
68
53
|
:pass => ::ActionSubscriber.configuration.password,
|
69
54
|
:port => ::ActionSubscriber.configuration.port,
|
55
|
+
:recover_from_connection_close => true,
|
56
|
+
:threadpool_size => ::ActionSubscriber.configuration.threadpool_size,
|
70
57
|
:user => ::ActionSubscriber.configuration.username,
|
71
58
|
:vhost => ::ActionSubscriber.configuration.virtual_host,
|
72
|
-
:automatically_recover => true,
|
73
|
-
:network_recovery_interval => NETWORK_RECOVERY_INTERVAL,
|
74
|
-
:recover_from_connection_close => true,
|
75
59
|
}
|
76
60
|
end
|
77
61
|
private_class_method :connection_options
|
62
|
+
|
63
|
+
def self.subscriber_connections
|
64
|
+
@subscriber_connections ||= {}
|
65
|
+
end
|
66
|
+
private_class_method :subscriber_connections
|
78
67
|
end
|
79
68
|
end
|
@@ -2,7 +2,9 @@ module ActionSubscriber
|
|
2
2
|
class Route
|
3
3
|
attr_reader :acknowledgements,
|
4
4
|
:action,
|
5
|
-
:
|
5
|
+
:concurrency,
|
6
|
+
:connection_name,
|
7
|
+
:durable,
|
6
8
|
:exchange,
|
7
9
|
:prefetch,
|
8
10
|
:queue,
|
@@ -13,6 +15,8 @@ module ActionSubscriber
|
|
13
15
|
def initialize(attributes)
|
14
16
|
@acknowledgements = attributes.fetch(:acknowledgements)
|
15
17
|
@action = attributes.fetch(:action)
|
18
|
+
@concurrency = attributes.fetch(:concurrency, 1)
|
19
|
+
@connection_name = attributes.fetch(:connection_name)
|
16
20
|
@durable = attributes.fetch(:durable)
|
17
21
|
@exchange = attributes.fetch(:exchange).to_s
|
18
22
|
@prefetch = attributes.fetch(:prefetch) { ::ActionSubscriber.config.prefetch }
|
@@ -12,27 +12,28 @@ module ActionSubscriber
|
|
12
12
|
@routes = routes
|
13
13
|
end
|
14
14
|
|
15
|
-
def
|
15
|
+
def setup_subscriptions!
|
16
|
+
fail ::RuntimeError, "you cannot setup queues multiple times, this should only happen once at startup" unless subscriptions.empty?
|
16
17
|
routes.each do |route|
|
17
|
-
|
18
|
+
route.concurrency.times do
|
19
|
+
subscriptions << {
|
20
|
+
:route => route,
|
21
|
+
:queue => setup_queue(route),
|
22
|
+
}
|
23
|
+
end
|
18
24
|
end
|
19
25
|
end
|
20
26
|
|
21
27
|
private
|
22
28
|
|
23
|
-
def
|
24
|
-
@
|
29
|
+
def subscriptions
|
30
|
+
@subscriptions ||= []
|
25
31
|
end
|
26
32
|
|
27
33
|
def setup_queue(route)
|
28
|
-
channel = ::ActionSubscriber::RabbitConnection.
|
29
|
-
# Make channels threadsafe again! Believe Me!
|
30
|
-
# Accessing channels from multiple threads for messsage acknowledgement will crash
|
31
|
-
# a channel and stop messages from being received on that channel
|
32
|
-
# this isn't very clear in the documentation for march_hare/bunny, but it is
|
33
|
-
# explicitly addresses here: https://github.com/rabbitmq/rabbitmq-java-client/issues/53
|
34
|
-
channel = ::ActionSubscriber::Synchronizer.new(channel)
|
34
|
+
channel = ::ActionSubscriber::RabbitConnection.with_connection(route.connection_name){ |connection| connection.create_channel }
|
35
35
|
exchange = channel.topic(route.exchange)
|
36
|
+
# TODO go to back to the old way of creating a queue?
|
36
37
|
queue = create_queue(channel, route.queue, :durable => route.durable)
|
37
38
|
queue.bind(exchange, :routing_key => route.routing_key)
|
38
39
|
queue
|
@@ -12,6 +12,17 @@ module ActionSubscriber
|
|
12
12
|
:exchange => "events",
|
13
13
|
}.freeze
|
14
14
|
|
15
|
+
def initialize
|
16
|
+
@current_connection_name = :default
|
17
|
+
end
|
18
|
+
|
19
|
+
def connection(name, settings)
|
20
|
+
::ActionSubscriber::RabbitConnection.setup_connection(name, settings)
|
21
|
+
@current_connection_name = name
|
22
|
+
yield
|
23
|
+
@current_connection_name = :default
|
24
|
+
end
|
25
|
+
|
15
26
|
def default_routing_key_for(route_settings)
|
16
27
|
[
|
17
28
|
route_settings[:publisher],
|
@@ -30,7 +41,7 @@ module ActionSubscriber
|
|
30
41
|
end
|
31
42
|
|
32
43
|
def default_routes_for(subscriber)
|
33
|
-
subscriber.routes.each do |route|
|
44
|
+
subscriber.routes({:connection_name => @current_connection_name}).each do |route|
|
34
45
|
routes << route
|
35
46
|
end
|
36
47
|
end
|
@@ -40,7 +51,7 @@ module ActionSubscriber
|
|
40
51
|
end
|
41
52
|
|
42
53
|
def route(subscriber, action, options = {})
|
43
|
-
route_settings = DEFAULT_SETTINGS.merge(options).merge(:subscriber => subscriber, :action => action)
|
54
|
+
route_settings = DEFAULT_SETTINGS.merge(:connection_name => @current_connection_name).merge(options).merge(:subscriber => subscriber, :action => action)
|
44
55
|
route_settings[:routing_key] ||= default_routing_key_for(route_settings)
|
45
56
|
route_settings[:queue] ||= default_queue_for(route_settings)
|
46
57
|
routes << Route.new(route_settings)
|
data/lib/action_subscriber.rb
CHANGED
@@ -25,9 +25,6 @@ require "action_subscriber/subscribable"
|
|
25
25
|
require "action_subscriber/bunny/subscriber"
|
26
26
|
require "action_subscriber/march_hare/subscriber"
|
27
27
|
require "action_subscriber/babou"
|
28
|
-
require "action_subscriber/publisher"
|
29
|
-
require "action_subscriber/publisher/async"
|
30
|
-
require "action_subscriber/synchronizer"
|
31
28
|
require "action_subscriber/route"
|
32
29
|
require "action_subscriber/route_set"
|
33
30
|
require "action_subscriber/router"
|
@@ -81,19 +78,21 @@ module ActionSubscriber
|
|
81
78
|
end
|
82
79
|
end
|
83
80
|
|
84
|
-
def self.
|
85
|
-
|
81
|
+
def self.setup_default_connection!
|
82
|
+
::ActionSubscriber::RabbitConnection.setup_connection(:default, {})
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.setup_subscriptions!
|
86
|
+
route_set.setup_subscriptions!
|
86
87
|
end
|
87
88
|
|
88
89
|
def self.start_queues
|
89
|
-
|
90
|
-
setup_queues!
|
90
|
+
setup_subscriptions!
|
91
91
|
print_subscriptions
|
92
92
|
end
|
93
93
|
|
94
94
|
def self.start_subscribers
|
95
|
-
|
96
|
-
setup_queues!
|
95
|
+
setup_subscriptions!
|
97
96
|
auto_subscribe!
|
98
97
|
print_subscriptions
|
99
98
|
end
|
@@ -106,21 +105,14 @@ module ActionSubscriber
|
|
106
105
|
require "action_subscriber/railtie" if defined?(Rails)
|
107
106
|
::ActiveSupport.run_load_hooks(:action_subscriber, Base)
|
108
107
|
|
109
|
-
# Intialize async publisher adapter
|
110
|
-
::ActionSubscriber::Publisher::Async.publisher_adapter
|
111
|
-
|
112
108
|
##
|
113
109
|
# Private Implementation
|
114
110
|
#
|
115
111
|
def self.route_set
|
116
112
|
@route_set ||= begin
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
else
|
121
|
-
logger.warn "DEPRECATION WARNING: We are inferring your routes by looking at your subscribers. This behavior is deprecated and will be removed in version 2.0. Please see the routing guide at https://github.com/mxenabled/action_subscriber/blob/master/routing.md"
|
122
|
-
RouteSet.new(self.send(:default_routes))
|
123
|
-
end
|
113
|
+
fail "cannot start because no routes have been defined. Please make sure that you call ActionSubscriber.draw_routes when your application loads" unless @draw_routes_block
|
114
|
+
routes = Router.draw_routes(&@draw_routes_block)
|
115
|
+
RouteSet.new(routes)
|
124
116
|
end
|
125
117
|
end
|
126
118
|
private_class_method :route_set
|
@@ -132,8 +124,3 @@ module ActionSubscriber
|
|
132
124
|
end
|
133
125
|
private_class_method :default_routes
|
134
126
|
end
|
135
|
-
|
136
|
-
at_exit do
|
137
|
-
::ActionSubscriber::Publisher::Async.publisher_adapter.shutdown!
|
138
|
-
::ActionSubscriber::RabbitConnection.publisher_disconnect!
|
139
|
-
end
|
@@ -32,7 +32,7 @@ describe "subscriber filters", :integration => true do
|
|
32
32
|
it "runs multiple around filters" do
|
33
33
|
$messages = [] #testing the order of things
|
34
34
|
::ActionSubscriber.auto_subscribe!
|
35
|
-
::
|
35
|
+
::ActivePublisher.publish("insta.first", "hEY Guyz!", "events")
|
36
36
|
|
37
37
|
verify_expectation_within(1.0) do
|
38
38
|
expect($messages).to eq [:whisper_before, :yell_before, "hEY Guyz!", :yell_after, :whisper_after]
|
@@ -17,7 +17,7 @@ describe "at_least_once! mode", :integration => true do
|
|
17
17
|
|
18
18
|
it "retries a failed job until it succeeds" do
|
19
19
|
::ActionSubscriber.auto_subscribe!
|
20
|
-
::
|
20
|
+
::ActivePublisher.publish("gorby_puff.grumpy", "GrumpFace", "events")
|
21
21
|
|
22
22
|
verify_expectation_within(2.0) do
|
23
23
|
expect($messages).to eq Set.new(["GrumpFace::0","GrumpFace::1","GrumpFace::2"])
|
@@ -17,7 +17,7 @@ describe "at_most_once! mode", :integration => true do
|
|
17
17
|
|
18
18
|
it "does not retry a failed message" do
|
19
19
|
::ActionSubscriber.auto_subscribe!
|
20
|
-
::
|
20
|
+
::ActivePublisher.publish("pokemon.caught_em_all", "All Pokemon have been caught", "events")
|
21
21
|
|
22
22
|
verify_expectation_within(1.0) do
|
23
23
|
expect($messages.size).to eq 1
|
@@ -7,7 +7,6 @@ class GusSubscriber < ActionSubscriber::Base
|
|
7
7
|
end
|
8
8
|
|
9
9
|
describe "Automatically reconnect on connection failure", :integration => true, :slow => true do
|
10
|
-
let(:connection) { subscriber.connection }
|
11
10
|
let(:draw_routes) do
|
12
11
|
::ActionSubscriber.draw_routes do
|
13
12
|
default_routes_for GusSubscriber
|
@@ -18,7 +17,7 @@ describe "Automatically reconnect on connection failure", :integration => true,
|
|
18
17
|
|
19
18
|
it "reconnects when a connection drops" do
|
20
19
|
::ActionSubscriber::auto_subscribe!
|
21
|
-
::
|
20
|
+
::ActivePublisher.publish("gus.spoke", "First", "events")
|
22
21
|
verify_expectation_within(5.0) do
|
23
22
|
expect($messages).to eq(Set.new(["First"]))
|
24
23
|
end
|
@@ -26,10 +25,10 @@ describe "Automatically reconnect on connection failure", :integration => true,
|
|
26
25
|
close_all_connections!
|
27
26
|
sleep 5.0
|
28
27
|
verify_expectation_within(5.0) do
|
29
|
-
expect(connection).to
|
28
|
+
expect(::ActionSubscriber::RabbitConnection.with_connection(:default){|connection| connection.open?}).to eq(true)
|
30
29
|
end
|
31
30
|
|
32
|
-
::
|
31
|
+
::ActivePublisher.publish("gus.spoke", "Second", "events")
|
33
32
|
verify_expectation_within(5.0) do
|
34
33
|
expect($messages).to eq(Set.new(["First", "Second"]))
|
35
34
|
end
|