serverengine 2.0.0pre1-x64-mingw32
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/.rspec +2 -0
- data/.travis.yml +20 -0
- data/Changelog +122 -0
- data/Gemfile +2 -0
- data/LICENSE +202 -0
- data/NOTICE +3 -0
- data/README.md +514 -0
- data/Rakefile +26 -0
- data/appveyor.yml +24 -0
- data/examples/server.rb +138 -0
- data/examples/spawn_worker_script.rb +38 -0
- data/lib/serverengine.rb +46 -0
- data/lib/serverengine/blocking_flag.rb +77 -0
- data/lib/serverengine/command_sender.rb +89 -0
- data/lib/serverengine/config_loader.rb +82 -0
- data/lib/serverengine/daemon.rb +233 -0
- data/lib/serverengine/daemon_logger.rb +135 -0
- data/lib/serverengine/embedded_server.rb +67 -0
- data/lib/serverengine/multi_process_server.rb +155 -0
- data/lib/serverengine/multi_spawn_server.rb +95 -0
- data/lib/serverengine/multi_thread_server.rb +80 -0
- data/lib/serverengine/multi_worker_server.rb +150 -0
- data/lib/serverengine/privilege.rb +57 -0
- data/lib/serverengine/process_manager.rb +508 -0
- data/lib/serverengine/server.rb +178 -0
- data/lib/serverengine/signal_thread.rb +116 -0
- data/lib/serverengine/signals.rb +31 -0
- data/lib/serverengine/socket_manager.rb +171 -0
- data/lib/serverengine/socket_manager_unix.rb +98 -0
- data/lib/serverengine/socket_manager_win.rb +154 -0
- data/lib/serverengine/supervisor.rb +313 -0
- data/lib/serverengine/utils.rb +62 -0
- data/lib/serverengine/version.rb +3 -0
- data/lib/serverengine/winsock.rb +128 -0
- data/lib/serverengine/worker.rb +81 -0
- data/serverengine.gemspec +37 -0
- data/spec/blocking_flag_spec.rb +59 -0
- data/spec/daemon_logger_spec.rb +175 -0
- data/spec/daemon_spec.rb +169 -0
- data/spec/multi_process_server_spec.rb +113 -0
- data/spec/server_worker_context.rb +232 -0
- data/spec/signal_thread_spec.rb +94 -0
- data/spec/socket_manager_spec.rb +119 -0
- data/spec/spec_helper.rb +19 -0
- data/spec/supervisor_spec.rb +215 -0
- metadata +184 -0
data/Rakefile
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
|
4
|
+
require 'rake/testtask'
|
5
|
+
require 'rake/clean'
|
6
|
+
|
7
|
+
require 'rspec/core/rake_task'
|
8
|
+
|
9
|
+
RSpec::Core::RakeTask.new(:spec)
|
10
|
+
task :default => [:spec, :build]
|
11
|
+
|
12
|
+
# 1. update Changelog and lib/serverengine/version.rb
|
13
|
+
# 2. bundle && bundle exec rake build:all
|
14
|
+
# 3. release 3 packages built on pkg/ directory
|
15
|
+
namespace :build do
|
16
|
+
desc 'Build gems for all platforms'
|
17
|
+
task :all do
|
18
|
+
Bundler.with_clean_env do
|
19
|
+
%w[ruby x86-mingw32 x64-mingw32].each do |name|
|
20
|
+
ENV['GEM_BUILD_FAKE_PLATFORM'] = name
|
21
|
+
Rake::Task["build"].execute
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
data/appveyor.yml
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
---
|
2
|
+
install:
|
3
|
+
- SET PATH=C:\Ruby%ruby_version%\bin;%PATH%
|
4
|
+
- ruby --version
|
5
|
+
- gem --version
|
6
|
+
- bundle install
|
7
|
+
build: off
|
8
|
+
test_script:
|
9
|
+
- bundle exec rake -rdevkit
|
10
|
+
|
11
|
+
environment:
|
12
|
+
matrix:
|
13
|
+
- ruby_version: "23-x64"
|
14
|
+
devkit: C:\Ruby23-x64\DevKit
|
15
|
+
- ruby_version: "23"
|
16
|
+
devkit: C:\Ruby23\DevKit
|
17
|
+
- ruby_version: "22-x64"
|
18
|
+
devkit: C:\Ruby23-x64\DevKit
|
19
|
+
- ruby_version: "22"
|
20
|
+
devkit: C:\Ruby23\DevKit
|
21
|
+
- ruby_version: "21-x64"
|
22
|
+
devkit: C:\Ruby23-x64\DevKit
|
23
|
+
- ruby_version: "21"
|
24
|
+
devkit: C:\Ruby23\DevKit
|
data/examples/server.rb
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
require 'serverengine'
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'optparse'
|
5
|
+
|
6
|
+
# This is a script to run ServerEngine and SocketManager as a real process.
|
7
|
+
# bundle exec ruby example/server.rb [-t TYPE] [-w NUM]
|
8
|
+
# available type of workers are: embedded(default), process, thread, spawn
|
9
|
+
|
10
|
+
foreground = false
|
11
|
+
supervisor = false
|
12
|
+
worker_type = nil
|
13
|
+
workers = 4
|
14
|
+
exit_with_code = nil
|
15
|
+
exit_at_seconds = 5
|
16
|
+
exit_at_random = false
|
17
|
+
stop_immediately_at_exit = false
|
18
|
+
unrecoverable_exit_codes = []
|
19
|
+
|
20
|
+
opt = OptionParser.new
|
21
|
+
opt.on('-f'){ foreground = true }
|
22
|
+
opt.on('-x'){ supervisor = true }
|
23
|
+
opt.on('-t TYPE'){|v| worker_type = v }
|
24
|
+
opt.on('-w NUM'){|v| workers = v.to_i }
|
25
|
+
opt.on('-e NUM'){|v| exit_with_code = v.to_i }
|
26
|
+
opt.on('-s NUM'){|v| exit_at_seconds = v.to_i }
|
27
|
+
opt.on('-r'){ exit_at_random = true }
|
28
|
+
opt.on('-i'){ stop_immediately_at_exit = true }
|
29
|
+
opt.on('-u NUM'){|v| unrecoverable_exit_codes << v.to_i }
|
30
|
+
opt.parse!(ARGV)
|
31
|
+
|
32
|
+
if exit_with_code
|
33
|
+
ENV['EXIT_WITH_CODE'] = exit_with_code.to_s
|
34
|
+
ENV['EXIT_AT_SECONDS'] = exit_at_seconds.to_s
|
35
|
+
if exit_at_random
|
36
|
+
ENV['EXIT_AT_RANDOM'] = 'true'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
module MyServer
|
41
|
+
attr_reader :socket_manager_path
|
42
|
+
|
43
|
+
def before_run
|
44
|
+
@socket_manager_path = ServerEngine::SocketManager::Server.generate_path
|
45
|
+
@socket_manager_server = ServerEngine::SocketManager::Server.open(@socket_manager_path)
|
46
|
+
rescue Exception => e
|
47
|
+
logger.error "unexpected error in server, class #{e.class}: #{e.message}"
|
48
|
+
raise
|
49
|
+
end
|
50
|
+
|
51
|
+
def after_run
|
52
|
+
logger.info "Server stopped."
|
53
|
+
@socket_manager_server.close
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
module MyWorker
|
58
|
+
def initialize
|
59
|
+
@stop = false
|
60
|
+
@socket_manager = ServerEngine::SocketManager::Client.new(server.socket_manager_path)
|
61
|
+
@exit_with_code = ENV.key?('EXIT_WITH_CODE') ? ENV['EXIT_WITH_CODE'].to_i : nil
|
62
|
+
@exit_at_seconds = ENV.key?('EXIT_AT_SECONDS') ? ENV['EXIT_AT_SECONDS'].to_i : nil
|
63
|
+
@exit_at_random = ENV.key?('EXIT_AT_RANDOM')
|
64
|
+
end
|
65
|
+
|
66
|
+
def main
|
67
|
+
# test to listen the same port
|
68
|
+
logger.info "Starting to run Worker."
|
69
|
+
_listen_sock = @socket_manager.listen_tcp('0.0.0.0', 12345)
|
70
|
+
stop_at = if @exit_with_code
|
71
|
+
stop_seconds = @exit_at_random ? rand(@exit_at_seconds) : @exit_at_seconds
|
72
|
+
logger.info "Stop #{stop_seconds} seconds later with code #{@exit_with_code}."
|
73
|
+
Time.now + stop_seconds
|
74
|
+
else
|
75
|
+
nil
|
76
|
+
end
|
77
|
+
until @stop
|
78
|
+
if stop_at && Time.now >= stop_at
|
79
|
+
logger.info "Exitting with code #{@exit_with_code}"
|
80
|
+
exit! @exit_with_code
|
81
|
+
end
|
82
|
+
logger.info "Awesome work!"
|
83
|
+
sleep 1
|
84
|
+
end
|
85
|
+
logger.info "Exitting"
|
86
|
+
rescue Exception => e
|
87
|
+
logger.warn "unexpected error, class #{e.class}: #{e.message}"
|
88
|
+
raise
|
89
|
+
end
|
90
|
+
|
91
|
+
def stop
|
92
|
+
@stop = true
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
module MySpawnWorker
|
97
|
+
def spawn(process_manager)
|
98
|
+
env = {
|
99
|
+
'SERVER_ENGINE_CONFIG' => config.to_json,
|
100
|
+
'SERVER_ENGINE_SOCKET_MANAGER_PATH' => server.socket_manager_path,
|
101
|
+
}
|
102
|
+
if ENV['EXIT_WITH_CODE']
|
103
|
+
env['EXIT_WITH_CODE'] = ENV['EXIT_WITH_CODE']
|
104
|
+
env['EXIT_AT_SECONDS'] = ENV['EXIT_AT_SECONDS']
|
105
|
+
if ENV['EXIT_AT_RANDOM']
|
106
|
+
env['EXIT_AT_RANDOM'] = 'true'
|
107
|
+
end
|
108
|
+
end
|
109
|
+
process_manager.spawn(env, "ruby", File.expand_path("../spawn_worker_script.rb", __FILE__))
|
110
|
+
rescue Exception => e
|
111
|
+
logger.error "unexpected error, class #{e.class}: #{e.message}"
|
112
|
+
raise
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
opts = {
|
117
|
+
daemonize: !foreground,
|
118
|
+
daemon_process_name: 'mydaemon',
|
119
|
+
supervisor: supervisor,
|
120
|
+
log: 'myserver.log',
|
121
|
+
pid_path: 'myserver.pid',
|
122
|
+
worker_type: worker_type,
|
123
|
+
workers: workers,
|
124
|
+
}
|
125
|
+
if stop_immediately_at_exit
|
126
|
+
opts[:stop_immediately_at_unrecoverable_exit] = true
|
127
|
+
end
|
128
|
+
unless unrecoverable_exit_codes.empty?
|
129
|
+
opts[:unrecoverable_exit_codes] = unrecoverable_exit_codes
|
130
|
+
end
|
131
|
+
|
132
|
+
worker_klass = MyWorker
|
133
|
+
if worker_type == 'spawn'
|
134
|
+
worker_klass = MySpawnWorker
|
135
|
+
end
|
136
|
+
se = ServerEngine.create(MyServer, worker_klass, opts)
|
137
|
+
|
138
|
+
se.run
|
@@ -0,0 +1,38 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path("../..", __FILE__)
|
2
|
+
|
3
|
+
require 'serverengine'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
begin
|
7
|
+
conf = JSON.parse(ENV['SERVER_ENGINE_CONFIG'], symbolize_names: true)
|
8
|
+
logger = ServerEngine::DaemonLogger.new(conf[:log] || STDOUT, conf)
|
9
|
+
logger.info "Starting to run Worker."
|
10
|
+
socket_manager = ServerEngine::SocketManager::Client.new(ENV['SERVER_ENGINE_SOCKET_MANAGER_PATH'])
|
11
|
+
exit_with_code = ENV.key?('EXIT_WITH_CODE') ? ENV['EXIT_WITH_CODE'].to_i : nil
|
12
|
+
exit_at_seconds = ENV.key?('EXIT_AT_SECONDS') ? ENV['EXIT_AT_SECONDS'].to_i : nil
|
13
|
+
exit_at_random = ENV.key?('EXIT_AT_RANDOM')
|
14
|
+
stop_at = if exit_with_code
|
15
|
+
stop_seconds = exit_at_random ? rand(exit_at_seconds) : exit_at_seconds
|
16
|
+
logger.info "Stop #{stop_seconds} seconds later with code #{exit_with_code}."
|
17
|
+
Time.now + stop_seconds
|
18
|
+
else
|
19
|
+
nil
|
20
|
+
end
|
21
|
+
|
22
|
+
@stop = false
|
23
|
+
trap(:SIGTERM) { @stop = true }
|
24
|
+
trap(:SIGINT) { @stop = true }
|
25
|
+
|
26
|
+
_listen_sock = socket_manager.listen_tcp('0.0.0.0', 12345)
|
27
|
+
until @stop
|
28
|
+
if stop_at && Time.now >= stop_at
|
29
|
+
logger.info "Exitting with code #{exit_with_code}"
|
30
|
+
exit! exit_with_code
|
31
|
+
end
|
32
|
+
logger.info 'Awesome work!'
|
33
|
+
sleep 1
|
34
|
+
end
|
35
|
+
logger.info 'Exitting'
|
36
|
+
rescue Exception => e
|
37
|
+
logger.error "unexpected error in spawn worker, class #{e.class}: #{e.message}"
|
38
|
+
end
|
data/lib/serverengine.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
#
|
2
|
+
# ServerEngine
|
3
|
+
#
|
4
|
+
# Copyright (C) 2012-2013 Sadayuki Furuhashi
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
module ServerEngine
|
19
|
+
|
20
|
+
require 'sigdump'
|
21
|
+
|
22
|
+
require 'serverengine/version'
|
23
|
+
|
24
|
+
require 'serverengine/utils' # ServerEngine.windows? and other util methods
|
25
|
+
|
26
|
+
require 'serverengine/daemon'
|
27
|
+
require 'serverengine/supervisor'
|
28
|
+
require 'serverengine/server'
|
29
|
+
require 'serverengine/worker'
|
30
|
+
require 'serverengine/socket_manager'
|
31
|
+
|
32
|
+
def self.create(server_module, worker_module, load_config_proc={}, &block)
|
33
|
+
Daemon.new(server_module, worker_module, load_config_proc, &block)
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.ruby_bin_path
|
37
|
+
if ServerEngine.windows?
|
38
|
+
require 'windows/library'
|
39
|
+
ruby_path = "\0" * 256
|
40
|
+
Windows::Library::GetModuleFileName.call(0, ruby_path, 256)
|
41
|
+
return ruby_path.rstrip.gsub(/\\/, '/')
|
42
|
+
else
|
43
|
+
return File.join(RbConfig::CONFIG["bindir"], RbConfig::CONFIG["RUBY_INSTALL_NAME"]) + RbConfig::CONFIG["EXEEXT"]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
#
|
2
|
+
# ServerEngine
|
3
|
+
#
|
4
|
+
# Copyright (C) 2012-2013 Sadayuki Furuhashi
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
require 'thread'
|
19
|
+
|
20
|
+
module ServerEngine
|
21
|
+
class BlockingFlag
|
22
|
+
def initialize
|
23
|
+
@set = false
|
24
|
+
@mutex = Mutex.new
|
25
|
+
@cond = ConditionVariable.new
|
26
|
+
end
|
27
|
+
|
28
|
+
def set!
|
29
|
+
toggled = false
|
30
|
+
@mutex.synchronize do
|
31
|
+
unless @set
|
32
|
+
@set = true
|
33
|
+
toggled = true
|
34
|
+
end
|
35
|
+
@cond.broadcast
|
36
|
+
end
|
37
|
+
return toggled
|
38
|
+
end
|
39
|
+
|
40
|
+
def reset!
|
41
|
+
toggled = false
|
42
|
+
@mutex.synchronize do
|
43
|
+
if @set
|
44
|
+
@set = false
|
45
|
+
toggled = true
|
46
|
+
end
|
47
|
+
@cond.broadcast
|
48
|
+
end
|
49
|
+
return toggled
|
50
|
+
end
|
51
|
+
|
52
|
+
def set?
|
53
|
+
@set
|
54
|
+
end
|
55
|
+
|
56
|
+
def wait_for_set(timeout=nil)
|
57
|
+
@mutex.synchronize do
|
58
|
+
unless @set
|
59
|
+
@cond.wait(@mutex, timeout)
|
60
|
+
end
|
61
|
+
return @set
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
alias_method :wait, :wait_for_set
|
66
|
+
|
67
|
+
def wait_for_reset(timeout=nil)
|
68
|
+
@mutex.synchronize do
|
69
|
+
if @set
|
70
|
+
@cond.wait(@mutex, timeout)
|
71
|
+
end
|
72
|
+
return !@set
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
#
|
2
|
+
# ServerEngine
|
3
|
+
#
|
4
|
+
# Copyright (C) 2012-2013 FURUHASHI Sadayuki
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
require 'serverengine/signals'
|
19
|
+
|
20
|
+
module ServerEngine
|
21
|
+
module CommandSender
|
22
|
+
# requires send_signal method or @pid
|
23
|
+
module Signal
|
24
|
+
private
|
25
|
+
def _stop(graceful)
|
26
|
+
_send_signal(!ServerEngine.windows? && graceful ? Signals::GRACEFUL_STOP : Signals::IMMEDIATE_STOP)
|
27
|
+
end
|
28
|
+
|
29
|
+
def _restart(graceful)
|
30
|
+
_send_signal(graceful ? Signals::GRACEFUL_RESTART : Signals::IMMEDIATE_RESTART)
|
31
|
+
end
|
32
|
+
|
33
|
+
def _reload
|
34
|
+
_send_signal(Signals::RELOAD)
|
35
|
+
end
|
36
|
+
|
37
|
+
def _detach
|
38
|
+
_send_signal(Signals::DETACH)
|
39
|
+
end
|
40
|
+
|
41
|
+
def _dump
|
42
|
+
_send_signal(Signals::DUMP)
|
43
|
+
end
|
44
|
+
|
45
|
+
def _send_signal(sig)
|
46
|
+
if respond_to?(:send_signal, true)
|
47
|
+
send_signal(sig)
|
48
|
+
else
|
49
|
+
Process.kill(sig, @pid)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# requires @command_sender_pipe
|
55
|
+
module Pipe
|
56
|
+
private
|
57
|
+
def _stop(graceful)
|
58
|
+
begin
|
59
|
+
_send_command(graceful ? "GRACEFUL_STOP" : "IMMEDIATE_STOP")
|
60
|
+
rescue Errno::EPIPE
|
61
|
+
# already stopped, then nothing to do
|
62
|
+
ensure
|
63
|
+
@command_sender_pipe.close rescue nil
|
64
|
+
@command_sender_pipe = nil
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def _restart(graceful)
|
69
|
+
_send_command(graceful ? "GRACEFUL_RESTART" : "IMMEDIATE_RESTART")
|
70
|
+
end
|
71
|
+
|
72
|
+
def _reload
|
73
|
+
_send_command("RELOAD")
|
74
|
+
end
|
75
|
+
|
76
|
+
def _detach
|
77
|
+
_send_command("DETACH")
|
78
|
+
end
|
79
|
+
|
80
|
+
def _dump
|
81
|
+
_send_command("DUMP")
|
82
|
+
end
|
83
|
+
|
84
|
+
def _send_command(cmd)
|
85
|
+
@command_sender_pipe.write cmd + "\n"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|