serverengine 2.0.0pre1-x86-mingw32
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 +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
@@ -0,0 +1,94 @@
|
|
1
|
+
|
2
|
+
describe ServerEngine::SignalThread do
|
3
|
+
it 'start and stop' do
|
4
|
+
t = SignalThread.new
|
5
|
+
t.stop.should == t
|
6
|
+
t.join
|
7
|
+
end
|
8
|
+
|
9
|
+
unless ServerEngine.windows?
|
10
|
+
it 'call handler' do
|
11
|
+
n = 0
|
12
|
+
|
13
|
+
t = SignalThread.new do |st|
|
14
|
+
st.trap('CONT') { n += 1 }
|
15
|
+
end
|
16
|
+
|
17
|
+
Process.kill('CONT', Process.pid)
|
18
|
+
sleep 0.5
|
19
|
+
|
20
|
+
t.stop.join
|
21
|
+
|
22
|
+
n.should == 1
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'SIG_IGN' do
|
26
|
+
# IGNORE signal handler has possible race condition in Ruby 2.1
|
27
|
+
# https://bugs.ruby-lang.org/issues/9835
|
28
|
+
# That's why ignore this test in Ruby2.1
|
29
|
+
if RUBY_VERSION >= '2.2'
|
30
|
+
t = SignalThread.new do |st|
|
31
|
+
st.trap('QUIT', 'SIG_IGN')
|
32
|
+
end
|
33
|
+
|
34
|
+
Process.kill('QUIT', Process.pid)
|
35
|
+
|
36
|
+
t.stop.join
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'signal in handler' do
|
41
|
+
n = 0
|
42
|
+
|
43
|
+
SignalThread.new do |st|
|
44
|
+
st.trap('QUIT') do
|
45
|
+
if n < 3
|
46
|
+
Process.kill('QUIT', Process.pid)
|
47
|
+
n += 1
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
Process.kill('QUIT', Process.pid)
|
53
|
+
sleep 0.5
|
54
|
+
|
55
|
+
n.should == 3
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'stop in handler' do
|
59
|
+
t = SignalThread.new do |st|
|
60
|
+
st.trap('QUIT') { st.stop }
|
61
|
+
end
|
62
|
+
|
63
|
+
Process.kill('QUIT', Process.pid)
|
64
|
+
sleep 0.5
|
65
|
+
|
66
|
+
t.join
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'should not deadlock' do
|
70
|
+
n = 0
|
71
|
+
|
72
|
+
SignalThread.new do |st|
|
73
|
+
st.trap('CONT') { n += 1 }
|
74
|
+
end
|
75
|
+
|
76
|
+
(1..10).map {
|
77
|
+
Thread.new do
|
78
|
+
10.times {
|
79
|
+
Process.kill('CONT', Process.pid)
|
80
|
+
}
|
81
|
+
end
|
82
|
+
}.each { |t|
|
83
|
+
t.join
|
84
|
+
}
|
85
|
+
|
86
|
+
# give chance to run the signal thread
|
87
|
+
sleep 0.1
|
88
|
+
|
89
|
+
# result won't be 100 because of kernel limitation
|
90
|
+
n.should > 0
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
@@ -0,0 +1,119 @@
|
|
1
|
+
|
2
|
+
describe ServerEngine::SocketManager do
|
3
|
+
include_context 'test server and worker'
|
4
|
+
|
5
|
+
let(:server_path) do
|
6
|
+
'tmp/socket_manager_test.sock'
|
7
|
+
end
|
8
|
+
|
9
|
+
after(:each) do
|
10
|
+
File.unlink(server_path) if File.exist?(server_path)
|
11
|
+
end
|
12
|
+
|
13
|
+
if ServerEngine.windows?
|
14
|
+
let(:server_port) do
|
15
|
+
24223
|
16
|
+
end
|
17
|
+
|
18
|
+
let(:test_port) do
|
19
|
+
9101
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'is windows' do
|
23
|
+
SocketManager::Client.is_a?(SocketManagerWin::ClientModule)
|
24
|
+
SocketManager::Server.is_a?(SocketManagerWin::ServerModule)
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'with thread' do
|
28
|
+
it 'works' do
|
29
|
+
server = SocketManager::Server.open(server_port)
|
30
|
+
|
31
|
+
thread = Thread.new do
|
32
|
+
|
33
|
+
client = ServerEngine::SocketManager::Client.new(server_port)
|
34
|
+
tcp = client.listen_tcp('127.0.0.1', test_port)
|
35
|
+
udp = client.listen_udp('127.0.0.1', test_port)
|
36
|
+
|
37
|
+
incr_test_state(:is_tcp_server) if tcp.is_a?(TCPServer)
|
38
|
+
incr_test_state(:is_udp_socket) if udp.is_a?(UDPSocket)
|
39
|
+
|
40
|
+
data, _from = udp.recvfrom(10)
|
41
|
+
incr_test_state(:udp_data_sent) if data == "ok"
|
42
|
+
|
43
|
+
s = tcp.accept
|
44
|
+
s.write("ok")
|
45
|
+
s.close
|
46
|
+
end
|
47
|
+
|
48
|
+
sleep 1
|
49
|
+
|
50
|
+
u = UDPSocket.new
|
51
|
+
u.send "ok", 0, '127.0.0.1', test_port
|
52
|
+
u.close
|
53
|
+
|
54
|
+
t = TCPSocket.open('127.0.0.1', test_port)
|
55
|
+
t.read.should == "ok"
|
56
|
+
t.close
|
57
|
+
|
58
|
+
server.close
|
59
|
+
thread.join
|
60
|
+
|
61
|
+
test_state(:is_tcp_server).should == 1
|
62
|
+
test_state(:is_udp_socket).should == 1
|
63
|
+
test_state(:udp_data_sent).should == 1
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
else
|
68
|
+
let(:test_port) do
|
69
|
+
9102
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'is unix' do
|
73
|
+
SocketManager::Client.is_a?(SocketManagerUnix::ClientModule)
|
74
|
+
SocketManager::Server.is_a?(SocketManagerUnix::ServerModule)
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'with fork' do
|
78
|
+
it 'works' do
|
79
|
+
server = SocketManager::Server.open(server_path)
|
80
|
+
|
81
|
+
fork do
|
82
|
+
server.close
|
83
|
+
|
84
|
+
client = server.new_client
|
85
|
+
|
86
|
+
tcp = client.listen_tcp('127.0.0.1', test_port)
|
87
|
+
udp = client.listen_udp('127.0.0.1', test_port)
|
88
|
+
|
89
|
+
incr_test_state(:is_tcp_server) if tcp.is_a?(TCPServer)
|
90
|
+
incr_test_state(:is_udp_socket) if udp.is_a?(UDPSocket)
|
91
|
+
|
92
|
+
data, _from = udp.recvfrom(10)
|
93
|
+
incr_test_state(:udp_data_sent) if data == "ok"
|
94
|
+
|
95
|
+
s = tcp.accept
|
96
|
+
s.write("ok")
|
97
|
+
s.close
|
98
|
+
end
|
99
|
+
|
100
|
+
wait_for_fork
|
101
|
+
|
102
|
+
u = UDPSocket.new
|
103
|
+
u.send "ok", 0, '127.0.0.1', test_port
|
104
|
+
u.close
|
105
|
+
|
106
|
+
t = TCPSocket.open('127.0.0.1', test_port)
|
107
|
+
t.read.should == "ok"
|
108
|
+
t.close
|
109
|
+
|
110
|
+
server.close
|
111
|
+
|
112
|
+
test_state(:is_tcp_server).should == 1
|
113
|
+
test_state(:is_udp_socket).should == 1
|
114
|
+
test_state(:udp_data_sent).should == 1
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :test)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'serverengine'
|
12
|
+
include ServerEngine
|
13
|
+
|
14
|
+
# require sigdump only in unix, because there is no suport for SIGCONT in windows.
|
15
|
+
unless ServerEngine.windows?
|
16
|
+
require 'sigdump/setup'
|
17
|
+
end
|
18
|
+
require 'server_worker_context'
|
19
|
+
|
@@ -0,0 +1,215 @@
|
|
1
|
+
|
2
|
+
describe ServerEngine::Supervisor do
|
3
|
+
include_context 'test server and worker'
|
4
|
+
|
5
|
+
def start_supervisor(worker = nil, **config)
|
6
|
+
if ServerEngine.windows?
|
7
|
+
config[:windows_daemon_cmdline] = windows_supervisor_cmdline(nil, worker, config)
|
8
|
+
end
|
9
|
+
sv = Supervisor.new(TestServer, worker || TestWorker, config)
|
10
|
+
t = Thread.new { sv.main }
|
11
|
+
|
12
|
+
return sv, t
|
13
|
+
end
|
14
|
+
|
15
|
+
def start_daemon(**config)
|
16
|
+
if ServerEngine.windows?
|
17
|
+
config[:windows_daemon_cmdline] = windows_daemon_cmdline
|
18
|
+
end
|
19
|
+
daemon = Daemon.new(nil, TestWorker, config)
|
20
|
+
t = Thread.new { daemon.main }
|
21
|
+
|
22
|
+
return daemon, t
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'when :log=IO option is given' do
|
26
|
+
it 'can start' do
|
27
|
+
daemon, t = start_daemon(log: STDOUT)
|
28
|
+
|
29
|
+
begin
|
30
|
+
wait_for_fork
|
31
|
+
ensure
|
32
|
+
daemon.server.stop(true)
|
33
|
+
t.join
|
34
|
+
end
|
35
|
+
|
36
|
+
test_state(:worker_run).should == 1
|
37
|
+
daemon.server.logger.should be_an_instance_of(ServerEngine::DaemonLogger)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'when :logger option is given' do
|
42
|
+
it 'uses specified logger instance' do
|
43
|
+
logger = ServerEngine::DaemonLogger.new(STDOUT)
|
44
|
+
daemon, t = start_daemon(logger: logger)
|
45
|
+
|
46
|
+
begin
|
47
|
+
wait_for_fork
|
48
|
+
ensure
|
49
|
+
daemon.server.stop(true)
|
50
|
+
t.join
|
51
|
+
end
|
52
|
+
|
53
|
+
test_state(:worker_run).should == 1
|
54
|
+
daemon.server.logger.should == logger
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'when both :logger and :log options are given' do
|
59
|
+
it 'start ignoring :log' do
|
60
|
+
logger = ServerEngine::DaemonLogger.new(STDOUT)
|
61
|
+
daemon, t = start_daemon(logger: logger, log: STDERR)
|
62
|
+
|
63
|
+
begin
|
64
|
+
wait_for_fork
|
65
|
+
ensure
|
66
|
+
daemon.server.stop(true)
|
67
|
+
t.join
|
68
|
+
end
|
69
|
+
|
70
|
+
test_state(:worker_run).should == 1
|
71
|
+
daemon.server.logger.should == logger
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
['signal', 'pipe'].each do |sender|
|
76
|
+
context "when using #{sender} as command_sender" do
|
77
|
+
|
78
|
+
it 'start and graceful stop' do
|
79
|
+
pending 'not supported on Windows' if ServerEngine.windows? && sender == 'signal'
|
80
|
+
|
81
|
+
sv, t = start_supervisor(command_sender: sender)
|
82
|
+
|
83
|
+
begin
|
84
|
+
wait_for_fork
|
85
|
+
|
86
|
+
test_state(:server_before_run).should == 1
|
87
|
+
test_state(:server_after_start).should == 1 # parent
|
88
|
+
ensure
|
89
|
+
sv.stop(true)
|
90
|
+
t.join
|
91
|
+
end
|
92
|
+
|
93
|
+
test_state(:server_stop).should == 1
|
94
|
+
test_state(:server_stop_graceful).should == 1
|
95
|
+
test_state(:server_restart).should == 0
|
96
|
+
|
97
|
+
test_state(:server_after_run).should == 1
|
98
|
+
test_state(:server_after_start).should == 1
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'immediate stop' do
|
102
|
+
pending 'not supported on Windows' if ServerEngine.windows? && sender == 'signal'
|
103
|
+
|
104
|
+
sv, t = start_supervisor(command_sender: sender)
|
105
|
+
|
106
|
+
begin
|
107
|
+
wait_for_fork
|
108
|
+
ensure
|
109
|
+
sv.stop(false)
|
110
|
+
t.join
|
111
|
+
end
|
112
|
+
|
113
|
+
test_state(:server_stop).should == 1
|
114
|
+
test_state(:server_stop_immediate).should == 1
|
115
|
+
test_state(:server_after_run).should == 1
|
116
|
+
test_state(:server_after_start).should == 1
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'graceful restart' do
|
120
|
+
pending 'not supported on Windows' if ServerEngine.windows? && sender == 'signal'
|
121
|
+
|
122
|
+
sv, t = start_supervisor(command_sender: sender)
|
123
|
+
|
124
|
+
begin
|
125
|
+
wait_for_fork
|
126
|
+
|
127
|
+
sv.restart(true)
|
128
|
+
wait_for_stop
|
129
|
+
|
130
|
+
ensure
|
131
|
+
sv.stop(true)
|
132
|
+
t.join
|
133
|
+
end
|
134
|
+
|
135
|
+
test_state(:server_stop).should == 1
|
136
|
+
test_state(:server_restart_graceful).should == 1
|
137
|
+
|
138
|
+
test_state(:server_before_run).should == 1
|
139
|
+
test_state(:server_after_run).should == 1
|
140
|
+
test_state(:server_after_start).should == 1
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'immediate restart' do
|
144
|
+
pending 'not supported on Windows' if ServerEngine.windows? && sender == 'signal'
|
145
|
+
|
146
|
+
sv, t = start_supervisor(command_sender: sender)
|
147
|
+
|
148
|
+
begin
|
149
|
+
wait_for_fork
|
150
|
+
|
151
|
+
sv.restart(false)
|
152
|
+
wait_for_stop
|
153
|
+
|
154
|
+
ensure
|
155
|
+
sv.stop(true)
|
156
|
+
t.join
|
157
|
+
end
|
158
|
+
|
159
|
+
test_state(:server_stop).should == 1
|
160
|
+
test_state(:server_restart_immediate).should == 1
|
161
|
+
|
162
|
+
test_state(:server_before_run).should == 1
|
163
|
+
test_state(:server_after_run).should == 1
|
164
|
+
test_state(:server_after_start).should == 1
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'reload' do
|
168
|
+
pending 'not supported on Windows' if ServerEngine.windows? && sender == 'signal'
|
169
|
+
|
170
|
+
sv, t = start_supervisor(command_sender: sender)
|
171
|
+
|
172
|
+
begin
|
173
|
+
wait_for_fork
|
174
|
+
|
175
|
+
sv.reload
|
176
|
+
|
177
|
+
ensure
|
178
|
+
sv.stop(true)
|
179
|
+
t.join
|
180
|
+
end
|
181
|
+
|
182
|
+
test_state(:server_stop).should == 1
|
183
|
+
test_state(:server_reload).should == 1
|
184
|
+
end
|
185
|
+
|
186
|
+
# TODO detach
|
187
|
+
|
188
|
+
it 'auto restart in limited ratio' do
|
189
|
+
pending 'not supported on Windows' if ServerEngine.windows? && sender == 'signal'
|
190
|
+
|
191
|
+
sv, t = start_supervisor(RunErrorWorker, server_restart_wait: 1, command_sender: sender)
|
192
|
+
|
193
|
+
begin
|
194
|
+
sleep 2.2
|
195
|
+
ensure
|
196
|
+
sv.stop(true)
|
197
|
+
t.join
|
198
|
+
end
|
199
|
+
|
200
|
+
test_state(:worker_run).should == 3
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
module InitializeErrorServer
|
206
|
+
def initialize
|
207
|
+
raise StandardError, "error test"
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
it 'initialize error' do
|
212
|
+
sv = Supervisor.new(InitializeErrorServer, TestWorker)
|
213
|
+
lambda { sv.main }.should raise_error(StandardError)
|
214
|
+
end
|
215
|
+
end
|