kinetic 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +36 -2
- data/lib/kinetic/base.rb +22 -4
- data/lib/kinetic/cli.rb +5 -4
- data/lib/kinetic/configuration.rb +5 -1
- data/lib/kinetic/dsl.rb +16 -2
- data/lib/kinetic/errors.rb +2 -1
- data/lib/kinetic/master.rb +60 -23
- data/lib/kinetic/serializers/json.rb +19 -0
- data/lib/kinetic/version.rb +1 -1
- data/lib/kinetic/worker.rb +6 -2
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f720debb00d4ab62ce3b3e8d7e4f7eaf422be407
|
4
|
+
data.tar.gz: a01b254919820ca788f0aa9ee8d42c06a7a7ca06
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6243b0b4ae9062a16aeda4bf06626088fd0cfaf80ed2de1b4a385f827ff491c063a69e168ef90cd46d35ba8f6b0ca0a1909a261c99670dd38634b730ede822d2
|
7
|
+
data.tar.gz: be6f7059f4fb70d65e6d1cab62357d9f082f783a249e3e7e40694d93de2a7cf71b182795d26d184d26588ab524d93d2aab6b0c4535723013a095cfcf4fbae731
|
data/README.md
CHANGED
@@ -14,19 +14,53 @@ Kinetic is an AMQP worker framework designed in vein of microframeworks such as
|
|
14
14
|
gem install kinetic
|
15
15
|
```
|
16
16
|
|
17
|
-
|
17
|
+
## Basic Usage
|
18
18
|
|
19
19
|
Kinetic follows a pattern similar to that of Sinatra, so most of the conventions should be recognizable.
|
20
20
|
|
21
21
|
|
22
22
|
Example: simple_worker.rb
|
23
23
|
|
24
|
+
```ruby
|
25
|
+
on 'message' do |message|
|
26
|
+
puts "Received #{message}"
|
27
|
+
end
|
24
28
|
```
|
29
|
+
|
30
|
+
This code will create a queue on the amqp server listening for messages on the simple_worker.rb.direct exchange with
|
31
|
+
the routing key 'message'. By default the message is expected to be in JSON format, however in the future other
|
32
|
+
serializers may be available.
|
33
|
+
|
34
|
+
## Configuration
|
35
|
+
Kinetic configuration follows a similar DSL to that of sinatra. Configuration can be set by calling the `set` method,
|
36
|
+
and can be retrieved by referencing the `config` variable or the `get` or `get!` methods.
|
37
|
+
|
38
|
+
### set
|
39
|
+
To set a configuration value use set:
|
40
|
+
|
41
|
+
```ruby
|
25
42
|
set :host, 'localhost'
|
26
|
-
set :port, 5678
|
27
43
|
|
28
44
|
on 'message' do |message|
|
29
45
|
puts "Received #{message}"
|
30
46
|
end
|
31
47
|
```
|
32
48
|
|
49
|
+
### Defaults
|
50
|
+
[Kinetic::Configuration](http://rdoc.info/github/fugufish/kinetic/master/Kinetic/Configuration)
|
51
|
+
|
52
|
+
|
53
|
+
|
54
|
+
## Changelog
|
55
|
+
|
56
|
+
### 0.0.3
|
57
|
+
* Fix bug where process shut down does not kill children
|
58
|
+
* Add ability to set process name for master and workers
|
59
|
+
* Add after fork block capability allowing code to be written to run after the worker forks
|
60
|
+
|
61
|
+
### 0.0.1
|
62
|
+
* Initial release
|
63
|
+
|
64
|
+
|
65
|
+
|
66
|
+
|
data/lib/kinetic/base.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# ruby dependencies
|
2
2
|
require 'logger'
|
3
3
|
require 'fileutils'
|
4
|
+
require 'yaml'
|
4
5
|
|
5
6
|
# external dependencies
|
6
7
|
require 'amqp'
|
@@ -8,6 +9,7 @@ require 'confstruct'
|
|
8
9
|
require 'kgio'
|
9
10
|
require 'active_support/core_ext/module/delegation'
|
10
11
|
require 'active_support/core_ext/hash/reverse_merge'
|
12
|
+
require 'active_support/inflections'
|
11
13
|
require 'awesome_print'
|
12
14
|
require 'awesome_print/core_ext/logger'
|
13
15
|
require 'msgpack'
|
@@ -24,7 +26,7 @@ module Kinetic
|
|
24
26
|
|
25
27
|
module Delegator
|
26
28
|
|
27
|
-
%w{on config logging set}.each { |m| delegate m.to_sym, to: :application }
|
29
|
+
%w{on config logging set config_file deserialize}.each { |m| delegate m.to_sym, to: :application }
|
28
30
|
|
29
31
|
def application
|
30
32
|
Application
|
@@ -38,22 +40,38 @@ module Kinetic
|
|
38
40
|
class << self
|
39
41
|
include Kinetic::DSL
|
40
42
|
|
43
|
+
|
44
|
+
def after_fork_procs
|
45
|
+
@after_fork_procs ||= []
|
46
|
+
end
|
47
|
+
|
41
48
|
def exchanges
|
42
49
|
@exchanges ||= {}
|
43
50
|
end
|
44
51
|
|
52
|
+
def deserialize(message)
|
53
|
+
@serializer.deserialize(message)
|
54
|
+
end
|
55
|
+
|
45
56
|
private
|
46
57
|
|
58
|
+
def set_serializer(serializer)
|
59
|
+
logger.debug "Loading serializer #{serializer}"
|
60
|
+
require "kinetic/serializers/#{serializer.downcase}"
|
61
|
+
@serializer = Object.const_get("Kinetic::Serializers::#{serializer}")
|
62
|
+
end
|
63
|
+
|
47
64
|
def direct
|
48
65
|
exchanges[:direct] ||= {}
|
49
66
|
end
|
50
67
|
|
68
|
+
end
|
51
69
|
|
52
|
-
|
53
|
-
|
70
|
+
def initialize
|
71
|
+
self.class.send(:set_serializer, config.serializer)
|
54
72
|
end
|
55
73
|
|
56
|
-
delegate :logger, :set, :get, :get!, :exchanges,
|
74
|
+
delegate :logger, :set, :get, :get!, :exchanges, :after_fork_procs, :deserialize, :config, to: :class
|
57
75
|
|
58
76
|
end
|
59
77
|
|
data/lib/kinetic/cli.rb
CHANGED
@@ -9,13 +9,14 @@ module Kinetic
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def self.parse
|
12
|
-
|
12
|
+
command = nil
|
13
|
+
if ARGV.empty? || (ARGV[0] && COMMANDS.include?(ARGV[0]))
|
13
14
|
command = ARGV.shift
|
14
15
|
else
|
15
16
|
puts PARSER.banner
|
16
|
-
exit 1
|
17
|
+
exit! 1
|
17
18
|
end
|
18
|
-
command
|
19
|
+
command || 'run'
|
19
20
|
end
|
20
21
|
|
21
22
|
end
|
@@ -24,7 +25,7 @@ module Kinetic
|
|
24
25
|
end
|
25
26
|
|
26
27
|
|
27
|
-
at_exit { Master.new(Application.new).send(CLI.parse) }
|
28
|
+
at_exit { Master.new(Application.new).send(CLI.parse) if $!.nil? }
|
28
29
|
|
29
30
|
end
|
30
31
|
|
@@ -7,11 +7,15 @@ module Kinetic
|
|
7
7
|
app_file: $0,
|
8
8
|
workers: 1,
|
9
9
|
log_file: STDOUT,
|
10
|
+
host: 'localhost',
|
11
|
+
port: 5672,
|
12
|
+
serializer: :JSON,
|
13
|
+
timeout: 1
|
10
14
|
}
|
11
15
|
|
12
16
|
def initialize
|
13
17
|
super
|
14
|
-
self.
|
18
|
+
self.deep_merge!(DEFAULTS)
|
15
19
|
end
|
16
20
|
|
17
21
|
end
|
data/lib/kinetic/dsl.rb
CHANGED
@@ -2,6 +2,11 @@ module Kinetic
|
|
2
2
|
|
3
3
|
module DSL
|
4
4
|
|
5
|
+
def after_fork(&block)
|
6
|
+
raise Kinetic::Errors::BlockMissing unless block_given?
|
7
|
+
after_fork_procs << block
|
8
|
+
end
|
9
|
+
|
5
10
|
# Sets a configuration value
|
6
11
|
#
|
7
12
|
# @param [Symbol] key the configuration key to set
|
@@ -9,10 +14,19 @@ module Kinetic
|
|
9
14
|
#
|
10
15
|
# @return [Boolean] returns true
|
11
16
|
def set(key, value)
|
17
|
+
logger.debug "Setting '#{key}' to '#{value}'"
|
12
18
|
config[key.to_sym] = value
|
13
19
|
true
|
14
20
|
end
|
15
21
|
|
22
|
+
# Set configuration file.
|
23
|
+
#
|
24
|
+
# @param [String] path to configuration file
|
25
|
+
def config_file(file)
|
26
|
+
set :config_file, File.expand_path(file)
|
27
|
+
config.configure(YAML.load(File.new(get!(:config_file), 'r')))
|
28
|
+
end
|
29
|
+
|
16
30
|
# Gets a configuration value
|
17
31
|
#
|
18
32
|
# @param [Symbol] key the key of the value to get
|
@@ -57,12 +71,12 @@ module Kinetic
|
|
57
71
|
@logger ||= reopen_logger
|
58
72
|
end
|
59
73
|
|
60
|
-
private
|
61
|
-
|
62
74
|
def config
|
63
75
|
@config ||= Kinetic::Configuration.new
|
64
76
|
end
|
65
77
|
|
78
|
+
protected
|
79
|
+
|
66
80
|
def reopen_logger
|
67
81
|
@logger = Logger.new(config.log_file)
|
68
82
|
end
|
data/lib/kinetic/errors.rb
CHANGED
@@ -3,7 +3,8 @@ module Kinetic
|
|
3
3
|
|
4
4
|
class KineticError < StandardError; end
|
5
5
|
|
6
|
-
class
|
6
|
+
class BlockMissing < KineticError; end
|
7
|
+
class NoSubscriberBlock < BlockMissing; end
|
7
8
|
class KeyMustBeString < KineticError; end
|
8
9
|
|
9
10
|
class MissingConfigurationValue < KineticError
|
data/lib/kinetic/master.rb
CHANGED
@@ -7,10 +7,10 @@ module Kinetic
|
|
7
7
|
WORKERS = {}
|
8
8
|
SIG_QUEUE = []
|
9
9
|
|
10
|
-
delegate :logger, :get, :get!, :set, to: :app
|
10
|
+
delegate :logger, :get, :get!, :set, :after_fork_procs, to: :app
|
11
11
|
|
12
12
|
|
13
|
-
attr_reader :app
|
13
|
+
attr_reader :app, :reexecpid
|
14
14
|
|
15
15
|
def initialize(app)
|
16
16
|
@app = app
|
@@ -24,6 +24,7 @@ module Kinetic
|
|
24
24
|
logger.info "Starting Kinetic #{Kinetic::VERSION} with PID #{Process.pid}"
|
25
25
|
logger.debug 'Configuration:'
|
26
26
|
logger.ap app.class.send(:config).to_hash
|
27
|
+
proc_name 'master'
|
27
28
|
write_pidfile!
|
28
29
|
initialize_self_pipe!
|
29
30
|
initialize_signal_traps!
|
@@ -38,22 +39,6 @@ module Kinetic
|
|
38
39
|
end
|
39
40
|
end
|
40
41
|
|
41
|
-
# Sets or gets the before fork block. This is called just before a worker is forked.
|
42
|
-
#
|
43
|
-
# @return [Proc] the before_block
|
44
|
-
def before_fork(&block)
|
45
|
-
@before_fork = block if block_given?
|
46
|
-
@before_fork || proc {}
|
47
|
-
end
|
48
|
-
|
49
|
-
# Sets or gets the after fork block. This is called immediately after a worker is forked.
|
50
|
-
#
|
51
|
-
# @return [Proc] the after_fork block
|
52
|
-
def after_fork(&block)
|
53
|
-
@after_fork = block if block_given?
|
54
|
-
@after_fork || proc {}
|
55
|
-
end
|
56
|
-
|
57
42
|
private
|
58
43
|
|
59
44
|
def master_sleep(time)
|
@@ -67,13 +52,11 @@ module Kinetic
|
|
67
52
|
when :QUIT
|
68
53
|
break
|
69
54
|
when :TERM, :INT
|
70
|
-
|
71
|
-
exit!(1)
|
55
|
+
stop(false)
|
72
56
|
break
|
73
57
|
else
|
74
58
|
master_sleep(0.5)
|
75
59
|
end while true
|
76
|
-
logger.warn "#{get(:name)} is going down!"
|
77
60
|
end
|
78
61
|
|
79
62
|
# Based on Unicorn's self-pipe
|
@@ -110,12 +93,13 @@ module Kinetic
|
|
110
93
|
WORKERS.value?(worker_nr) and next
|
111
94
|
#noinspection RubyArgCount
|
112
95
|
worker = Worker.new(worker_nr, @app)
|
113
|
-
logger.debug 'Calling before_fork block'
|
114
|
-
before_fork.call(self, worker)
|
115
96
|
logger.debug 'Forking worker'
|
116
97
|
parent = Process.pid
|
117
98
|
pid = fork do
|
118
99
|
begin
|
100
|
+
proc_name "worker-#{worker_nr}"
|
101
|
+
logger.debug 'Calling after fork procs'
|
102
|
+
after_fork_procs.each { |p| p.call }
|
119
103
|
#noinspection RubyArgCount
|
120
104
|
worker.run
|
121
105
|
rescue => e
|
@@ -125,6 +109,7 @@ module Kinetic
|
|
125
109
|
end
|
126
110
|
end
|
127
111
|
logger.debug "Worker #{worker_nr} started on #{pid}"
|
112
|
+
WORKERS[pid] = worker
|
128
113
|
end
|
129
114
|
rescue => e
|
130
115
|
logger.error(e)
|
@@ -148,7 +133,59 @@ module Kinetic
|
|
148
133
|
WORKERS.clear
|
149
134
|
|
150
135
|
after_fork.call(self, worker)
|
136
|
+
end
|
137
|
+
|
138
|
+
# Terminates all workers, but does not exit master process
|
139
|
+
def stop(graceful = true)
|
140
|
+
logger.warn graceful ? 'Process is shutting down gracefully' : 'Process is shutting down immediately!'
|
141
|
+
limit = Time.now + get!(:timeout)
|
142
|
+
until WORKERS.empty? || Time.now > limit
|
143
|
+
if graceful
|
144
|
+
soft_kill_each_worker(:QUIT)
|
145
|
+
else
|
146
|
+
kill_each_worker(:TERM)
|
147
|
+
end
|
148
|
+
sleep(0.1)
|
149
|
+
reap_all_workers
|
150
|
+
end
|
151
|
+
kill_each_worker(:KILL)
|
152
|
+
end
|
153
|
+
|
154
|
+
def kill_each_worker(signal)
|
155
|
+
WORKERS.keys.each { |wpid| kill_worker(signal, wpid) }
|
156
|
+
end
|
157
|
+
|
158
|
+
# delivers a signal to a worker and fails gracefully if the worker
|
159
|
+
# is no longer running.
|
160
|
+
def kill_worker(signal, wpid)
|
161
|
+
Process.kill(signal, wpid)
|
162
|
+
rescue Errno::ESRCH
|
163
|
+
worker = WORKERS.delete(wpid) and worker.close rescue nil
|
164
|
+
end
|
165
|
+
|
166
|
+
# reaps all unreaped workers
|
167
|
+
def reap_all_workers
|
168
|
+
|
169
|
+
begin
|
170
|
+
wpid, status = Process.waitpid2(-1, Process::WNOHANG)
|
171
|
+
wpid or return
|
172
|
+
if reexec_pid == wpid
|
173
|
+
logger.error "reaped #{status.inspect} exec()-ed"
|
174
|
+
self.reexec_pid = 0
|
175
|
+
self.pid = pid.chomp('.oldbin') if pid
|
176
|
+
proc_name 'master'
|
177
|
+
else
|
178
|
+
worker = WORKERS.delete(wpid) and worker.close rescue nil
|
179
|
+
m = "reaped #{status.inspect} worker=#{worker.nr rescue 'unknown'}"
|
180
|
+
status.success? ? logger.info(m) : logger.error(m)
|
181
|
+
end
|
182
|
+
rescue Errno::ECHILD
|
183
|
+
break
|
184
|
+
end while true
|
185
|
+
end
|
151
186
|
|
187
|
+
def proc_name(tag)
|
188
|
+
$0 = "#{get!(:name)} [#{tag}]"
|
152
189
|
end
|
153
190
|
|
154
191
|
end
|
data/lib/kinetic/version.rb
CHANGED
data/lib/kinetic/worker.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Kinetic
|
2
2
|
class Worker
|
3
3
|
|
4
|
-
delegate :logger, :get, :get!, :set, to: :app
|
4
|
+
delegate :logger, :get, :get!, :set, :deserialize, to: :app
|
5
5
|
|
6
6
|
attr_reader :id, :app, :channel, :exchanges
|
7
7
|
|
@@ -61,7 +61,11 @@ module Kinetic
|
|
61
61
|
q.bind(exchanges[name], routing_key: queue)
|
62
62
|
logger.debug " Subscribing to messages for '#{queue}'"
|
63
63
|
q.subscribe do |meta, payload|
|
64
|
-
|
64
|
+
begin
|
65
|
+
block.call(deserialize(payload))
|
66
|
+
rescue Exception => e
|
67
|
+
logger.error e
|
68
|
+
end
|
65
69
|
end
|
66
70
|
end
|
67
71
|
end
|
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.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jarod Reid
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2014-01-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: confstruct
|
@@ -187,6 +187,7 @@ files:
|
|
187
187
|
- lib/kinetic/errors.rb
|
188
188
|
- lib/kinetic/master.rb
|
189
189
|
- lib/kinetic/publisher.rb
|
190
|
+
- lib/kinetic/serializers/json.rb
|
190
191
|
- lib/kinetic/version.rb
|
191
192
|
- lib/kinetic/worker.rb
|
192
193
|
- spec/base_spec.rb
|