kinetic 0.0.1 → 0.0.2
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 +2 -0
- data/.travis.yml +4 -0
- data/README.md +4 -0
- data/kinetic.gemspec +2 -1
- data/lib/kinetic/base.rb +6 -33
- data/lib/kinetic/configuration.rb +1 -0
- data/lib/kinetic/dsl.rb +74 -0
- data/lib/kinetic/errors.rb +7 -6
- data/lib/kinetic/master.rb +38 -15
- data/lib/kinetic/version.rb +1 -1
- data/lib/kinetic/worker.rb +4 -6
- data/spec/base_spec.rb +8 -0
- data/spec/dsl_spec.rb +103 -0
- data/spec/spec_helper.rb +1 -0
- metadata +39 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b7a08f594cae0d5c01c00f340386df7c7788a745
|
4
|
+
data.tar.gz: 280fe43699041a8a233b455887d795b6c47e42f8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 65871da55313e959082177229600037b1ffdf5e94b1886bebb525e9099effe4b249fb7b581f54d45ed908fa68eac60e0ce7e44841e96f0738d7ae1fc731a8c3f
|
7
|
+
data.tar.gz: e8452cb376cab92b21796269deff0ac5ab24306099675eca52eeb9d64071cced80ba649e0b6399748f77d113b13e6bdd7f7aecbcf1a4bc1cbf386faa30be73ff
|
data/.gitignore
CHANGED
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
# Kinetic
|
2
2
|
|
3
|
+
[<img src="https://secure.travis-ci.org/fugufish/kinetic.png">](http://travis-ci.org/fugufish/kinetic)
|
4
|
+
[](https://codeclimate.com/repos/52b06fa413d637197401ebb7/feed)
|
5
|
+
|
6
|
+
|
3
7
|
Kinetic is an AMQP worker framework designed in vein of microframeworks such as sinatra. Its goal is to provide an
|
4
8
|
easy way to configure and run AMQP consumers in order to reduce developer overhead when working with AMQP. Kinetic
|
5
9
|
follows the master/worker convention used in Unicorn, and its prefork code is mostly based on that of Unicorn's.
|
data/kinetic.gemspec
CHANGED
data/lib/kinetic/base.rb
CHANGED
@@ -17,6 +17,8 @@ require_relative '../kinetic/version'
|
|
17
17
|
require_relative '../kinetic/configuration'
|
18
18
|
require_relative '../kinetic/master'
|
19
19
|
require_relative '../kinetic/worker'
|
20
|
+
require_relative '../kinetic/dsl'
|
21
|
+
require_relative '../kinetic/errors'
|
20
22
|
|
21
23
|
module Kinetic
|
22
24
|
|
@@ -34,28 +36,10 @@ module Kinetic
|
|
34
36
|
class Base
|
35
37
|
|
36
38
|
class << self
|
39
|
+
include Kinetic::DSL
|
37
40
|
|
38
|
-
|
39
|
-
|
40
|
-
def set(key, value)
|
41
|
-
config[key] = value
|
42
|
-
end
|
43
|
-
|
44
|
-
def config
|
45
|
-
@config ||= Kinetic::Configuration.new
|
46
|
-
end
|
47
|
-
|
48
|
-
def logger
|
49
|
-
@logger ||= reopen_logger
|
50
|
-
end
|
51
|
-
|
52
|
-
def reopen_logger
|
53
|
-
@logger = Logger.new(STDOUT)
|
54
|
-
end
|
55
|
-
|
56
|
-
def on(key, &block)
|
57
|
-
logger.debug "Setting up '#{key}' on 'direct'"
|
58
|
-
direct[key] = block
|
41
|
+
def exchanges
|
42
|
+
@exchanges ||= {}
|
59
43
|
end
|
60
44
|
|
61
45
|
private
|
@@ -65,22 +49,11 @@ module Kinetic
|
|
65
49
|
end
|
66
50
|
|
67
51
|
|
68
|
-
def exchanges
|
69
|
-
@exchanges ||= {}
|
70
|
-
end
|
71
|
-
|
72
|
-
end
|
73
52
|
|
74
|
-
delegate :logger, to: :class
|
75
|
-
delegate :config, to: :class
|
76
53
|
|
77
|
-
def reopen_logger
|
78
|
-
self.class.reopen_logger
|
79
54
|
end
|
80
55
|
|
81
|
-
|
82
|
-
self.class.send(:exchanges)
|
83
|
-
end
|
56
|
+
delegate :logger, :set, :get, :get!, :exchanges, to: :class
|
84
57
|
|
85
58
|
end
|
86
59
|
|
data/lib/kinetic/dsl.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
module Kinetic
|
2
|
+
|
3
|
+
module DSL
|
4
|
+
|
5
|
+
# Sets a configuration value
|
6
|
+
#
|
7
|
+
# @param [Symbol] key the configuration key to set
|
8
|
+
# @param [Symbol] value the configuration value
|
9
|
+
#
|
10
|
+
# @return [Boolean] returns true
|
11
|
+
def set(key, value)
|
12
|
+
config[key.to_sym] = value
|
13
|
+
true
|
14
|
+
end
|
15
|
+
|
16
|
+
# Gets a configuration value
|
17
|
+
#
|
18
|
+
# @param [Symbol] key the key of the value to get
|
19
|
+
#
|
20
|
+
# @return [Object, nil] returns the requested configuration value or nil if the value does not exist
|
21
|
+
def get(key)
|
22
|
+
config[key.to_sym]
|
23
|
+
end
|
24
|
+
|
25
|
+
# Gets a configuration value and returns an exception if the value is not present
|
26
|
+
#
|
27
|
+
# @param [Symbol] key the key of the value to get
|
28
|
+
#
|
29
|
+
# @return [Object]
|
30
|
+
#
|
31
|
+
# @raise [Kinetic::Errors::MissingConfigurationValue] if the value does not exist
|
32
|
+
def get!(key)
|
33
|
+
raise Kinetic::Errors::MissingConfigurationValue.new(key) unless (value = get(key))
|
34
|
+
value
|
35
|
+
end
|
36
|
+
|
37
|
+
# Defines a direct queue subscription. Direct queues do not allow fuzzy matching so all messages sent to this queue
|
38
|
+
# must exactly match the key.
|
39
|
+
#
|
40
|
+
# @param [String] key the key of the queue to which to subscribe.
|
41
|
+
#
|
42
|
+
# @yield [message] yields the message passed to the queue to the block
|
43
|
+
#
|
44
|
+
# @raise [Kinetic::Errors::NoSubcriberBlock] if a block is not passed
|
45
|
+
# @raise [Kinetic::Errors::KeyMustBeString] if the passed key is not a string
|
46
|
+
def on_direct(key, &block)
|
47
|
+
raise Kinetic::Errors::NoSubscriberBlock unless block_given?
|
48
|
+
raise Kinetic::Errors::KeyMustBeString unless key.is_a? String
|
49
|
+
logger.debug "Setting up '#{key}' on 'direct'"
|
50
|
+
direct[key] = block
|
51
|
+
end
|
52
|
+
|
53
|
+
alias :on :on_direct
|
54
|
+
|
55
|
+
# @return [Logger] returns the application logger instance
|
56
|
+
def logger
|
57
|
+
@logger ||= reopen_logger
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def config
|
63
|
+
@config ||= Kinetic::Configuration.new
|
64
|
+
end
|
65
|
+
|
66
|
+
def reopen_logger
|
67
|
+
@logger = Logger.new(config.log_file)
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
data/lib/kinetic/errors.rb
CHANGED
@@ -3,16 +3,17 @@ module Kinetic
|
|
3
3
|
|
4
4
|
class KineticError < StandardError; end
|
5
5
|
|
6
|
-
class
|
6
|
+
class NoSubscriberBlock < KineticError; end
|
7
|
+
class KeyMustBeString < KineticError; end
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
super
|
12
|
-
self.set_backtrace(backtrace)
|
9
|
+
class MissingConfigurationValue < KineticError
|
10
|
+
|
11
|
+
def initialize(key)
|
12
|
+
super "Expected configuration for '#{key}' but the configuration was not found."
|
13
13
|
end
|
14
14
|
|
15
15
|
end
|
16
16
|
|
17
|
+
|
17
18
|
end
|
18
19
|
end
|
data/lib/kinetic/master.rb
CHANGED
@@ -7,8 +7,8 @@ module Kinetic
|
|
7
7
|
WORKERS = {}
|
8
8
|
SIG_QUEUE = []
|
9
9
|
|
10
|
-
delegate :logger, to: :app
|
11
|
-
|
10
|
+
delegate :logger, :get, :get!, :set, to: :app
|
11
|
+
|
12
12
|
|
13
13
|
attr_reader :app
|
14
14
|
|
@@ -23,12 +23,15 @@ module Kinetic
|
|
23
23
|
begin
|
24
24
|
logger.info "Starting Kinetic #{Kinetic::VERSION} with PID #{Process.pid}"
|
25
25
|
logger.debug 'Configuration:'
|
26
|
-
logger.ap config.to_hash
|
26
|
+
logger.ap app.class.send(:config).to_hash
|
27
27
|
write_pidfile!
|
28
28
|
initialize_self_pipe!
|
29
29
|
initialize_signal_traps!
|
30
30
|
spawn_missing_workers
|
31
31
|
join
|
32
|
+
rescue => e
|
33
|
+
logger.fatal e
|
34
|
+
logger.fatal 'Unable to start worker!'
|
32
35
|
ensure
|
33
36
|
call_on_exit_callbacks
|
34
37
|
exit!
|
@@ -53,11 +56,24 @@ module Kinetic
|
|
53
56
|
|
54
57
|
private
|
55
58
|
|
59
|
+
def master_sleep(time)
|
60
|
+
IO.select([ SELF_PIPE[0] ], nil, nil, time) or return
|
61
|
+
SELF_PIPE[0].kgio_tryread(11)
|
62
|
+
end
|
63
|
+
|
56
64
|
def join
|
57
|
-
logger.debug
|
58
|
-
|
59
|
-
|
60
|
-
|
65
|
+
logger.debug 'Joining thread'
|
66
|
+
case SIG_QUEUE.shift
|
67
|
+
when :QUIT
|
68
|
+
break
|
69
|
+
when :TERM, :INT
|
70
|
+
logger.warn "#{get(:name)} is shutting down immediately!"
|
71
|
+
exit!(1)
|
72
|
+
break
|
73
|
+
else
|
74
|
+
master_sleep(0.5)
|
75
|
+
end while true
|
76
|
+
logger.warn "#{get(:name)} is going down!"
|
61
77
|
end
|
62
78
|
|
63
79
|
# Based on Unicorn's self-pipe
|
@@ -73,33 +89,40 @@ module Kinetic
|
|
73
89
|
end
|
74
90
|
|
75
91
|
def awaken_master
|
76
|
-
logger.debug 'Awakening master'
|
77
92
|
SELF_PIPE[1].kgio_trywrite('.')
|
78
93
|
end
|
79
94
|
|
80
95
|
def write_pidfile!
|
81
|
-
|
82
|
-
logger.info "Writing PID file to #{
|
83
|
-
File.open(
|
96
|
+
set(:pid, File.join(get!(:root), "#{get!(:name)}.pid"))
|
97
|
+
logger.info "Writing PID file to #{get!(:pid)}"
|
98
|
+
File.open(get!(:pid), 'w') { |f| f.write(Process.pid) }
|
84
99
|
end
|
85
100
|
|
86
101
|
def call_on_exit_callbacks
|
87
102
|
ensure
|
88
|
-
FileUtils.rm(
|
103
|
+
FileUtils.rm(get(:pid)) if get(:pid)
|
89
104
|
end
|
90
105
|
|
91
106
|
def spawn_missing_workers
|
92
107
|
logger.debug 'Spawning missing workers'
|
93
108
|
worker_nr = -1
|
94
|
-
until (worker_nr += 1) ==
|
109
|
+
until (worker_nr += 1) == get!(:workers)
|
95
110
|
WORKERS.value?(worker_nr) and next
|
111
|
+
#noinspection RubyArgCount
|
96
112
|
worker = Worker.new(worker_nr, @app)
|
97
113
|
logger.debug 'Calling before_fork block'
|
98
114
|
before_fork.call(self, worker)
|
99
115
|
logger.debug 'Forking worker'
|
116
|
+
parent = Process.pid
|
100
117
|
pid = fork do
|
101
|
-
|
102
|
-
|
118
|
+
begin
|
119
|
+
#noinspection RubyArgCount
|
120
|
+
worker.run
|
121
|
+
rescue => e
|
122
|
+
logger.fatal e
|
123
|
+
logger.fatal 'Unable to start workers'
|
124
|
+
Process.kill(:TERM, parent)
|
125
|
+
end
|
103
126
|
end
|
104
127
|
logger.debug "Worker #{worker_nr} started on #{pid}"
|
105
128
|
end
|
data/lib/kinetic/version.rb
CHANGED
data/lib/kinetic/worker.rb
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
module Kinetic
|
2
2
|
class Worker
|
3
3
|
|
4
|
-
delegate :logger, to: :app
|
5
|
-
delegate :config, to: :app
|
4
|
+
delegate :logger, :get, :get!, :set, to: :app
|
6
5
|
|
7
6
|
attr_reader :id, :app, :channel, :exchanges
|
8
7
|
|
@@ -24,9 +23,8 @@ module Kinetic
|
|
24
23
|
end
|
25
24
|
|
26
25
|
def run
|
27
|
-
logger.debug "Establishing connection host: '#{
|
28
|
-
AMQP.start(host:
|
29
|
-
app.reopen_logger
|
26
|
+
logger.debug "Establishing connection host: '#{get!(:host)}', port: '#{get!(:port)}'"
|
27
|
+
AMQP.start(host: get!(:host), port: get!(:port)) do |connection|
|
30
28
|
logger.debug "AMQP started with conneciton #{connection}"
|
31
29
|
initialize_channel!(connection)
|
32
30
|
initialize_exchanges!
|
@@ -45,7 +43,7 @@ module Kinetic
|
|
45
43
|
def initialize_exchanges!
|
46
44
|
logger.debug 'Initializing exchanges'
|
47
45
|
@exchanges = {}
|
48
|
-
prefix =
|
46
|
+
prefix = get!(:name)
|
49
47
|
app.exchanges.each_key do |name|
|
50
48
|
logger.debug " Initializing #{name} exchange"
|
51
49
|
@exchanges[name] = channel.send(name, "#{prefix}.#{name}")
|
data/spec/base_spec.rb
ADDED
data/spec/dsl_spec.rb
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require_relative '../lib/kinetic/dsl'
|
3
|
+
require_relative '../lib/kinetic/errors'
|
4
|
+
class TestClass
|
5
|
+
extend Kinetic::DSL
|
6
|
+
end
|
7
|
+
|
8
|
+
describe Kinetic::DSL do
|
9
|
+
# setting this to open brackes helps us test the returns true case
|
10
|
+
|
11
|
+
let(:config) { { key: 'value' } }
|
12
|
+
|
13
|
+
before :each do
|
14
|
+
stub_const("Kinetic::Configuration", config)
|
15
|
+
end
|
16
|
+
|
17
|
+
subject do
|
18
|
+
TestClass
|
19
|
+
end
|
20
|
+
|
21
|
+
before :each do
|
22
|
+
subject.stub(config: config)
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '::set' do
|
26
|
+
|
27
|
+
it 'returns true' do
|
28
|
+
subject.set('foo', 'baz').should == true
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'sets the configuration value' do
|
32
|
+
subject.set(:baz, 'bar')
|
33
|
+
subject.send(:config)[:baz].should == 'bar'
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'symbolizes strings into keys' do
|
37
|
+
subject.set('foo', 'bar')
|
38
|
+
subject.send(:config)[:foo].should == 'bar'
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '::get' do
|
44
|
+
|
45
|
+
it 'returns the value of the config key' do
|
46
|
+
config[:foo] = 'bar'
|
47
|
+
subject.get(:foo).should == 'bar'
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'symbolizes the key' do
|
51
|
+
config[:baz] = 'bar'
|
52
|
+
subject.get('baz').should == 'bar'
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
describe '::get!' do
|
58
|
+
|
59
|
+
it 'raises Kinetic::Errors::MissingConfigurationValue if the value is not present' do
|
60
|
+
expect do
|
61
|
+
subject.send(:get!, :nonexistentkey)
|
62
|
+
end.to raise_error(Kinetic::Errors::MissingConfigurationValue)
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'returns the value if the value is present and does not raise an exception' do
|
66
|
+
config[:existentkey] = 'foo'
|
67
|
+
expect do
|
68
|
+
subject.get!(:existentkey).should == 'foo'
|
69
|
+
end.not_to raise_error
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'symbolizes the key' do
|
73
|
+
config[:akey] = 'foo'
|
74
|
+
expect do
|
75
|
+
subject.get!(:akey)
|
76
|
+
end.not_to raise_error
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
describe '::logger' do
|
82
|
+
|
83
|
+
let(:logger) { double('Logger') }
|
84
|
+
|
85
|
+
after :each do
|
86
|
+
subject.instance_variable_set(:@logger, nil)
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'returns calls reopen_logger and returns the result if @logger is nil' do
|
90
|
+
subject.instance_variable_set(:@logger, nil)
|
91
|
+
subject.should_receive(:reopen_logger).and_return(logger)
|
92
|
+
subject.logger.should == logger
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'returns the existing logger if already set' do
|
96
|
+
subject.instance_variable_set(:@logger, logger)
|
97
|
+
subject.should_not_receive(:reopen_logger)
|
98
|
+
subject.logger.should == logger
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'rspec'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kinetic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jarod Reid
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-12-
|
11
|
+
date: 2013-12-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: confstruct
|
@@ -136,6 +136,34 @@ dependencies:
|
|
136
136
|
- - '>='
|
137
137
|
- !ruby/object:Gem::Version
|
138
138
|
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: rspec
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - '>='
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - '>='
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: yard
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - '>='
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - '>='
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
139
167
|
description: A powerful yet simple AMQP worker framework
|
140
168
|
email:
|
141
169
|
- jreid@voyst.com
|
@@ -144,6 +172,7 @@ extensions: []
|
|
144
172
|
extra_rdoc_files: []
|
145
173
|
files:
|
146
174
|
- .gitignore
|
175
|
+
- .travis.yml
|
147
176
|
- Gemfile
|
148
177
|
- LICENSE.txt
|
149
178
|
- README.md
|
@@ -154,11 +183,15 @@ files:
|
|
154
183
|
- lib/kinetic/base.rb
|
155
184
|
- lib/kinetic/cli.rb
|
156
185
|
- lib/kinetic/configuration.rb
|
186
|
+
- lib/kinetic/dsl.rb
|
157
187
|
- lib/kinetic/errors.rb
|
158
188
|
- lib/kinetic/master.rb
|
159
189
|
- lib/kinetic/publisher.rb
|
160
190
|
- lib/kinetic/version.rb
|
161
191
|
- lib/kinetic/worker.rb
|
192
|
+
- spec/base_spec.rb
|
193
|
+
- spec/dsl_spec.rb
|
194
|
+
- spec/spec_helper.rb
|
162
195
|
homepage: ''
|
163
196
|
licenses:
|
164
197
|
- MIT
|
@@ -183,5 +216,8 @@ rubygems_version: 2.0.3
|
|
183
216
|
signing_key:
|
184
217
|
specification_version: 4
|
185
218
|
summary: A powerful yet simple AMQP worker framework
|
186
|
-
test_files:
|
219
|
+
test_files:
|
220
|
+
- spec/base_spec.rb
|
221
|
+
- spec/dsl_spec.rb
|
222
|
+
- spec/spec_helper.rb
|
187
223
|
has_rdoc:
|