stapfen 2.2.1 → 3.0.2.21
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 +15 -7
- data/.travis.yml +8 -5
- data/Gemfile +0 -3
- data/README.md +37 -27
- data/lib/stapfen.rb +17 -3
- data/lib/stapfen/client/kafka.rb +12 -3
- data/lib/stapfen/version.rb +1 -1
- data/lib/stapfen/worker.rb +163 -146
- data/spec/client/kafka_spec.rb +33 -8
- data/spec/spec_helper.rb +3 -4
- data/spec/worker_spec.rb +346 -178
- data/stapfen.gemspec +7 -5
- metadata +39 -35
- data/lib/stapfen/logger.rb +0 -48
- data/spec/logger_spec.rb +0 -41
checksums.yaml
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
|
-
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
---
|
|
2
|
+
!binary "U0hBMQ==":
|
|
3
|
+
metadata.gz: !binary |-
|
|
4
|
+
OGRlODBmYzg5OWFlNmMwYTdjYTc4MDkwY2ZiNzMyYTVhODY2YjgxNA==
|
|
5
|
+
data.tar.gz: !binary |-
|
|
6
|
+
NDQ0YTIwZjRlNjdiMzg4NTE3YThiZDZjOGJhOGFjZWI4OWM1ODg3Mw==
|
|
7
|
+
SHA512:
|
|
8
|
+
metadata.gz: !binary |-
|
|
9
|
+
MzI4ZDUwNTg4OTA1ZGFkNWY5ZjVjMzU0N2E3NTFkOGI5ZTliYWI0NTliYzkz
|
|
10
|
+
N2RhNGFjZDg2YjM4NmQ5YTllYjNmMDBiMGQ1MDAwOWFmYzM5NjVmNDk4ZWI3
|
|
11
|
+
YTBjODk1N2RhMmUyMTMyZmE5MjNiZmI5ODAxZTA4ZDA1YjNjZjM=
|
|
12
|
+
data.tar.gz: !binary |-
|
|
13
|
+
MTA3MTg2MTU4NWVhY2Y2YTlkY2QzOGJjOWMwNTVhNDFmNTk4ZTFkMTM3ZTM3
|
|
14
|
+
OTBkNzk0ODdmNjBjODlkOTM3NzkzMjQwZDRkMzE4OTI0YWJlYTQ3MTEyMDA2
|
|
15
|
+
ZTFjOWI4YjY5MzE3ZGUzNzgzOWFmMzM2MTlkNGEyMTQwY2Y5YTY=
|
data/.travis.yml
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
language: ruby
|
|
2
2
|
rvm:
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
- ruby-head
|
|
4
|
+
- 2.1
|
|
5
|
+
- 1.9.3
|
|
6
|
+
- jruby
|
|
7
|
+
deploy:
|
|
8
|
+
provider: rubygems
|
|
9
|
+
api_key:
|
|
10
|
+
secure: Nec0EJ9uI21cWjT+e2Yo/MWb03QsQMPk6jMStMcYeTKfNtk/NntPl1LJOQ73JV625QVAItaRzuXDbCfV9Sm2c6zvWJapEJkVp7DAQiZshzKkxSb94N3Uo+Jzgfw0m08Vz6x3af8bLMQgkbcVjLDpbz6L7H2nIf4t1UkJH/I8CeU=
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -20,10 +20,11 @@ Consider the following `myworker.rb` file:
|
|
|
20
20
|
|
|
21
21
|
```ruby
|
|
22
22
|
class MyWorker < Stapfen::Worker
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
configure do |worker|
|
|
24
|
+
# You can also specify your own logger, but this is the default...
|
|
25
|
+
worker.logger = Logger.new(STDOUT)
|
|
26
|
+
worker.protocol = STOMP
|
|
27
|
+
worker.client_options = {
|
|
27
28
|
:hosts => [
|
|
28
29
|
{
|
|
29
30
|
:host => 'localhost',
|
|
@@ -32,18 +33,14 @@ class MyWorker < Stapfen::Worker
|
|
|
32
33
|
:passcode => 'guest',
|
|
33
34
|
:ssl => false
|
|
34
35
|
}
|
|
35
|
-
]
|
|
36
|
+
],
|
|
37
|
+
:topic => 'thequeue',
|
|
38
|
+
:dead_letter_queue => '/queue/dlq',
|
|
39
|
+
:max_redeliveries => 0
|
|
36
40
|
}
|
|
37
41
|
end
|
|
38
42
|
|
|
39
|
-
|
|
40
|
-
log do
|
|
41
|
-
Logger.new(STDOUT)
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
consume 'thequeue', :dead_letter_queue => '/queue/dlq',
|
|
45
|
-
:max_redeliveries => 0 do |message|
|
|
46
|
-
|
|
43
|
+
consume do |message|
|
|
47
44
|
data = expensive_computation(message.body)
|
|
48
45
|
# Save my data, or do something worker-specific with it
|
|
49
46
|
persist(data)
|
|
@@ -57,13 +54,10 @@ end
|
|
|
57
54
|
MyWorker.run!
|
|
58
55
|
```
|
|
59
56
|
|
|
57
|
+
When using the STOMP protocol, `worker.client_options` can be set with any of the attributes described in a `Stomp::Client` [connection
|
|
58
|
+
hash](https://github.com/stompgem/stomp#hash-login-example-usage-this-is-the-recommended-login-technique) as well as any `subscription` options.
|
|
60
59
|
|
|
61
|
-
When using the
|
|
62
|
-
`Stomp::Client` [connection
|
|
63
|
-
hash](https://github.com/stompgem/stomp#hash-login-example-usage-this-is-the-recommended-login-technique).
|
|
64
|
-
|
|
65
|
-
In the case of the JMS protocol, the value returned from the `configure` block
|
|
66
|
-
is expected to be a valid [configuration
|
|
60
|
+
When using the JMS protocol, `worker.client_options` can be set with any of the attributes described in [configuration
|
|
67
61
|
hash](https://github.com/reidmorrison/jruby-jms#consumer) for the
|
|
68
62
|
[jruby-jms](https://github.com/reidmorrison/jruby-jms) gem.
|
|
69
63
|
|
|
@@ -76,30 +70,46 @@ require 'stapfen'
|
|
|
76
70
|
require 'stapfen/worker'
|
|
77
71
|
|
|
78
72
|
class MyWorker < Stapfen::Worker
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
73
|
+
configure do |worker|
|
|
74
|
+
# You can also specify your own logger, but this is the default...
|
|
75
|
+
worker.logger = Logger.new(STDOUT)
|
|
76
|
+
worker.protocol = KAFKA
|
|
77
|
+
worker.client_options = {
|
|
78
|
+
:topic => 'test',
|
|
84
79
|
:groupId => 'groupId',
|
|
85
80
|
:zookeepers => 'localhost:2181' # comma separated string of zookeepers
|
|
86
81
|
}
|
|
87
82
|
end
|
|
88
83
|
|
|
89
|
-
|
|
90
|
-
consume '/topic/test' do |message|
|
|
84
|
+
consume do |message|
|
|
91
85
|
puts "Recv: #{message.body}"
|
|
92
86
|
end
|
|
93
87
|
end
|
|
94
88
|
|
|
95
89
|
MyWorker.run!
|
|
96
90
|
```
|
|
91
|
+
|
|
92
|
+
##### Notes
|
|
93
|
+
* Testing with Kafka
|
|
94
|
+
* Start Staphen worker first
|
|
95
|
+
* Using producer included with kafka
|
|
96
|
+
* Produce some messages
|
|
97
|
+
* ```echo foobar | bin/kafka-console-producer.sh --broker-list <brokers> --topic <topic>```
|
|
98
|
+
* Worker should be able to read the message
|
|
99
|
+
* using the same groupId a consumer will start reading from the last offset that was read by a consumer from the same group
|
|
100
|
+
* For example, Given 2 consumers belong to the same groupId
|
|
101
|
+
* Consumer1 reads a few messages and dies
|
|
102
|
+
* A producer produces 5 messages
|
|
103
|
+
* Consumer2 starts up and will receive the 5 messages produced because it started at the last offset of Consumer1
|
|
104
|
+
|
|
97
105
|
---
|
|
98
106
|
|
|
99
107
|
It is also important to note that the `consume` block will be invoked inside an
|
|
100
108
|
**instance** of `MyWorker` and will execute inside its own `Thread`, so take
|
|
101
109
|
care when accessing other shared resources.
|
|
102
110
|
|
|
111
|
+
Also note you'll need to include the zk gem manually.
|
|
112
|
+
|
|
103
113
|
### Fallback and dead-letter-queue support
|
|
104
114
|
|
|
105
115
|
The consume block accepts the usual subscriptions headers, as well as two
|
|
@@ -109,7 +119,6 @@ the block returns `false`; after `:max_redeliveries`, it will send the message
|
|
|
109
119
|
to `:dead_letter_queue`. `consume` blocks without these headers will fail
|
|
110
120
|
silently rather than unreceive.
|
|
111
121
|
|
|
112
|
-
|
|
113
122
|
## Installation
|
|
114
123
|
|
|
115
124
|
Add this line to your application's Gemfile:
|
|
@@ -128,6 +137,7 @@ Or install it yourself as:
|
|
|
128
137
|
|
|
129
138
|
Download this from jar from Maven Central
|
|
130
139
|
* [activemq-all-5.8.0.jar](http://search.maven.org/#artifactdetails%7Corg.apache.activemq%7Cactivemq-all%7C5.8.0%7Cjar)
|
|
140
|
+
* `wget -O activemq-all-5.8.0.jar http://search.maven.org/remotecontent?filepath=org/apache/activemq/activemq-all/5.8.0/activemq-all-5.8.0.jar`
|
|
131
141
|
* Put it in gem root
|
|
132
142
|
* ```rake spec```
|
|
133
143
|
|
data/lib/stapfen.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require 'thread_safe'
|
|
2
|
+
|
|
1
3
|
begin
|
|
2
4
|
require 'stomp'
|
|
3
5
|
rescue LoadError
|
|
@@ -16,10 +18,22 @@ require 'stapfen/client'
|
|
|
16
18
|
require 'stapfen/worker'
|
|
17
19
|
|
|
18
20
|
module Stapfen
|
|
19
|
-
class ConfigurationError < StandardError
|
|
21
|
+
class ConfigurationError < StandardError; end
|
|
22
|
+
class ConsumeError < StandardError; end
|
|
23
|
+
class InvalidMessageError < StandardError; end
|
|
24
|
+
|
|
25
|
+
def self.logger=(instance)
|
|
26
|
+
@logger = instance
|
|
20
27
|
end
|
|
21
|
-
|
|
28
|
+
|
|
29
|
+
def self.logger
|
|
30
|
+
@logger ||= default_logger
|
|
22
31
|
end
|
|
23
|
-
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def self.default_logger
|
|
36
|
+
require 'logger'
|
|
37
|
+
Logger.new(STDOUT)
|
|
24
38
|
end
|
|
25
39
|
end
|
data/lib/stapfen/client/kafka.rb
CHANGED
|
@@ -20,6 +20,7 @@ module Stapfen
|
|
|
20
20
|
# @option configuration [String] :topic The kafka topic
|
|
21
21
|
# @option configuration [String] :groupId The kafka groupId
|
|
22
22
|
# @option configuration [String] :zookeepers Comma separated list of zookeepers
|
|
23
|
+
# @option configuration [Hash] :consumer_opts Options for Hermann consumer
|
|
23
24
|
#
|
|
24
25
|
# @raises [ConfigurationError] if required configs are not present
|
|
25
26
|
def initialize(configuration)
|
|
@@ -28,8 +29,9 @@ module Stapfen
|
|
|
28
29
|
@topic = @config[:topic]
|
|
29
30
|
@groupId = @config[:groupId]
|
|
30
31
|
@zookeepers = @config[:zookeepers]
|
|
32
|
+
opts = @config[:consumer_opts]
|
|
31
33
|
raise ConfigurationError unless @groupId && @zookeepers
|
|
32
|
-
@connection = Hermann::Consumer.new(@topic, @groupId, @zookeepers)
|
|
34
|
+
@connection = Hermann::Consumer.new(@topic, @groupId, @zookeepers, opts)
|
|
33
35
|
end
|
|
34
36
|
|
|
35
37
|
# This method is not implemenented
|
|
@@ -42,6 +44,14 @@ module Stapfen
|
|
|
42
44
|
false
|
|
43
45
|
end
|
|
44
46
|
|
|
47
|
+
# API compatibilty method, doesn't actually indicate that the connection
|
|
48
|
+
# is closed. Will only return true if no connection currently exists
|
|
49
|
+
#
|
|
50
|
+
# @return [Boolean]
|
|
51
|
+
def closed?
|
|
52
|
+
return connection.nil?
|
|
53
|
+
end
|
|
54
|
+
|
|
45
55
|
# Closes the consumer threads created by kafka.
|
|
46
56
|
#
|
|
47
57
|
# @return [Boolean] True/false depending on whether we actually closed
|
|
@@ -61,8 +71,7 @@ module Stapfen
|
|
|
61
71
|
#
|
|
62
72
|
# @params [block] block to yield consumed messages
|
|
63
73
|
def subscribe(destination, headers={}, &block)
|
|
64
|
-
|
|
65
|
-
connection.consume(destination.as_kafka, &block)
|
|
74
|
+
connection.consume(destination, &block)
|
|
66
75
|
end
|
|
67
76
|
|
|
68
77
|
def runloop
|
data/lib/stapfen/version.rb
CHANGED
data/lib/stapfen/worker.rb
CHANGED
|
@@ -1,58 +1,145 @@
|
|
|
1
|
-
require 'stomp'
|
|
2
|
-
require 'stapfen/logger'
|
|
3
1
|
require 'stapfen/destination'
|
|
4
2
|
require 'stapfen/message'
|
|
5
3
|
|
|
6
4
|
module Stapfen
|
|
7
5
|
class Worker
|
|
8
|
-
|
|
6
|
+
KAFKA = :kafka.freeze
|
|
7
|
+
STOMP = :stomp.freeze
|
|
8
|
+
JMS = :jms.freeze
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
@@signals_handled = false
|
|
12
|
-
@@workers = []
|
|
10
|
+
attr_accessor :client_options, :protocol, :logger, :stapfen_client
|
|
13
11
|
|
|
14
12
|
class << self
|
|
15
|
-
attr_accessor :
|
|
16
|
-
|
|
13
|
+
attr_accessor :instance_configuration, :consumers, :destructor
|
|
14
|
+
|
|
15
|
+
def configure(&configuration_block)
|
|
16
|
+
unless block_given?
|
|
17
|
+
raise Stapfen::ConfigurationError, "Method `configure` requires a block"
|
|
18
|
+
end
|
|
19
|
+
self.instance_configuration = configuration_block
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Instantiate a new +Worker+ instance and run it
|
|
23
|
+
def run!
|
|
24
|
+
worker = self.new
|
|
25
|
+
|
|
26
|
+
@@workers << worker
|
|
27
|
+
|
|
28
|
+
handle_signals
|
|
29
|
+
|
|
30
|
+
worker.run
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Main message consumption block
|
|
34
|
+
def consume(config_overrides={}, &consume_block)
|
|
35
|
+
unless block_given?
|
|
36
|
+
raise Stapfen::ConsumeError, "Method `consume` requires a block"
|
|
37
|
+
end
|
|
38
|
+
@consumers ||= ThreadSafe::Array.new
|
|
39
|
+
@consumers << [config_overrides, consume_block]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Optional method, specifes a block to execute when the worker is shutting
|
|
43
|
+
# down.
|
|
44
|
+
def shutdown(&block)
|
|
45
|
+
@destructor = block
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Return all the currently running Stapfen::Worker instances in this
|
|
49
|
+
# process
|
|
50
|
+
def workers
|
|
51
|
+
@@workers
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Invoke +exit_cleanly+ on each of the registered Worker instances that
|
|
55
|
+
# this class is keeping track of
|
|
56
|
+
#
|
|
57
|
+
# @return [Boolean] Whether or not we've exited/terminated cleanly
|
|
58
|
+
def exit_cleanly
|
|
59
|
+
return false if workers.empty?
|
|
60
|
+
|
|
61
|
+
cleanly = true
|
|
62
|
+
workers.each do |w|
|
|
63
|
+
begin
|
|
64
|
+
w.exit_cleanly
|
|
65
|
+
rescue StandardError => ex
|
|
66
|
+
$stderr.write("Failure while exiting cleanly #{ex.inspect}\n#{ex.backtrace}")
|
|
67
|
+
cleanly = false
|
|
68
|
+
end
|
|
69
|
+
end
|
|
17
70
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
71
|
+
if RUBY_PLATFORM == 'java'
|
|
72
|
+
Stapfen.logger.info 'Telling the JVM to exit cleanly'
|
|
73
|
+
Java::JavaLang::System.exit(0)
|
|
74
|
+
end
|
|
21
75
|
|
|
22
|
-
|
|
76
|
+
return cleanly
|
|
77
|
+
end
|
|
23
78
|
|
|
24
|
-
|
|
79
|
+
# Utility method to set up the proper worker signal handlers
|
|
80
|
+
def handle_signals
|
|
81
|
+
return if @@signals_handled
|
|
82
|
+
|
|
83
|
+
Signal.trap(:INT) do
|
|
84
|
+
self.exit_cleanly
|
|
85
|
+
exit!
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
Signal.trap(:TERM) do
|
|
89
|
+
self.exit_cleanly
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
@@signals_handled = true
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Class variables are put in this method to allow for "reset" style
|
|
96
|
+
# functionality if needed. Useful for testing (see worker_spec.rb).
|
|
97
|
+
def set_class_variable_defaults
|
|
98
|
+
@@signals_handled = false
|
|
99
|
+
@@workers = ThreadSafe::Array.new
|
|
100
|
+
end
|
|
25
101
|
|
|
26
|
-
worker.run
|
|
27
102
|
end
|
|
28
103
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
104
|
+
set_class_variable_defaults
|
|
105
|
+
|
|
106
|
+
############################################################################
|
|
107
|
+
# Instance Methods
|
|
108
|
+
############################################################################
|
|
109
|
+
|
|
110
|
+
def initialize
|
|
111
|
+
instance_configuration = self.class.instance_configuration
|
|
112
|
+
if instance_configuration
|
|
113
|
+
self.configure &instance_configuration
|
|
35
114
|
end
|
|
36
|
-
|
|
115
|
+
self.client_options ||= {}
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def configure(&configuration_block)
|
|
119
|
+
self.instance_eval &configuration_block
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def logger
|
|
123
|
+
@logger ||= Stapfen.logger
|
|
37
124
|
end
|
|
38
125
|
|
|
39
126
|
# Force the worker to use STOMP as the messaging protocol (default)
|
|
40
127
|
#
|
|
41
128
|
# @return [Boolean]
|
|
42
|
-
def
|
|
129
|
+
def use_stomp!
|
|
43
130
|
begin
|
|
44
131
|
require 'stomp'
|
|
45
132
|
rescue LoadError
|
|
46
|
-
|
|
133
|
+
Stapfen.logger.info 'You need the `stomp` gem to be installed to use stomp!'
|
|
47
134
|
raise
|
|
48
135
|
end
|
|
49
136
|
|
|
50
|
-
@protocol =
|
|
137
|
+
@protocol = STOMP
|
|
51
138
|
return true
|
|
52
139
|
end
|
|
53
140
|
|
|
54
|
-
def
|
|
55
|
-
@protocol
|
|
141
|
+
def stomp?
|
|
142
|
+
@protocol == STOMP
|
|
56
143
|
end
|
|
57
144
|
|
|
58
145
|
# Force the worker to use JMS as the messaging protocol.
|
|
@@ -60,25 +147,25 @@ module Stapfen
|
|
|
60
147
|
# *Note:* Only works under JRuby
|
|
61
148
|
#
|
|
62
149
|
# @return [Boolean]
|
|
63
|
-
def
|
|
150
|
+
def use_jms!
|
|
64
151
|
unless RUBY_PLATFORM == 'java'
|
|
65
|
-
raise Stapfen::ConfigurationError,
|
|
152
|
+
raise Stapfen::ConfigurationError, 'You cannot use JMS unless you are running under JRuby!'
|
|
66
153
|
end
|
|
67
154
|
|
|
68
155
|
begin
|
|
69
156
|
require 'java'
|
|
70
157
|
require 'jms'
|
|
71
158
|
rescue LoadError
|
|
72
|
-
|
|
159
|
+
Stapfen.logger.info 'You need the `jms` gem to be installed to use JMS!'
|
|
73
160
|
raise
|
|
74
161
|
end
|
|
75
162
|
|
|
76
|
-
@protocol =
|
|
163
|
+
@protocol = JMS
|
|
77
164
|
return true
|
|
78
165
|
end
|
|
79
166
|
|
|
80
|
-
def
|
|
81
|
-
@protocol ==
|
|
167
|
+
def jms?
|
|
168
|
+
@protocol == JMS
|
|
82
169
|
end
|
|
83
170
|
|
|
84
171
|
# Force the worker to use Kafka as the messaging protocol.
|
|
@@ -86,158 +173,88 @@ module Stapfen
|
|
|
86
173
|
# *Note:* Only works under JRuby
|
|
87
174
|
#
|
|
88
175
|
# @return [Boolean]
|
|
89
|
-
def
|
|
176
|
+
def use_kafka!
|
|
90
177
|
unless RUBY_PLATFORM == 'java'
|
|
91
|
-
raise Stapfen::ConfigurationError,
|
|
178
|
+
raise Stapfen::ConfigurationError, 'You cannot use Kafka unless you are running under JRuby!'
|
|
92
179
|
end
|
|
93
180
|
|
|
94
181
|
begin
|
|
95
182
|
require 'java'
|
|
96
183
|
require 'hermann'
|
|
97
184
|
rescue LoadError
|
|
98
|
-
|
|
185
|
+
Stapfen.logger.info 'You need the `hermann` gem to be installed to use Kafka!'
|
|
99
186
|
raise
|
|
100
187
|
end
|
|
101
188
|
|
|
102
|
-
@protocol =
|
|
189
|
+
@protocol = KAFKA
|
|
103
190
|
return true
|
|
104
191
|
end
|
|
105
192
|
|
|
106
|
-
def
|
|
107
|
-
@protocol ==
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
# Optional method, should be passed a block which will yield a {{Logger}}
|
|
111
|
-
# instance for the Stapfen worker to use
|
|
112
|
-
def self.log(&block)
|
|
113
|
-
@logger = block
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
# Main message consumption block
|
|
117
|
-
def self.consume(queue_name, headers={}, &block)
|
|
118
|
-
unless block_given?
|
|
119
|
-
raise Stapfen::ConsumeError, "Cannot consume #{queue_name} without a block!"
|
|
120
|
-
end
|
|
121
|
-
@consumers ||= []
|
|
122
|
-
@consumers << [queue_name, headers, block]
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
# Optional method, specifes a block to execute when the worker is shutting
|
|
126
|
-
# down.
|
|
127
|
-
def self.shutdown(&block)
|
|
128
|
-
@destructor = block
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
# Return all the currently running Stapfen::Worker instances in this
|
|
132
|
-
# process
|
|
133
|
-
def self.workers
|
|
134
|
-
@@workers
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
# Invoke +exit_cleanly+ on each of the registered Worker instances that
|
|
138
|
-
# this class is keeping track of
|
|
139
|
-
#
|
|
140
|
-
# @return [Boolean] Whether or not we've exited/terminated cleanly
|
|
141
|
-
def self.exit_cleanly
|
|
142
|
-
return false if workers.empty?
|
|
143
|
-
|
|
144
|
-
cleanly = true
|
|
145
|
-
workers.each do |w|
|
|
146
|
-
begin
|
|
147
|
-
w.exit_cleanly
|
|
148
|
-
rescue StandardError => ex
|
|
149
|
-
$stderr.write("Failure while exiting cleanly #{ex.inspect}\n#{ex.backtrace}")
|
|
150
|
-
cleanly = false
|
|
151
|
-
end
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
if RUBY_PLATFORM == 'java'
|
|
155
|
-
info "Telling the JVM to exit cleanly"
|
|
156
|
-
Java::JavaLang::System.exit(0)
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
return cleanly
|
|
193
|
+
def kafka?
|
|
194
|
+
@protocol == KAFKA
|
|
160
195
|
end
|
|
161
196
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
return if @@signals_handled
|
|
165
|
-
|
|
166
|
-
Signal.trap(:INT) do
|
|
167
|
-
self.exit_cleanly
|
|
168
|
-
exit!
|
|
169
|
-
end
|
|
170
|
-
|
|
171
|
-
Signal.trap(:TERM) do
|
|
172
|
-
self.exit_cleanly
|
|
173
|
-
end
|
|
174
|
-
|
|
175
|
-
@@signals_handled = true
|
|
197
|
+
def protocol
|
|
198
|
+
@protocol ||= KAFKA
|
|
176
199
|
end
|
|
177
200
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
############################################################################
|
|
181
|
-
# Instance Methods
|
|
182
|
-
############################################################################
|
|
183
|
-
|
|
184
|
-
attr_accessor :client
|
|
185
|
-
|
|
186
201
|
def run
|
|
187
|
-
|
|
202
|
+
case protocol
|
|
203
|
+
when STOMP
|
|
188
204
|
require 'stapfen/client/stomp'
|
|
189
|
-
|
|
190
|
-
|
|
205
|
+
stapfen_client = Stapfen::Client::Stomp.new(client_options)
|
|
206
|
+
when JMS
|
|
191
207
|
require 'stapfen/client/jms'
|
|
192
|
-
|
|
193
|
-
|
|
208
|
+
stapfen_client = Stapfen::Client::JMS.new(client_options)
|
|
209
|
+
when KAFKA
|
|
194
210
|
require 'stapfen/client/kafka'
|
|
195
|
-
|
|
211
|
+
stapfen_client = Stapfen::Client::Kafka.new(client_options)
|
|
212
|
+
else
|
|
213
|
+
raise 'No client specified'
|
|
196
214
|
end
|
|
197
215
|
|
|
198
|
-
|
|
216
|
+
logger.info("Running with #{stapfen_client} inside of Thread:#{Thread.current.inspect}")
|
|
199
217
|
|
|
200
|
-
|
|
218
|
+
stapfen_client.connect
|
|
201
219
|
|
|
202
|
-
self.class.consumers.each do |
|
|
203
|
-
|
|
204
|
-
[:
|
|
205
|
-
|
|
206
|
-
end
|
|
220
|
+
self.class.consumers.each do |config_overrides, block|
|
|
221
|
+
consumer_config = client_options.merge(config_overrides)
|
|
222
|
+
consumer_topic = consumer_config[:topic]
|
|
223
|
+
consumer_can_unreceive = !(consumer_config.keys & [:max_redeliveries, :dead_letter_queue]).empty?
|
|
207
224
|
|
|
208
225
|
# We're taking each block and turning it into a method so that we can
|
|
209
226
|
# use the instance scope instead of the blocks originally bound scope
|
|
210
227
|
# which would be at a class level
|
|
211
|
-
|
|
212
|
-
self.class.send(:define_method,
|
|
228
|
+
methodized_topic = consumer_topic.gsub(/[.|\-]/, '_').to_sym
|
|
229
|
+
self.class.send(:define_method, methodized_topic, &block)
|
|
213
230
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
if
|
|
217
|
-
|
|
231
|
+
stapfen_client.subscribe(consumer_topic, consumer_config) do |message_entity|
|
|
232
|
+
stapfen_message = nil
|
|
233
|
+
if stomp?
|
|
234
|
+
stapfen_message = Stapfen::Message.from_stomp(message_entity)
|
|
218
235
|
end
|
|
219
236
|
|
|
220
|
-
if
|
|
221
|
-
|
|
237
|
+
if jms?
|
|
238
|
+
stapfen_message = Stapfen::Message.from_jms(message_entity)
|
|
222
239
|
end
|
|
223
240
|
|
|
224
|
-
if
|
|
225
|
-
|
|
241
|
+
if kafka?
|
|
242
|
+
stapfen_message = Stapfen::Message.from_kafka(message_entity)
|
|
226
243
|
end
|
|
227
244
|
|
|
228
|
-
success = self.send(
|
|
245
|
+
success = self.send(methodized_topic, stapfen_message)
|
|
229
246
|
|
|
230
247
|
unless success
|
|
231
|
-
if
|
|
232
|
-
|
|
248
|
+
if stapfen_client.can_unreceive? && consumer_can_unreceive
|
|
249
|
+
stapfen_client.unreceive(message_entity, consumer_config)
|
|
233
250
|
end
|
|
234
251
|
end
|
|
235
252
|
end
|
|
236
253
|
end
|
|
237
254
|
|
|
238
255
|
begin
|
|
239
|
-
|
|
240
|
-
|
|
256
|
+
stapfen_client.runloop
|
|
257
|
+
logger.info("Exiting the runloop for #{self}")
|
|
241
258
|
rescue Interrupt
|
|
242
259
|
exit_cleanly
|
|
243
260
|
end
|
|
@@ -246,19 +263,19 @@ module Stapfen
|
|
|
246
263
|
# Invokes the shutdown block if it has been created, and closes the
|
|
247
264
|
# {{Stomp::Client}} connection unless it has already been shut down
|
|
248
265
|
def exit_cleanly
|
|
249
|
-
info("#{self} exiting ")
|
|
266
|
+
logger.info("#{self} exiting ")
|
|
250
267
|
self.class.destructor.call if self.class.destructor
|
|
251
268
|
|
|
252
|
-
info
|
|
269
|
+
logger.info 'Killing client'
|
|
253
270
|
begin
|
|
254
271
|
# Only close the client if we have one sitting around
|
|
255
|
-
if
|
|
256
|
-
unless
|
|
257
|
-
|
|
272
|
+
if stapfen_client
|
|
273
|
+
unless stapfen_client.closed?
|
|
274
|
+
stapfen_client.close
|
|
258
275
|
end
|
|
259
276
|
end
|
|
260
277
|
rescue StandardError => exc
|
|
261
|
-
error "Exception received while trying to close client! #{exc.inspect}"
|
|
278
|
+
logger.error "Exception received while trying to close client! #{exc.inspect}"
|
|
262
279
|
end
|
|
263
280
|
end
|
|
264
281
|
end
|