daemonic 0.0.2 → 0.1.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 +5 -1
- data/.travis.yml +8 -0
- data/{LICENSE.txt → MIT-LICENSE.txt} +1 -1
- data/README.md +108 -78
- data/Rakefile +11 -4
- data/daemonic.gemspec +7 -4
- data/examples/init-d.sh +37 -0
- data/examples/rss +41 -0
- data/features/support/env.rb +1 -0
- data/features/worker.feature +43 -0
- data/lib/daemonic.rb +40 -8
- data/lib/daemonic/cli.rb +147 -40
- data/lib/daemonic/daemon.rb +126 -0
- data/lib/daemonic/pool.rb +43 -82
- data/lib/daemonic/producer.rb +49 -0
- data/lib/daemonic/version.rb +1 -1
- metadata +68 -31
- data/bin/daemonic +0 -6
- data/lib/daemonic/configuration.rb +0 -110
- data/lib/daemonic/logging.rb +0 -14
- data/lib/daemonic/master.rb +0 -119
- data/lib/daemonic/pidfile.rb +0 -64
- data/lib/daemonic/worker.rb +0 -93
- data/test/config +0 -6
- data/test/crappy_daemon.rb +0 -1
- data/test/integration_test.rb +0 -155
- data/test/test_daemon.rb +0 -10
@@ -0,0 +1,49 @@
|
|
1
|
+
module Daemonic
|
2
|
+
class Producer
|
3
|
+
|
4
|
+
attr_reader :worker, :queue, :concurrency, :options
|
5
|
+
|
6
|
+
def initialize(worker, options)
|
7
|
+
@worker = worker
|
8
|
+
@options = options
|
9
|
+
@concurrency = options.fetch(:concurrency) { 4 }
|
10
|
+
|
11
|
+
@queue = SizedQueue.new(concurrency) { concurrency + 1 }
|
12
|
+
@running = true
|
13
|
+
end
|
14
|
+
|
15
|
+
def run
|
16
|
+
logger.info "Starting producer with #{concurrency} consumer threads."
|
17
|
+
|
18
|
+
Signal.trap("INT") { stop }
|
19
|
+
Signal.trap("TERM") { stop }
|
20
|
+
|
21
|
+
pool = Pool.new(concurrency, worker, logger)
|
22
|
+
|
23
|
+
producer = Thread.new do
|
24
|
+
while @running
|
25
|
+
worker.produce(pool)
|
26
|
+
Thread.pass
|
27
|
+
end
|
28
|
+
logger.info { "Producer has been shut down. Stopping the thread pool" }
|
29
|
+
pool.stop
|
30
|
+
end
|
31
|
+
|
32
|
+
producer.join
|
33
|
+
|
34
|
+
logger.info { "Shutting down" }
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
def stop
|
39
|
+
@running = false
|
40
|
+
end
|
41
|
+
|
42
|
+
def logger
|
43
|
+
@logger ||= Logger.new(@options[:log] || STDOUT).tap { |logger|
|
44
|
+
logger.level = @options[:log_level] || Logger::INFO
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
data/lib/daemonic/version.rb
CHANGED
metadata
CHANGED
@@ -1,71 +1,109 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: daemonic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- iain
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-07-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - ~>
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '1.
|
19
|
+
version: '1.6'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - ~>
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '1.
|
26
|
+
version: '1.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 3.0.0
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 3.0.0
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: rake
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
30
44
|
requirements:
|
31
|
-
- -
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: cucumber
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: aruba
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
32
74
|
- !ruby/object:Gem::Version
|
33
75
|
version: '0'
|
34
76
|
type: :development
|
35
77
|
prerelease: false
|
36
78
|
version_requirements: !ruby/object:Gem::Requirement
|
37
79
|
requirements:
|
38
|
-
- -
|
80
|
+
- - ">="
|
39
81
|
- !ruby/object:Gem::Version
|
40
82
|
version: '0'
|
41
|
-
description:
|
83
|
+
description: Daemonic makes multi-threaded daemons easy.
|
42
84
|
email:
|
43
85
|
- iain@iain.nl
|
44
|
-
executables:
|
45
|
-
- daemonic
|
86
|
+
executables: []
|
46
87
|
extensions: []
|
47
88
|
extra_rdoc_files: []
|
48
89
|
files:
|
49
|
-
- .gitignore
|
90
|
+
- ".gitignore"
|
91
|
+
- ".travis.yml"
|
50
92
|
- Gemfile
|
51
|
-
- LICENSE.txt
|
93
|
+
- MIT-LICENSE.txt
|
52
94
|
- README.md
|
53
95
|
- Rakefile
|
54
|
-
- bin/daemonic
|
55
96
|
- daemonic.gemspec
|
97
|
+
- examples/init-d.sh
|
98
|
+
- examples/rss
|
99
|
+
- features/support/env.rb
|
100
|
+
- features/worker.feature
|
56
101
|
- lib/daemonic.rb
|
57
102
|
- lib/daemonic/cli.rb
|
58
|
-
- lib/daemonic/
|
59
|
-
- lib/daemonic/logging.rb
|
60
|
-
- lib/daemonic/master.rb
|
61
|
-
- lib/daemonic/pidfile.rb
|
103
|
+
- lib/daemonic/daemon.rb
|
62
104
|
- lib/daemonic/pool.rb
|
105
|
+
- lib/daemonic/producer.rb
|
63
106
|
- lib/daemonic/version.rb
|
64
|
-
- lib/daemonic/worker.rb
|
65
|
-
- test/config
|
66
|
-
- test/crappy_daemon.rb
|
67
|
-
- test/integration_test.rb
|
68
|
-
- test/test_daemon.rb
|
69
107
|
homepage: https://github.com/yourkarma/daemonic
|
70
108
|
licenses:
|
71
109
|
- MIT
|
@@ -76,22 +114,21 @@ require_paths:
|
|
76
114
|
- lib
|
77
115
|
required_ruby_version: !ruby/object:Gem::Requirement
|
78
116
|
requirements:
|
79
|
-
- -
|
117
|
+
- - ">="
|
80
118
|
- !ruby/object:Gem::Version
|
81
119
|
version: '0'
|
82
120
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
121
|
requirements:
|
84
|
-
- -
|
122
|
+
- - ">="
|
85
123
|
- !ruby/object:Gem::Version
|
86
124
|
version: '0'
|
87
125
|
requirements: []
|
88
126
|
rubyforge_project:
|
89
|
-
rubygems_version: 2.
|
127
|
+
rubygems_version: 2.2.2
|
90
128
|
signing_key:
|
91
129
|
specification_version: 4
|
92
|
-
summary:
|
130
|
+
summary: Daemonic makes multi-threaded daemons easy.
|
93
131
|
test_files:
|
94
|
-
-
|
95
|
-
-
|
96
|
-
|
97
|
-
- test/test_daemon.rb
|
132
|
+
- features/support/env.rb
|
133
|
+
- features/worker.feature
|
134
|
+
has_rdoc:
|
data/bin/daemonic
DELETED
@@ -1,110 +0,0 @@
|
|
1
|
-
require 'logger'
|
2
|
-
require 'shellwords'
|
3
|
-
|
4
|
-
module Daemonic
|
5
|
-
class Configuration
|
6
|
-
|
7
|
-
Invalid = Class.new(ArgumentError)
|
8
|
-
|
9
|
-
attr_reader :eventual_config, :given_options
|
10
|
-
|
11
|
-
def initialize(given_options, pwd)
|
12
|
-
@given_options = given_options
|
13
|
-
@pwd = pwd
|
14
|
-
@eventual_config = {}
|
15
|
-
end
|
16
|
-
|
17
|
-
def command
|
18
|
-
self[:command]
|
19
|
-
end
|
20
|
-
|
21
|
-
def daemonize?
|
22
|
-
self[:daemonize]
|
23
|
-
end
|
24
|
-
|
25
|
-
def config_file
|
26
|
-
self[:config_file]
|
27
|
-
end
|
28
|
-
|
29
|
-
def workers
|
30
|
-
Integer(self[:workers] || "1")
|
31
|
-
end
|
32
|
-
|
33
|
-
def reload
|
34
|
-
@eventual_config = {}
|
35
|
-
@logger = nil
|
36
|
-
load_options defaults
|
37
|
-
load_options given_options
|
38
|
-
load_options config_file_options if config_file
|
39
|
-
load_options given_options
|
40
|
-
logger.debug { to_h.inspect }
|
41
|
-
validate
|
42
|
-
end
|
43
|
-
|
44
|
-
def working_dir
|
45
|
-
self[:working_dir]
|
46
|
-
end
|
47
|
-
|
48
|
-
def program_name
|
49
|
-
self[:program_name] || File.basename(working_dir)
|
50
|
-
end
|
51
|
-
|
52
|
-
def pidfile
|
53
|
-
self[:pidfile] || File.join(working_dir, "tmp/#{program_name}.pid")
|
54
|
-
end
|
55
|
-
|
56
|
-
def logfile
|
57
|
-
self[:logfile] || STDOUT
|
58
|
-
end
|
59
|
-
|
60
|
-
def logger
|
61
|
-
@logger ||= ::Logger.new(logfile).tap { |logger|
|
62
|
-
logger.formatter = proc { |severity, datetime, progname, msg|
|
63
|
-
"[#{severity}] [#{datetime}] [#{program_name}] [#{Process.pid}] #{msg}\n"
|
64
|
-
}
|
65
|
-
logger.level = ::Logger.const_get(loglevel)
|
66
|
-
}
|
67
|
-
end
|
68
|
-
|
69
|
-
def to_h
|
70
|
-
eventual_config
|
71
|
-
end
|
72
|
-
|
73
|
-
def loglevel
|
74
|
-
(self[:loglevel] || "INFO").to_s.upcase
|
75
|
-
end
|
76
|
-
|
77
|
-
protected
|
78
|
-
|
79
|
-
def [](key)
|
80
|
-
eventual_config[key.to_s]
|
81
|
-
end
|
82
|
-
|
83
|
-
def []=(key, value)
|
84
|
-
eventual_config[key.to_s] = value
|
85
|
-
end
|
86
|
-
|
87
|
-
private
|
88
|
-
|
89
|
-
def load_options(options)
|
90
|
-
options.each do |key, value|
|
91
|
-
self[key] = value
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
def config_file_options
|
96
|
-
contents = File.open(config_file, 'r:utf-8').read
|
97
|
-
args = Shellwords.split(contents)
|
98
|
-
CLI.parse(args)
|
99
|
-
end
|
100
|
-
|
101
|
-
def validate
|
102
|
-
raise Invalid, "No command specified" if command.nil?
|
103
|
-
end
|
104
|
-
|
105
|
-
def defaults
|
106
|
-
{ :workers => 1, :working_dir => @pwd }
|
107
|
-
end
|
108
|
-
|
109
|
-
end
|
110
|
-
end
|
data/lib/daemonic/logging.rb
DELETED
data/lib/daemonic/master.rb
DELETED
@@ -1,119 +0,0 @@
|
|
1
|
-
require 'thread'
|
2
|
-
require 'daemonic/version'
|
3
|
-
require 'daemonic/logging'
|
4
|
-
require 'daemonic/pidfile'
|
5
|
-
|
6
|
-
Thread.abort_on_exception = true
|
7
|
-
|
8
|
-
module Daemonic
|
9
|
-
class Master
|
10
|
-
include Logging
|
11
|
-
|
12
|
-
attr_reader :config, :pidfile
|
13
|
-
|
14
|
-
def initialize(config)
|
15
|
-
@config = config
|
16
|
-
@shutting_down = false
|
17
|
-
@current_status = nil
|
18
|
-
config.reload
|
19
|
-
update_program_name("booting")
|
20
|
-
@pidfile = Pidfile.new(
|
21
|
-
pid: Process.pid,
|
22
|
-
config: config,
|
23
|
-
)
|
24
|
-
end
|
25
|
-
|
26
|
-
def start
|
27
|
-
Dir.chdir(config.working_dir)
|
28
|
-
write_pidfile
|
29
|
-
at_exit { clean_pidfile }
|
30
|
-
trap_signals
|
31
|
-
start_workers
|
32
|
-
update_program_name(nil)
|
33
|
-
wait_until_done
|
34
|
-
update_program_name
|
35
|
-
fatal "Shutting down master"
|
36
|
-
end
|
37
|
-
|
38
|
-
def restart
|
39
|
-
@monitor = false
|
40
|
-
update_program_name("restarting")
|
41
|
-
pool.restart { update_program_name }
|
42
|
-
update_program_name(nil)
|
43
|
-
@monitor = true
|
44
|
-
end
|
45
|
-
|
46
|
-
def stop
|
47
|
-
@monitor = false
|
48
|
-
update_program_name("shutting down")
|
49
|
-
pool.stop { update_program_name }
|
50
|
-
@shutting_down = true
|
51
|
-
end
|
52
|
-
|
53
|
-
def hup
|
54
|
-
config.reload
|
55
|
-
pool.start
|
56
|
-
pool.hup
|
57
|
-
end
|
58
|
-
|
59
|
-
private
|
60
|
-
|
61
|
-
def write_pidfile
|
62
|
-
pidfile.write
|
63
|
-
end
|
64
|
-
|
65
|
-
def clean_pidfile
|
66
|
-
pidfile.clean
|
67
|
-
end
|
68
|
-
|
69
|
-
def start_workers
|
70
|
-
pool.start { update_program_name }
|
71
|
-
@monitor = true
|
72
|
-
end
|
73
|
-
|
74
|
-
def pool
|
75
|
-
@pool ||= Pool.new(config)
|
76
|
-
end
|
77
|
-
|
78
|
-
def trap_signals
|
79
|
-
trap("USR2") { Thread.new { info "USR2 received!"; restart } }
|
80
|
-
trap("TERM") { Thread.new { info "TERM received!"; stop } }
|
81
|
-
trap("INT") { Thread.new { info "INT received!"; stop } }
|
82
|
-
trap("HUP") { Thread.new { info "HUP received!"; hup } }
|
83
|
-
trap("TTIN") { Thread.new { info "TTIN received!"; ttin } }
|
84
|
-
trap("TTOU") { Thread.new { info "TTOU received!"; ttou } }
|
85
|
-
end
|
86
|
-
|
87
|
-
def ttin
|
88
|
-
pool.increase!
|
89
|
-
end
|
90
|
-
|
91
|
-
def ttou
|
92
|
-
pool.decrease!
|
93
|
-
end
|
94
|
-
|
95
|
-
def wait_until_done
|
96
|
-
while keep_running?
|
97
|
-
pool.monitor if @monitor
|
98
|
-
update_program_name
|
99
|
-
sleep 0.1
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
def keep_running?
|
104
|
-
not @shutting_down
|
105
|
-
end
|
106
|
-
|
107
|
-
def update_program_name(additional = :unchanged)
|
108
|
-
@current_status = additional if additional != :unchanged
|
109
|
-
base = "#{config.program_name} master [version #{VERSION}; #{pool.count}/#{pool.desired_workers} workers]"
|
110
|
-
if @current_status
|
111
|
-
$PROGRAM_NAME = "#{base} (#{@current_status})"
|
112
|
-
else
|
113
|
-
$PROGRAM_NAME = base
|
114
|
-
end
|
115
|
-
$PROGRAM_NAME
|
116
|
-
end
|
117
|
-
|
118
|
-
end
|
119
|
-
end
|