pastry 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.
- data/README.rdoc +29 -0
- data/bin/pastry +28 -0
- data/lib/pastry.rb +111 -0
- metadata +78 -0
data/README.rdoc
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
= Thin Pastry flakes
|
2
|
+
|
3
|
+
This gem is just scratching an itch, that I could not run a master and fork several thin workers
|
4
|
+
listening on the same parent socket.
|
5
|
+
|
6
|
+
Why fork thin ? So I can make use of the multiple cores more effectively.
|
7
|
+
|
8
|
+
== Warning
|
9
|
+
|
10
|
+
It's fairly pre-alpha, so use at your own peril
|
11
|
+
|
12
|
+
== Usage
|
13
|
+
|
14
|
+
pastry -R myapp.ru -n 2 -P /tmp/myapp.pid -l /tmp/myapp.log -a 127.0.0.1 -p 3000 -d
|
15
|
+
|
16
|
+
kill -TERM `cat /tmp/myapp.pid`
|
17
|
+
|
18
|
+
== Dependencies
|
19
|
+
|
20
|
+
* requires patched eventmachine in http://github.com/deepfryed/eventmachine
|
21
|
+
|
22
|
+
== TODO
|
23
|
+
|
24
|
+
* graceful stop / restart
|
25
|
+
* increase or decrease worker count
|
26
|
+
|
27
|
+
== License
|
28
|
+
|
29
|
+
{Creative Commons Attribution - CC BY}[http://creativecommons.org/licenses/by/3.0]
|
data/bin/pastry
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'pastry'
|
4
|
+
require 'optparse'
|
5
|
+
|
6
|
+
options = {}
|
7
|
+
parser = OptionParser.new do |opts|
|
8
|
+
opts.banner = "pastry [options]"
|
9
|
+
|
10
|
+
opts.on('-n', '--servers number', 'worker count') {|value| options[:workers] = value }
|
11
|
+
opts.on('-E', '--environment name', 'rack environment') {|value| options[:env] = value }
|
12
|
+
opts.on('-R', '--rackup file', 'rackup file') {|value| options[:rackup] = value }
|
13
|
+
opts.on('-a', '--address name', 'bind ip/host') {|value| options[:host] = value }
|
14
|
+
opts.on('-p', '--port name', 'bind port') {|value| options[:port] = value }
|
15
|
+
opts.on('-s', '--socket file', 'unix socket') {|value| options[:unix] = value }
|
16
|
+
opts.on('-d', '--[no-]daemon', 'daemonize') {|value| options[:daemonize] = value }
|
17
|
+
opts.on('-l', '--logfile file', 'logfile') {|value| options[:logfile] = value }
|
18
|
+
opts.on('-P', '--pidfile file', 'pidfile') {|value| options[:pidfile] = value }
|
19
|
+
end
|
20
|
+
|
21
|
+
parser.parse!
|
22
|
+
|
23
|
+
app = Rack::Builder.parse_file(options.delete(:rackup) || 'config.ru').first
|
24
|
+
size = options.delete(:workers) || 2
|
25
|
+
env = options.delete(:env) || 'development'
|
26
|
+
|
27
|
+
ENV['RACK_ENV'] = env
|
28
|
+
Pastry.new(size, app, options).start
|
data/lib/pastry.rb
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'logger'
|
3
|
+
require 'socket'
|
4
|
+
require 'thin'
|
5
|
+
|
6
|
+
class Pastry
|
7
|
+
attr_reader :pool, :unix, :host, :port, :pidfile, :logfile, :daemon
|
8
|
+
|
9
|
+
def initialize pool, app, options = {}
|
10
|
+
@pool = pool
|
11
|
+
@app = app
|
12
|
+
@host = options.fetch :host, '127.0.0.1'
|
13
|
+
@port = options.fetch :port, 3000
|
14
|
+
@unix = options.fetch :socket, nil
|
15
|
+
@queue = options.fetch :queue, 1024
|
16
|
+
@logfile = options.fetch :logfile, nil
|
17
|
+
@daemon = options.fetch :daemonize, false
|
18
|
+
@pidfile = options.fetch :pidfile, '/tmp/pastry.pid'
|
19
|
+
# TODO: validation
|
20
|
+
end
|
21
|
+
|
22
|
+
def start
|
23
|
+
ensure_not_running!
|
24
|
+
Process.daemon if daemon
|
25
|
+
start!
|
26
|
+
end
|
27
|
+
|
28
|
+
def ensure_not_running!
|
29
|
+
if File.exists?(pidfile) && pid = File.read(pidfile).to_i
|
30
|
+
running = Process.kill(0, pid) rescue nil
|
31
|
+
raise "already running with pid #{pid}" if running
|
32
|
+
FileUtils.rm_f(pidfile)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def create_pidfile
|
37
|
+
File.open(pidfile, 'w') {|fh| fh.write(Process.pid)}
|
38
|
+
end
|
39
|
+
|
40
|
+
def motd
|
41
|
+
"starting #{@app} pastry with #{pool} flakes listening on #{unix ? 'socket %s ' % unix : 'port %d' % port}"
|
42
|
+
end
|
43
|
+
|
44
|
+
def start!
|
45
|
+
create_pidfile
|
46
|
+
server = unix ? UnixServer.new(unix) : TCPServer.new(host, port)
|
47
|
+
|
48
|
+
server.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true) unless unix
|
49
|
+
server.fcntl(Fcntl::F_SETFL, server.fcntl(Fcntl::F_GETFL) | Fcntl::O_NONBLOCK)
|
50
|
+
|
51
|
+
server.listen(@queue)
|
52
|
+
server.extend(PastryServer)
|
53
|
+
|
54
|
+
@running = true
|
55
|
+
server.app = @app
|
56
|
+
logger = Logger.new(logfile || daemon ? '/tmp/pastry.log' : $stdout, 0)
|
57
|
+
pids = pool.times.map { run(server) }
|
58
|
+
|
59
|
+
logger.info motd
|
60
|
+
Signal.trap('CHLD') do
|
61
|
+
unless @running
|
62
|
+
died = pids.reject {|pid| Process.kill(0, pid) rescue nil}
|
63
|
+
pids -= died
|
64
|
+
died.each do |pid|
|
65
|
+
logger.info "process #{pid} died, starting a new one"
|
66
|
+
pids << run(server)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
at_exit { FileUtils.rm_f(pidfile) }
|
72
|
+
|
73
|
+
%w(INT TERM HUP).each do |signal|
|
74
|
+
Signal.trap(signal) do
|
75
|
+
@running = false
|
76
|
+
logger.info "caught #{signal}, closing time for the bakery -- no more pastries!"
|
77
|
+
pids.each {|pid| Process.kill(signal, pid) }
|
78
|
+
exit
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
Process.waitall rescue nil
|
83
|
+
end
|
84
|
+
|
85
|
+
def run server
|
86
|
+
fork { EM.run { Backend.new.start(server) } }
|
87
|
+
end
|
88
|
+
|
89
|
+
module PastryServer
|
90
|
+
attr_accessor :app
|
91
|
+
end
|
92
|
+
|
93
|
+
class Backend < Thin::Backends::Base
|
94
|
+
def start server
|
95
|
+
@stopping = false
|
96
|
+
@running = true
|
97
|
+
@server = server
|
98
|
+
|
99
|
+
config
|
100
|
+
trap_signals!
|
101
|
+
|
102
|
+
EM.attach_server_socket(server, Thin::Connection, &method(:initialize_connection))
|
103
|
+
end
|
104
|
+
|
105
|
+
def trap_signals!
|
106
|
+
%w(INT TERM HUP CHLD).each do |signal|
|
107
|
+
Signal.trap(signal) { exit }
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end # Backend
|
111
|
+
end # Pastry
|
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pastry
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Bharanee Rathna
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-11-26 00:00:00 +11:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: thin
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
version: "0"
|
31
|
+
type: :runtime
|
32
|
+
version_requirements: *id001
|
33
|
+
description: thin runner that forks and supports binding to single socket
|
34
|
+
email: deepfryed@gmail.com
|
35
|
+
executables:
|
36
|
+
- pastry
|
37
|
+
extensions: []
|
38
|
+
|
39
|
+
extra_rdoc_files:
|
40
|
+
- README.rdoc
|
41
|
+
files:
|
42
|
+
- lib/pastry.rb
|
43
|
+
- README.rdoc
|
44
|
+
- bin/pastry
|
45
|
+
has_rdoc: true
|
46
|
+
homepage: http://github.com/deepfryed/pastry
|
47
|
+
licenses: []
|
48
|
+
|
49
|
+
post_install_message:
|
50
|
+
rdoc_options: []
|
51
|
+
|
52
|
+
require_paths:
|
53
|
+
- lib
|
54
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
segments:
|
60
|
+
- 0
|
61
|
+
version: "0"
|
62
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
segments:
|
68
|
+
- 0
|
69
|
+
version: "0"
|
70
|
+
requirements: []
|
71
|
+
|
72
|
+
rubyforge_project:
|
73
|
+
rubygems_version: 1.3.7
|
74
|
+
signing_key:
|
75
|
+
specification_version: 3
|
76
|
+
summary: thin runner that supports forking
|
77
|
+
test_files: []
|
78
|
+
|