shoryuken 0.0.5 → 1.0.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/.gitignore +1 -0
- data/.hound.yml +2 -0
- data/.travis.yml +2 -1
- data/Gemfile +2 -0
- data/README.md +20 -8
- data/Rakefile +2 -2
- data/examples/bootstrap_queues.rb +17 -9
- data/lib/shoryuken.rb +27 -3
- data/lib/shoryuken/cli.rb +18 -126
- data/lib/shoryuken/client.rb +26 -12
- data/lib/shoryuken/environment_loader.rb +167 -0
- data/lib/shoryuken/extensions/active_job_adapter.rb +12 -4
- data/lib/shoryuken/fetcher.rb +6 -7
- data/lib/shoryuken/manager.rb +6 -2
- data/lib/shoryuken/middleware/server/auto_delete.rb +7 -1
- data/lib/shoryuken/middleware/server/timing.rb +2 -2
- data/lib/shoryuken/processor.rb +6 -4
- data/lib/shoryuken/sns_arn.rb +27 -0
- data/lib/shoryuken/topic.rb +17 -0
- data/lib/shoryuken/version.rb +1 -1
- data/lib/shoryuken/worker.rb +4 -9
- data/shoryuken.gemspec +4 -1
- data/spec/integration/launcher_spec.rb +53 -62
- data/spec/shoryuken/client_spec.rb +9 -50
- data/spec/shoryuken/default_worker_registry_spec.rb +1 -1
- data/spec/shoryuken/fetcher_spec.rb +27 -21
- data/spec/shoryuken/middleware/server/auto_delete_spec.rb +20 -8
- data/spec/shoryuken/middleware/server/timing_spec.rb +14 -4
- data/spec/shoryuken/processor_spec.rb +74 -24
- data/spec/shoryuken/sns_arn_spec.rb +42 -0
- data/spec/shoryuken/topic_spec.rb +32 -0
- data/spec/shoryuken/util_spec.rb +3 -1
- data/spec/shoryuken/worker_spec.rb +25 -12
- data/spec/spec_helper.rb +22 -11
- metadata +57 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9072ce45c134cd9563ee7dddac27bc7d19733cde
|
4
|
+
data.tar.gz: 2b48cb3b0d594ef1111456d0f9101ce154056995
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 62bde57b564da861f90c2a68816a69743766f1120dd146817f884b2d4d2e5e5119dcef8858516b7d2581517a04177ec0edb151a97f254c6185ae9092c96efab2
|
7
|
+
data.tar.gz: e011b7d261eaf6e4b87d7dc322d2f458da09fdcd5074eac90eedb8101c082b2987f303b557ba20d90c89b321a7d4df7d613852890b6888263ccbc8adb1bf77db
|
data/.gitignore
CHANGED
data/.hound.yml
ADDED
data/.travis.yml
CHANGED
@@ -4,6 +4,7 @@ rvm:
|
|
4
4
|
# - 1.9.2
|
5
5
|
- 2.0.0
|
6
6
|
- 2.1.0
|
7
|
+
- 2.2.0
|
7
8
|
# - ruby-head
|
8
9
|
# - jruby-19mode
|
9
10
|
# - jruby-head
|
@@ -13,4 +14,4 @@ notifications:
|
|
13
14
|
on_success: change
|
14
15
|
on_failure: always
|
15
16
|
|
16
|
-
script: bundle exec rspec spec
|
17
|
+
script: SPEC_ALL=true bundle exec rspec spec
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -5,6 +5,7 @@
|
|
5
5
|
Shoryuken _sho-ryu-ken_ is a super-efficient [AWS SQS](https://aws.amazon.com/sqs/) thread-based message processor.
|
6
6
|
|
7
7
|
[](https://travis-ci.org/phstc/shoryuken)
|
8
|
+
[](https://codeclimate.com/github/phstc/shoryuken)
|
8
9
|
|
9
10
|
## Key features
|
10
11
|
|
@@ -15,8 +16,8 @@ Yeah, Shoryuken load balances the messages consumption!
|
|
15
16
|
Given this configuration:
|
16
17
|
|
17
18
|
```yaml
|
18
|
-
concurrency: 25
|
19
|
-
delay: 25
|
19
|
+
concurrency: 25
|
20
|
+
delay: 25
|
20
21
|
queues:
|
21
22
|
- [high_priority, 6]
|
22
23
|
- [default, 2]
|
@@ -37,6 +38,10 @@ If all queues get empty, all processors will be changed to the waiting state and
|
|
37
38
|
|
38
39
|
To be even more performance and cost efficient, Shoryuken fetches SQS messages in batches, so a single SQS request can fetch up to 10 messages.
|
39
40
|
|
41
|
+
## Requirements
|
42
|
+
|
43
|
+
Ruby 2.0 or greater. Ruby 1.9 is no longer supported.
|
44
|
+
|
40
45
|
## Installation
|
41
46
|
|
42
47
|
Add this line to your application's Gemfile:
|
@@ -109,19 +114,26 @@ aws:
|
|
109
114
|
access_key_id: ... # or <%= ENV['AWS_ACCESS_KEY_ID'] %>
|
110
115
|
secret_access_key: ... # or <%= ENV['AWS_SECRET_ACCESS_KEY'] %>
|
111
116
|
region: us-east-1 # or <%= ENV['AWS_REGION'] %>
|
112
|
-
receive_message: # See http://docs.aws.amazon.com/
|
117
|
+
receive_message: # See http://docs.aws.amazon.com/sdkforruby/api/Aws/SQS/Queue.html#receive_messages-instance_method
|
113
118
|
# wait_time_seconds: N # The number of seconds to wait for new messages when polling. Defaults to the #wait_time_seconds defined on the queue
|
114
|
-
|
115
|
-
-
|
116
|
-
-
|
117
|
-
concurrency: 25
|
118
|
-
delay: 25
|
119
|
+
attribute_names:
|
120
|
+
- ApproximateReceiveCount
|
121
|
+
- SentTimestamp
|
122
|
+
concurrency: 25 # The number of allocated threads to process messages. Default 25
|
123
|
+
delay: 25 # The delay in seconds to pause a queue when it's empty. Default 0
|
119
124
|
queues:
|
120
125
|
- [high_priority, 6]
|
121
126
|
- [default, 2]
|
122
127
|
- [low_priority, 1]
|
123
128
|
```
|
124
129
|
|
130
|
+
The ```aws``` section is used to configure both the Aws objects used by Shoryuken internally, and also to set up some Shoryuken-specific config. The Shoryuken-specific keys are listed below, and you can expect any other key defined in that block to be passed on untouched to ```Aws::SQS::Client#initialize```:
|
131
|
+
|
132
|
+
- ```account_id``` is used when generating SNS ARNs
|
133
|
+
- ```sns_endpoint``` can be used to explicitly override the SNS endpoint
|
134
|
+
- ```sqs_endpoint``` can be used to explicitly override the SQS endpoint
|
135
|
+
- ```receive_message``` can be used to define the options passed to the http://docs.aws.amazon.com/sdkforruby/api/Aws/SQS/Queue.html#receive_messages-instance_method
|
136
|
+
|
125
137
|
### Rails Integration
|
126
138
|
|
127
139
|
You can tell Shoryuken to load your Rails application by passing the `-R` or `--rails` flag to the "shoryuken" command.
|
data/Rakefile
CHANGED
@@ -11,11 +11,11 @@ task :console do
|
|
11
11
|
if File.exist? config_file
|
12
12
|
config = YAML.load File.read(config_file)
|
13
13
|
|
14
|
-
|
14
|
+
Aws.config = config['aws']
|
15
15
|
end
|
16
16
|
|
17
17
|
def push(queue, message)
|
18
|
-
Shoryuken::Client.queues(queue).send_message message
|
18
|
+
Shoryuken::Client.queues(queue).send_message(message_body: message)
|
19
19
|
end
|
20
20
|
|
21
21
|
ARGV.clear
|
@@ -4,17 +4,25 @@ require 'shoryuken'
|
|
4
4
|
# load SQS credentials
|
5
5
|
config = YAML.load File.read(File.join(File.expand_path('..', __FILE__), 'shoryuken.yml'))
|
6
6
|
|
7
|
-
|
7
|
+
Aws.config = config['aws']
|
8
8
|
|
9
|
-
sqs =
|
9
|
+
sqs = Aws::SQS::Client.new
|
10
10
|
|
11
|
-
|
12
|
-
# after 7 attempts SQS will move the message to the dead letter queue
|
11
|
+
default_queue_url = sqs.create_queue(queue_name: 'default').queue_url
|
13
12
|
|
14
|
-
|
15
|
-
|
13
|
+
if sqs.config['endpoint'] =~ /amazonaws.com/
|
14
|
+
# create a dead letter queue
|
15
|
+
# after 7 attempts SQS will move the message to the dead letter queue
|
16
16
|
|
17
|
-
|
18
|
-
options[:redrive_policy] = %Q{{"maxReceiveCount":"7", "deadLetterTargetArn":"#{dl.arn}"}"}
|
17
|
+
dead_letter_queue_url = sqs.create_queue(queue_name: 'default_failures').queue_url
|
19
18
|
|
20
|
-
sqs.
|
19
|
+
dead_letter_queue_arn = sqs.get_queue_attributes(
|
20
|
+
queue_url: dead_letter_queue_url,
|
21
|
+
attribute_names: %w(QueueArn)
|
22
|
+
).attributes['QueueArn']
|
23
|
+
|
24
|
+
attributes = {}
|
25
|
+
attributes['RedrivePolicy'] = %Q{{"maxReceiveCount":"7", "deadLetterTargetArn":"#{dead_letter_queue_arn}"}}
|
26
|
+
|
27
|
+
sqs.set_queue_attributes queue_url: default_queue_url, attributes: attributes
|
28
|
+
end
|
data/lib/shoryuken.rb
CHANGED
@@ -1,18 +1,22 @@
|
|
1
1
|
require 'yaml'
|
2
|
-
require 'aws-sdk-
|
2
|
+
require 'aws-sdk-core'
|
3
|
+
require 'aws-sdk-resources'
|
3
4
|
require 'time'
|
4
5
|
|
5
6
|
require 'shoryuken/version'
|
6
7
|
require 'shoryuken/core_ext'
|
7
8
|
require 'shoryuken/util'
|
9
|
+
require 'shoryuken/logging'
|
10
|
+
require 'shoryuken/environment_loader'
|
8
11
|
require 'shoryuken/client'
|
9
12
|
require 'shoryuken/worker'
|
10
13
|
require 'shoryuken/worker_registry'
|
11
14
|
require 'shoryuken/default_worker_registry'
|
12
|
-
require 'shoryuken/logging'
|
13
15
|
require 'shoryuken/middleware/chain'
|
14
16
|
require 'shoryuken/middleware/server/auto_delete'
|
15
17
|
require 'shoryuken/middleware/server/timing'
|
18
|
+
require 'shoryuken/sns_arn'
|
19
|
+
require 'shoryuken/topic'
|
16
20
|
|
17
21
|
module Shoryuken
|
18
22
|
DEFAULTS = {
|
@@ -57,7 +61,7 @@ module Shoryuken
|
|
57
61
|
# end
|
58
62
|
# end
|
59
63
|
def configure_server
|
60
|
-
yield self
|
64
|
+
yield self if server?
|
61
65
|
end
|
62
66
|
|
63
67
|
def server_middleware
|
@@ -79,6 +83,22 @@ module Shoryuken
|
|
79
83
|
@@default_worker_options = options
|
80
84
|
end
|
81
85
|
|
86
|
+
def on_aws_initialization(&block)
|
87
|
+
@aws_initialization_callback = block
|
88
|
+
end
|
89
|
+
|
90
|
+
def on_start(&block)
|
91
|
+
@start_callback = block
|
92
|
+
end
|
93
|
+
|
94
|
+
def on_stop(&block)
|
95
|
+
@stop_callback = block
|
96
|
+
end
|
97
|
+
|
98
|
+
attr_reader :aws_initialization_callback,
|
99
|
+
:start_callback,
|
100
|
+
:stop_callback
|
101
|
+
|
82
102
|
private
|
83
103
|
|
84
104
|
def default_server_middleware
|
@@ -91,6 +111,10 @@ module Shoryuken
|
|
91
111
|
end
|
92
112
|
end
|
93
113
|
end
|
114
|
+
|
115
|
+
def server?
|
116
|
+
defined?(Shoryuken::CLI)
|
117
|
+
end
|
94
118
|
end
|
95
119
|
end
|
96
120
|
|
data/lib/shoryuken/cli.rb
CHANGED
@@ -20,19 +20,19 @@ module Shoryuken
|
|
20
20
|
self_read, self_write = IO.pipe
|
21
21
|
|
22
22
|
%w[INT TERM USR1 USR2 TTIN].each do |sig|
|
23
|
-
|
24
|
-
|
23
|
+
begin
|
24
|
+
trap sig do
|
25
|
+
self_write.puts(sig)
|
26
|
+
end
|
27
|
+
rescue ArgumentError
|
28
|
+
puts "Signal #{sig} not supported"
|
25
29
|
end
|
26
30
|
end
|
27
31
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
end
|
33
|
-
require_workers
|
34
|
-
validate!
|
35
|
-
patch_deprecated_workers!
|
32
|
+
options = parse_cli_args(args)
|
33
|
+
|
34
|
+
EnvironmentLoader.load(options)
|
35
|
+
|
36
36
|
daemonize
|
37
37
|
write_pid
|
38
38
|
load_celluloid
|
@@ -40,6 +40,11 @@ module Shoryuken
|
|
40
40
|
require 'shoryuken/launcher'
|
41
41
|
@launcher = Shoryuken::Launcher.new
|
42
42
|
|
43
|
+
if callback = Shoryuken.start_callback
|
44
|
+
logger.info "Calling Shoryuken.on_start block"
|
45
|
+
callback.call
|
46
|
+
end
|
47
|
+
|
43
48
|
begin
|
44
49
|
launcher.run
|
45
50
|
|
@@ -67,26 +72,6 @@ module Shoryuken
|
|
67
72
|
require 'shoryuken/manager'
|
68
73
|
end
|
69
74
|
|
70
|
-
def load_rails
|
71
|
-
# Adapted from: https://github.com/mperham/sidekiq/blob/master/lib/sidekiq/cli.rb
|
72
|
-
|
73
|
-
require 'rails'
|
74
|
-
if ::Rails::VERSION::MAJOR < 4
|
75
|
-
require File.expand_path("config/environment.rb")
|
76
|
-
::Rails.application.eager_load!
|
77
|
-
else
|
78
|
-
# Painful contortions, see 1791 for discussion
|
79
|
-
require File.expand_path("config/application.rb")
|
80
|
-
::Rails::Application.initializer "shoryuken.eager_load" do
|
81
|
-
::Rails.application.config.eager_load = true
|
82
|
-
end
|
83
|
-
require 'shoryuken/extensions/active_job_adapter' if defined?(::ActiveJob)
|
84
|
-
require File.expand_path("config/environment.rb")
|
85
|
-
end
|
86
|
-
|
87
|
-
logger.info "Rails environment loaded"
|
88
|
-
end
|
89
|
-
|
90
75
|
def daemonize
|
91
76
|
return unless Shoryuken.options[:daemon]
|
92
77
|
|
@@ -114,8 +99,6 @@ module Shoryuken
|
|
114
99
|
io.sync = true
|
115
100
|
end
|
116
101
|
$stdin.reopen('/dev/null')
|
117
|
-
|
118
|
-
initialize_logger
|
119
102
|
end
|
120
103
|
|
121
104
|
def write_pid
|
@@ -126,8 +109,8 @@ module Shoryuken
|
|
126
109
|
end
|
127
110
|
end
|
128
111
|
|
129
|
-
def
|
130
|
-
opts = {}
|
112
|
+
def parse_cli_args(argv)
|
113
|
+
opts = { queues: [] }
|
131
114
|
|
132
115
|
@parser = OptionParser.new do |o|
|
133
116
|
o.on '-c', '--concurrency INT', 'Processor threads to use' do |arg|
|
@@ -140,7 +123,7 @@ module Shoryuken
|
|
140
123
|
|
141
124
|
o.on '-q', '--queue QUEUE[,WEIGHT]...', 'Queues to process with optional weights' do |arg|
|
142
125
|
queue, weight = arg.split(',')
|
143
|
-
|
126
|
+
opts[:queues] << [queue, weight]
|
144
127
|
end
|
145
128
|
|
146
129
|
o.on '-r', '--require [PATH|DIR]', 'Location of the worker' do |arg|
|
@@ -213,96 +196,5 @@ module Shoryuken
|
|
213
196
|
raise Interrupt
|
214
197
|
end
|
215
198
|
end
|
216
|
-
|
217
|
-
def setup_options(args)
|
218
|
-
options = parse_options(args)
|
219
|
-
|
220
|
-
# yield parsed options in case we need to do more setup before configuration is parsed
|
221
|
-
yield(options) if block_given?
|
222
|
-
|
223
|
-
config = options[:config_file] ? parse_config(options[:config_file]).deep_symbolize_keys : {}
|
224
|
-
|
225
|
-
Shoryuken.options.merge!(config)
|
226
|
-
|
227
|
-
Shoryuken.options.merge!(options)
|
228
|
-
|
229
|
-
parse_queues
|
230
|
-
end
|
231
|
-
|
232
|
-
def parse_config(config_file)
|
233
|
-
if File.exist?(config_file)
|
234
|
-
YAML.load(ERB.new(IO.read(config_file)).result)
|
235
|
-
else
|
236
|
-
raise ArgumentError, "Config file #{config_file} does not exist"
|
237
|
-
end
|
238
|
-
end
|
239
|
-
|
240
|
-
def initialize_logger(options = Shoryuken.options)
|
241
|
-
Shoryuken::Logging.initialize_logger(options[:logfile]) if options[:logfile]
|
242
|
-
|
243
|
-
Shoryuken.logger.level = Logger::DEBUG if options[:verbose]
|
244
|
-
end
|
245
|
-
|
246
|
-
def validate!
|
247
|
-
raise ArgumentError, 'No queues supplied' if Shoryuken.queues.empty?
|
248
|
-
|
249
|
-
all_queues = Shoryuken.queues
|
250
|
-
queues_with_workers = Shoryuken.worker_registry.queues
|
251
|
-
|
252
|
-
unless defined?(::ActiveJob)
|
253
|
-
(all_queues - queues_with_workers).each do |queue|
|
254
|
-
logger.warn "No worker supplied for '#{queue}'"
|
255
|
-
end
|
256
|
-
end
|
257
|
-
|
258
|
-
initialize_aws
|
259
|
-
|
260
|
-
Shoryuken.queues.uniq.each do |queue|
|
261
|
-
# validate all queues and AWS credentials consequently
|
262
|
-
begin
|
263
|
-
Shoryuken::Client.queues queue
|
264
|
-
rescue AWS::SQS::Errors::NonExistentQueue => e
|
265
|
-
raise ArgumentError, "Queue '#{queue}' does not exist"
|
266
|
-
rescue => e
|
267
|
-
raise
|
268
|
-
end
|
269
|
-
end
|
270
|
-
end
|
271
|
-
|
272
|
-
def initialize_aws
|
273
|
-
# aws-sdk tries to load the credentials from the ENV variables: AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY
|
274
|
-
# when not explicit supplied
|
275
|
-
AWS.config Shoryuken.options[:aws] if Shoryuken.options[:aws]
|
276
|
-
end
|
277
|
-
|
278
|
-
def require_workers
|
279
|
-
require Shoryuken.options[:require] if Shoryuken.options[:require]
|
280
|
-
end
|
281
|
-
|
282
|
-
def parse_queues
|
283
|
-
Shoryuken.options[:queues].to_a.each { |queue_and_weight| parse_queue *queue_and_weight }
|
284
|
-
end
|
285
|
-
|
286
|
-
def parse_queue(queue, weight = nil)
|
287
|
-
[weight.to_i, 1].max.times { Shoryuken.queues << queue }
|
288
|
-
end
|
289
|
-
|
290
|
-
def patch_deprecated_workers!
|
291
|
-
Shoryuken.worker_registry.queues.each do |queue|
|
292
|
-
Shoryuken.worker_registry.workers(queue).each do |worker_class|
|
293
|
-
if worker_class.instance_method(:perform).arity == 1
|
294
|
-
logger.warn "[DEPRECATION] #{worker_class.name}#perform(sqs_msg) is deprecated. Please use #{worker_class.name}#perform(sqs_msg, body)"
|
295
|
-
|
296
|
-
worker_class.class_eval do
|
297
|
-
alias_method :deprecated_perform, :perform
|
298
|
-
|
299
|
-
def perform(sqs_msg, body = nil)
|
300
|
-
deprecated_perform(sqs_msg)
|
301
|
-
end
|
302
|
-
end
|
303
|
-
end
|
304
|
-
end
|
305
|
-
end
|
306
|
-
end
|
307
199
|
end
|
308
200
|
end
|
data/lib/shoryuken/client.rb
CHANGED
@@ -1,29 +1,43 @@
|
|
1
1
|
module Shoryuken
|
2
2
|
class Client
|
3
3
|
@@queues = {}
|
4
|
-
@@
|
4
|
+
@@topics = {}
|
5
5
|
|
6
6
|
class << self
|
7
|
-
def queues(
|
8
|
-
@@queues[
|
7
|
+
def queues(name)
|
8
|
+
@@queues[name.to_s] ||= sqs_resource.get_queue_by_name(queue_name: name)
|
9
9
|
end
|
10
10
|
|
11
|
-
def
|
12
|
-
|
11
|
+
def sns
|
12
|
+
@sns ||= Aws::SNS::Client.new(aws_client_options(:sns_endpoint))
|
13
13
|
end
|
14
14
|
|
15
|
-
def
|
16
|
-
|
15
|
+
def sns_arn
|
16
|
+
@sns_arn ||= SnsArn
|
17
17
|
end
|
18
18
|
|
19
|
-
def
|
20
|
-
|
19
|
+
def sqs
|
20
|
+
@sqs ||= Aws::SQS::Client.new(aws_client_options(:sqs_endpoint))
|
21
|
+
end
|
21
22
|
|
22
|
-
|
23
|
+
def sqs_resource
|
24
|
+
@sqs_resource ||= Aws::SQS::Resource.new(client: sqs)
|
23
25
|
end
|
24
26
|
|
25
|
-
def
|
26
|
-
|
27
|
+
def topics(name)
|
28
|
+
@@topics[name.to_s] ||= Topic.new(name, sns)
|
29
|
+
end
|
30
|
+
|
31
|
+
attr_accessor :account_id
|
32
|
+
attr_writer :sns, :sqs, :sqs_resource, :sns_arn
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def aws_client_options service_endpoint_key
|
37
|
+
explicit_endpoint = Shoryuken.options[:aws][service_endpoint_key]
|
38
|
+
options = {}
|
39
|
+
options[:endpoint] = explicit_endpoint unless explicit_endpoint.to_s.empty?
|
40
|
+
options
|
27
41
|
end
|
28
42
|
end
|
29
43
|
end
|