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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b7a08f594cae0d5c01c00f340386df7c7788a745
4
- data.tar.gz: 280fe43699041a8a233b455887d795b6c47e42f8
3
+ metadata.gz: f720debb00d4ab62ce3b3e8d7e4f7eaf422be407
4
+ data.tar.gz: a01b254919820ca788f0aa9ee8d42c06a7a7ca06
5
5
  SHA512:
6
- metadata.gz: 65871da55313e959082177229600037b1ffdf5e94b1886bebb525e9099effe4b249fb7b581f54d45ed908fa68eac60e0ce7e44841e96f0738d7ae1fc731a8c3f
7
- data.tar.gz: e8452cb376cab92b21796269deff0ac5ab24306099675eca52eeb9d64071cced80ba649e0b6399748f77d113b13e6bdd7f7aecbcf1a4bc1cbf386faa30be73ff
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
- ### Basic Usage
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, to: :class
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
- if ARGV[0] && COMMANDS.include?(ARGV[0])
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.reverse_merge!(DEFAULTS)
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
@@ -3,7 +3,8 @@ module Kinetic
3
3
 
4
4
  class KineticError < StandardError; end
5
5
 
6
- class NoSubscriberBlock < KineticError; end
6
+ class BlockMissing < KineticError; end
7
+ class NoSubscriberBlock < BlockMissing; end
7
8
  class KeyMustBeString < KineticError; end
8
9
 
9
10
  class MissingConfigurationValue < KineticError
@@ -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
- logger.warn "#{get(:name)} is shutting down immediately!"
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
@@ -0,0 +1,19 @@
1
+ require 'json'
2
+
3
+ module Kinetic
4
+ module Serializers
5
+
6
+ module JSON
7
+
8
+ def self.serialize(message)
9
+ ::JSON.generate(message)
10
+ end
11
+
12
+ def self.deserialize(message)
13
+ ::JSON.parse(message)
14
+ end
15
+
16
+ end
17
+
18
+ end
19
+ end
@@ -1,3 +1,3 @@
1
1
  module Kinetic
2
- VERSION = '0.0.2'
2
+ VERSION = '0.0.3'
3
3
  end
@@ -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
- block.call(MessagePack.unpack(payload))
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.2
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: 2013-12-17 00:00:00.000000000 Z
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