kinetic 0.0.2 → 0.0.3
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/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
|