activemessaging-kestrel-adapter 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +7 -0
- data/README.md +22 -2
- data/Rakefile +1 -1
- data/lib/active_messaging/adapters/kestrel.rb +67 -18
- data/test/test_activemessaging-kestrel-adapter.rb +29 -0
- data/version.txt +1 -1
- metadata +25 -10
- data/.Rakefile.swp +0 -0
data/History.txt
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
== 0.0.2 / 2011-02-03
|
2
|
+
* Implement simple retry in the face of receive errors to avoid log spew
|
3
|
+
by the ActiveMessaging Gateway as it retries over and over. Extensible
|
4
|
+
with other retry policies.
|
5
|
+
* Works with ActiveMessaging up to version 0.7.1
|
6
|
+
* Add some configuration tests
|
7
|
+
|
1
8
|
== 0.0.1 / 2011-02-02
|
2
9
|
* First release, actually works in a Rails application.
|
3
10
|
|
data/README.md
CHANGED
@@ -19,6 +19,13 @@ Examples
|
|
19
19
|
# development:
|
20
20
|
# adapter: kestrel
|
21
21
|
# servers: localhost:22133
|
22
|
+
# retry_policy:
|
23
|
+
# strategy: SimpleRetry
|
24
|
+
# config:
|
25
|
+
# tries: 1
|
26
|
+
# delay: 5
|
27
|
+
# the retry_policy: second is optional and should be left out under
|
28
|
+
# most circumstances (see below).
|
22
29
|
config = YAML.load(File.read("broker.yml"))
|
23
30
|
|
24
31
|
adapter = ActiveMessaging::Adapter::Kestrel::Connection.new(config[:development])
|
@@ -28,12 +35,25 @@ Examples
|
|
28
35
|
adapter.subscribe("queue2")
|
29
36
|
adapter.receive # get a message from any of the subscribed queues, or nil if they are all empty
|
30
37
|
|
38
|
+
Configuration
|
39
|
+
-------------
|
40
|
+
|
41
|
+
See the ActiveMessaging project for details of broker configuration. This
|
42
|
+
Kestrel adapter can be configured with a retry_policy, but it is not
|
43
|
+
necessary to do so as a sensible default will be used if the configuration
|
44
|
+
is not present. The default policy is as shown above. The default
|
45
|
+
policy in the face of receive exceptions is to sleep 5 seconds, then retry
|
46
|
+
once. If an exception still occurs, it is raised. The strategy must
|
47
|
+
refer to a class that once instantiated, responds to the do_work() method.
|
48
|
+
The method must take a options hash. The value of the options hash will
|
49
|
+
be the config: key in the configuration file. For 99% of use cases, you
|
50
|
+
should not even bother defining the retry_policy as the default is fine.
|
51
|
+
The ability to override the policy is provided for edge cases.
|
31
52
|
|
32
53
|
Requirements
|
33
54
|
------------
|
34
55
|
|
35
|
-
*
|
36
|
-
* flesh out the configuration
|
56
|
+
* memcached-client
|
37
57
|
|
38
58
|
Future
|
39
59
|
------
|
data/Rakefile
CHANGED
@@ -9,22 +9,60 @@ module ActiveMessaging
|
|
9
9
|
# Simple struct for wrapping received messages
|
10
10
|
Message = Struct.new(:headers, :body, :command)
|
11
11
|
|
12
|
+
# Resolve the provided string into a class object
|
13
|
+
def to_class(class_name, initial_scope = Kernel)
|
14
|
+
class_name.split('::').inject(initial_scope) { |scope, const_name| scope.const_get(const_name) }
|
15
|
+
end
|
16
|
+
|
17
|
+
# class for retrying things (should be pulled into a separate gem)
|
18
|
+
class SimpleRetry
|
19
|
+
# Yields to caller's block and retries up to options[:tries] (default 3)
|
20
|
+
# times in the face of exceptions. Returns return result of block if
|
21
|
+
# successful. If number of tries is exhausted, the exception is reraised.
|
22
|
+
# Retry loop will sleep options[:delay] seconds between retries (default 5).
|
23
|
+
# If you want error logging, pass in :logger. If not provided, Rails.logger
|
24
|
+
# is used if defined.
|
25
|
+
def do_work(options = {})
|
26
|
+
logger = defined?(::Rails) ? ::Rails.logger : nil
|
27
|
+
use_options = {:tries => 3, :delay => 5, :logger => logger}.merge(options || {})
|
28
|
+
exception = nil
|
29
|
+
return_value = nil
|
30
|
+
logger = use_options[:logger]
|
31
|
+
use_options[:tries].times do |try|
|
32
|
+
begin
|
33
|
+
exception = nil
|
34
|
+
return_value = yield
|
35
|
+
break
|
36
|
+
rescue Exception => e
|
37
|
+
exception = e
|
38
|
+
logger.warn("Got error on try #{try}: #{exception} retrying after #{use_options[:delay]} seconds") if logger
|
39
|
+
end
|
40
|
+
sleep use_options[:delay]
|
41
|
+
end
|
42
|
+
|
43
|
+
raise exception if exception
|
44
|
+
return return_value
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
12
48
|
# Connection to a kestrel message queue server
|
13
|
-
class Connection < ActiveMessaging::Adapters::
|
49
|
+
class Connection < ActiveMessaging::Adapters::BaseConnection
|
14
50
|
include ActiveMessaging::Adapter
|
15
51
|
register :kestrel
|
16
52
|
attr_accessor :reliable
|
17
|
-
# Time to sleep between polls of empty queues
|
18
|
-
attr_accessor :receive_throttle
|
19
53
|
# Reconnect on error
|
20
|
-
attr_accessor :
|
54
|
+
attr_accessor :retry_policy
|
21
55
|
|
22
56
|
def initialize(cfg)
|
23
|
-
|
24
|
-
|
25
|
-
@
|
57
|
+
cfg = symbolize_keys(cfg)
|
58
|
+
@retry_policy = cfg.delete(:retry_policy) || {:strategy => SimpleRetry, :config => {:tries => 1, :delay => 5}}
|
59
|
+
if @retry_policy[:strategy].is_a?(String)
|
60
|
+
# TODO: when getting a retry policy strategy from the config file, it will be a string. Convert it to a class
|
61
|
+
# using the Kestrel module as a context, then Kernel.
|
62
|
+
@retry_policy[:strategy] = Kestrel.const_get(retry_policy[:strategy]) rescue Kestrel.to_class(@retry_policy[:strategy])
|
63
|
+
end
|
26
64
|
@config = cfg
|
27
|
-
@
|
65
|
+
@subscriptions = {}
|
28
66
|
connect
|
29
67
|
nil
|
30
68
|
end
|
@@ -39,17 +77,17 @@ module ActiveMessaging
|
|
39
77
|
# messages from it
|
40
78
|
def subscribe(destination_name, headers = {})
|
41
79
|
headers[:destination] = destination_name
|
42
|
-
if @
|
80
|
+
if @subscriptions[destination_name]
|
43
81
|
# TODO: Should you get an exception or no?
|
44
82
|
else
|
45
|
-
@
|
83
|
+
@subscriptions[destination_name] = headers
|
46
84
|
end
|
47
85
|
nil
|
48
86
|
end
|
49
87
|
|
50
88
|
# Stop receiving messages from the named destination
|
51
89
|
def unsubscribe(destination_name, headers = {})
|
52
|
-
@
|
90
|
+
@subscriptions.delete(destination_name)
|
53
91
|
end
|
54
92
|
|
55
93
|
# Send a message to the named destination. headers can
|
@@ -67,10 +105,13 @@ module ActiveMessaging
|
|
67
105
|
# Gets a message from any subscribed destination and returns it as a
|
68
106
|
# ActiveMessaging::Adaptors::Kestrel::Message object
|
69
107
|
def receive
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
108
|
+
return nil if @subscriptions.size < 1
|
109
|
+
|
110
|
+
# instantiate a class for doing the retries
|
111
|
+
retrier = @retry_policy[:strategy].new
|
112
|
+
|
113
|
+
retrier.do_work(@retry_policy[:config]) do
|
114
|
+
queues_to_check = @subscriptions.size > 1 ? @subscriptions.keys.sort_by{rand} : @subscriptions.keys
|
74
115
|
queues_to_check.each do |queue|
|
75
116
|
if item = @kestrel.get(normalize(queue))
|
76
117
|
# TODO: ActiveMessaging ought to provide a way to do messaging
|
@@ -78,9 +119,7 @@ module ActiveMessaging
|
|
78
119
|
return Message.new({'destination' => queue}, item, 'MESSAGE')
|
79
120
|
end
|
80
121
|
end
|
81
|
-
|
82
|
-
# sleep @receive_throttle
|
83
|
-
#end
|
122
|
+
end
|
84
123
|
return nil
|
85
124
|
end
|
86
125
|
|
@@ -91,6 +130,16 @@ module ActiveMessaging
|
|
91
130
|
@normalized_names ||= Hash.new {|h,k| h[k] = k.gsub('/', '--FS--')}
|
92
131
|
@normalized_names[name]
|
93
132
|
end
|
133
|
+
|
134
|
+
def symbolize_keys(hash)
|
135
|
+
hash.inject({}) do |new_hash, (k, v)|
|
136
|
+
if v.is_a?(Hash)
|
137
|
+
v = symbolize_keys(v)
|
138
|
+
end
|
139
|
+
new_hash[k.to_sym] = v
|
140
|
+
new_hash
|
141
|
+
end
|
142
|
+
end
|
94
143
|
end
|
95
144
|
end
|
96
145
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'activemessaging'
|
3
|
+
require 'activemessaging-kestrel-adapter'
|
4
|
+
|
5
|
+
class TestActiveMessagingKestrelAdapter < Test::Unit::TestCase
|
6
|
+
def test_default_config
|
7
|
+
cfg = {:servers => 'localhost:22133'}
|
8
|
+
adapter = ActiveMessaging::Adapters::Kestrel::Connection.new(cfg)
|
9
|
+
assert_equal adapter.instance_variable_get("@retry_policy")[:strategy], ActiveMessaging::Adapters::Kestrel::SimpleRetry
|
10
|
+
assert_equal adapter.instance_variable_get("@retry_policy")[:config], {:tries => 1, :delay => 5}
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_config_from_yaml
|
14
|
+
yaml = <<-YAML
|
15
|
+
development:
|
16
|
+
adapter: kestrel
|
17
|
+
servers: localhost:22133
|
18
|
+
retry_policy:
|
19
|
+
strategy: SimpleRetry
|
20
|
+
config:
|
21
|
+
tries: 1
|
22
|
+
delay: 5
|
23
|
+
YAML
|
24
|
+
cfg = YAML.load(yaml)['development']
|
25
|
+
adapter = ActiveMessaging::Adapters::Kestrel::Connection.new(cfg)
|
26
|
+
assert_equal adapter.instance_variable_get("@retry_policy")[:strategy], ActiveMessaging::Adapters::Kestrel::SimpleRetry
|
27
|
+
assert_equal adapter.instance_variable_get("@retry_policy")[:config], {:tries => 1, :delay => 5}
|
28
|
+
end
|
29
|
+
end
|
data/version.txt
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.2
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activemessaging-kestrel-adapter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 27
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 2
|
10
|
+
version: 0.0.2
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Douglas A. Seifert
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-02-
|
18
|
+
date: 2011-02-04 00:00:00 -08:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -26,18 +26,34 @@ dependencies:
|
|
26
26
|
requirements:
|
27
27
|
- - "="
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
hash:
|
29
|
+
hash: 61
|
30
30
|
segments:
|
31
31
|
- 1
|
32
32
|
- 8
|
33
|
-
-
|
34
|
-
version: 1.8.
|
33
|
+
- 5
|
34
|
+
version: 1.8.5
|
35
35
|
type: :runtime
|
36
36
|
version_requirements: *id001
|
37
37
|
- !ruby/object:Gem::Dependency
|
38
|
-
name:
|
38
|
+
name: activemessaging
|
39
39
|
prerelease: false
|
40
40
|
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - "="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 1
|
46
|
+
segments:
|
47
|
+
- 0
|
48
|
+
- 7
|
49
|
+
- 1
|
50
|
+
version: 0.7.1
|
51
|
+
type: :runtime
|
52
|
+
version_requirements: *id002
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: bones
|
55
|
+
prerelease: false
|
56
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
41
57
|
none: false
|
42
58
|
requirements:
|
43
59
|
- - ">="
|
@@ -49,7 +65,7 @@ dependencies:
|
|
49
65
|
- 2
|
50
66
|
version: 3.6.2
|
51
67
|
type: :development
|
52
|
-
version_requirements: *
|
68
|
+
version_requirements: *id003
|
53
69
|
description: |-
|
54
70
|
This is an adapter to the kestrel messaging server for the ActiveMessaging framework. It is
|
55
71
|
in the early stages of development.
|
@@ -63,7 +79,6 @@ extra_rdoc_files:
|
|
63
79
|
- README.md
|
64
80
|
- bin/activemessaging-kestrel-adapter
|
65
81
|
files:
|
66
|
-
- .Rakefile.swp
|
67
82
|
- .bnsignore
|
68
83
|
- History.txt
|
69
84
|
- README.md
|
data/.Rakefile.swp
DELETED
Binary file
|