riser 0.1.1 → 0.1.2
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/LICENSE.txt +1 -1
- data/README.md +30 -16
- data/example/local_services.rb +2 -1
- data/example/simple_key_count.rb +2 -2
- data/example/simple_services.rb +2 -1
- data/lib/riser/daemon.rb +255 -109
- data/lib/riser/server.rb +33 -126
- data/lib/riser/sockaddr.rb +165 -0
- data/lib/riser/version.rb +1 -1
- data/lib/riser.rb +1 -1
- data/riser.gemspec +5 -2
- metadata +8 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cb038d800b97433c3499cb880acc77e0513d6dab1d18eace369137a98870cd8b
|
4
|
+
data.tar.gz: e4b2a6ab5cc93fa3e84c0dcdd53038e879d54e51ee2007f702a1afeb10c45409
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4b5fdb35eb7ea3c7f9b535e5d857acb7bb85b671db989ed90123ead48d1079606afbe35a9f0c970a4ddfe62c21c32246468406c33739fad5a037b5e61b92d525
|
7
|
+
data.tar.gz: 71a67816a2048bd849eb12801a40c8114b446bafbbde210e253c172af915026d524c976f6068472ebf747af2b9a0ad6b79d9e3344b74b58bf96c874337db3368
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -463,7 +463,8 @@ Riser::Daemon.start_daemon(daemonize: false,
|
|
463
463
|
socket << 'pid: ' << services.call_service(:pid_single) << "\n"
|
464
464
|
when '/sticky'
|
465
465
|
key = query || 'default'
|
466
|
-
socket << 'key: ' << key <<
|
466
|
+
socket << 'key: ' << key << "\n"
|
467
|
+
socket << 'pid: ' << services.call_service(:pid_stickty, key) << "\n"
|
467
468
|
else
|
468
469
|
socket << "unknown path: #{path}\n"
|
469
470
|
end
|
@@ -587,17 +588,23 @@ the web service's result of 'sticky process' pattern.
|
|
587
588
|
|
588
589
|
```
|
589
590
|
$ curl http://localhost:8000/sticky
|
590
|
-
key: default
|
591
|
+
key: default
|
592
|
+
pid: 3181
|
591
593
|
$ curl http://localhost:8000/sticky
|
592
|
-
key: default
|
594
|
+
key: default
|
595
|
+
pid: 3181
|
593
596
|
$ curl http://localhost:8000/sticky?foo
|
594
|
-
key: foo
|
597
|
+
key: foo
|
598
|
+
pid: 3179
|
595
599
|
$ curl http://localhost:8000/sticky?foo
|
596
|
-
key: foo
|
600
|
+
key: foo
|
601
|
+
pid: 3179
|
597
602
|
$ curl http://localhost:8000/sticky?bar
|
598
|
-
key: bar
|
603
|
+
key: bar
|
604
|
+
pid: 3185
|
599
605
|
$ curl http://localhost:8000/sticky?bar
|
600
|
-
key: bar
|
606
|
+
key: bar
|
607
|
+
pid: 3185
|
601
608
|
```
|
602
609
|
|
603
610
|
In the 'sticky process' pattern, the same process id will be given for
|
@@ -651,7 +658,8 @@ Riser::Daemon.start_daemon(daemonize: false,
|
|
651
658
|
socket << 'pid: ' << services.call_service(:pid_single) << "\n"
|
652
659
|
when '/sticky'
|
653
660
|
key = query || 'default'
|
654
|
-
socket << 'key: ' << key <<
|
661
|
+
socket << 'key: ' << key << "\n"
|
662
|
+
socket << 'pid: ' << services.call_service(:pid_stickty, key) << "\n"
|
655
663
|
else
|
656
664
|
socket << "unknown path: #{path}\n"
|
657
665
|
end
|
@@ -711,17 +719,23 @@ pid: 3855
|
|
711
719
|
|
712
720
|
```
|
713
721
|
$ curl http://localhost:8000/sticky
|
714
|
-
key: default
|
722
|
+
key: default
|
723
|
+
pid: 3855
|
715
724
|
$ curl http://localhost:8000/sticky
|
716
|
-
key: default
|
725
|
+
key: default
|
726
|
+
pid: 3855
|
717
727
|
$ curl http://localhost:8000/sticky?foo
|
718
|
-
key: foo
|
728
|
+
key: foo
|
729
|
+
pid: 3855
|
719
730
|
$ curl http://localhost:8000/sticky?foo
|
720
|
-
key: foo
|
731
|
+
key: foo
|
732
|
+
pid: 3855
|
721
733
|
$ curl http://localhost:8000/sticky?bar
|
722
|
-
key: bar
|
734
|
+
key: bar
|
735
|
+
pid: 3855
|
723
736
|
$ curl http://localhost:8000/sticky?bar
|
724
|
-
key: bar
|
737
|
+
key: bar
|
738
|
+
pid: 3855
|
725
739
|
```
|
726
740
|
|
727
741
|
### dRuby Services Callbacks
|
@@ -819,8 +833,8 @@ Riser::Daemon.start_daemon(daemonize: false,
|
|
819
833
|
builder.at_create{|key|
|
820
834
|
PStore.new("simple_key_count-#{key}.pstore", true)
|
821
835
|
}
|
822
|
-
builder.at_destroy{
|
823
|
-
# nothing to do.
|
836
|
+
builder.at_destroy{|pstore|
|
837
|
+
# nothing to do about `pstore'.
|
824
838
|
}
|
825
839
|
})
|
826
840
|
|
data/example/local_services.rb
CHANGED
@@ -42,7 +42,8 @@ Riser::Daemon.start_daemon(daemonize: false,
|
|
42
42
|
socket << 'pid: ' << services.call_service(:pid_single) << "\n"
|
43
43
|
when '/sticky'
|
44
44
|
key = query || 'default'
|
45
|
-
socket << 'key: ' << key <<
|
45
|
+
socket << 'key: ' << key << "\n"
|
46
|
+
socket << 'pid: ' << services.call_service(:pid_stickty, key) << "\n"
|
46
47
|
else
|
47
48
|
socket << "unknown path: #{path}\n"
|
48
49
|
end
|
data/example/simple_key_count.rb
CHANGED
data/example/simple_services.rb
CHANGED
@@ -42,7 +42,8 @@ Riser::Daemon.start_daemon(daemonize: false,
|
|
42
42
|
socket << 'pid: ' << services.call_service(:pid_single) << "\n"
|
43
43
|
when '/sticky'
|
44
44
|
key = query || 'default'
|
45
|
-
socket << 'key: ' << key <<
|
45
|
+
socket << 'key: ' << key << "\n"
|
46
|
+
socket << 'pid: ' << services.call_service(:pid_stickty, key) << "\n"
|
46
47
|
else
|
47
48
|
socket << "unknown path: #{path}\n"
|
48
49
|
end
|
data/lib/riser/daemon.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
|
3
|
+
require 'fileutils'
|
3
4
|
require 'logger'
|
4
5
|
require 'syslog/logger'
|
5
6
|
require 'yaml'
|
@@ -35,17 +36,19 @@ module Riser
|
|
35
36
|
|
36
37
|
class RootProcess
|
37
38
|
class SystemOperation
|
38
|
-
def initialize(logger, module_Process: Process, class_IO: IO)
|
39
|
+
def initialize(logger, module_FileUtils: FileUtils, module_Process: Process, class_IO: IO, class_File: File)
|
39
40
|
@logger = logger
|
41
|
+
@FileUtils = module_FileUtils
|
40
42
|
@Process = module_Process
|
41
43
|
@IO = class_IO
|
44
|
+
@File = class_File
|
42
45
|
end
|
43
46
|
|
44
47
|
def get_server_address(sockaddr_get)
|
45
48
|
begin
|
46
49
|
address_config = sockaddr_get.call
|
47
50
|
rescue
|
48
|
-
@logger.error("failed to get server address [#{$!}]")
|
51
|
+
@logger.error("failed to get server address [#{$!} (#{$!.class})]")
|
49
52
|
@logger.debug($!) if @logger.debug?
|
50
53
|
return
|
51
54
|
end
|
@@ -61,7 +64,37 @@ module Riser
|
|
61
64
|
begin
|
62
65
|
server_address.open_server
|
63
66
|
rescue
|
64
|
-
@logger.error("failed to open server socket: #{server_address} [#{$!}]")
|
67
|
+
@logger.error("failed to open server socket: #{server_address} [#{$!} (#{$!.class})]")
|
68
|
+
@logger.debug($!) if @logger.debug?
|
69
|
+
nil
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def listen(server_socket, backlog)
|
74
|
+
begin
|
75
|
+
server_socket.listen(backlog)
|
76
|
+
rescue
|
77
|
+
@logger.error("failed to listen(2) server socket with backlog: #{backlog} [#{$!} (#{$!.class})]")
|
78
|
+
@logger.debug($!) if @logger.debug?
|
79
|
+
nil
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def chmod(mode, path)
|
84
|
+
begin
|
85
|
+
@FileUtils.chmod(mode, path)
|
86
|
+
rescue
|
87
|
+
@logger.error("failed to chmod(2): #{'%04o' % mode} #{path} [#{$!} (#{$!.class})]")
|
88
|
+
@logger.debug($!) if @logger.debug?
|
89
|
+
nil
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def chown(owner, group, path)
|
94
|
+
begin
|
95
|
+
@FileUtils.chown(owner, group, path)
|
96
|
+
rescue
|
97
|
+
@logger.error("failed to chown(2): <#{owner}> <#{group}> <#{path}> [#{$!} (#{$!.class})]")
|
65
98
|
@logger.debug($!) if @logger.debug?
|
66
99
|
nil
|
67
100
|
end
|
@@ -71,7 +104,7 @@ module Riser
|
|
71
104
|
begin
|
72
105
|
@Process.kill(signal, pid)
|
73
106
|
rescue
|
74
|
-
@logger.error("failed to send signal (#{signal}) to process (pid: #{pid}) [#{$!}]")
|
107
|
+
@logger.error("failed to send signal (#{signal}) to process (pid: #{pid}) [#{$!} (#{$!.class})]")
|
75
108
|
@logger.debug($!) if @logger.debug?
|
76
109
|
nil
|
77
110
|
end
|
@@ -81,7 +114,7 @@ module Riser
|
|
81
114
|
begin
|
82
115
|
@Process.wait(pid, flags)
|
83
116
|
rescue
|
84
|
-
@logger.error("failed to wait(2) for process (pid: #{pid}) [#{$!}]")
|
117
|
+
@logger.error("failed to wait(2) for process (pid: #{pid}) [#{$!} (#{$!.class})]")
|
85
118
|
@logger.debug($!) if @logger.debug?
|
86
119
|
nil
|
87
120
|
end
|
@@ -91,7 +124,7 @@ module Riser
|
|
91
124
|
begin
|
92
125
|
@IO.pipe
|
93
126
|
rescue
|
94
|
-
@logger.error("failed to pipe(2) [#{$!}]")
|
127
|
+
@logger.error("failed to pipe(2) [#{$!} (#{$!.class})]")
|
95
128
|
@logger.debug($!) if @logger.debug?
|
96
129
|
nil
|
97
130
|
end
|
@@ -101,7 +134,7 @@ module Riser
|
|
101
134
|
begin
|
102
135
|
@Process.fork{ yield }
|
103
136
|
rescue
|
104
|
-
@logger.error("failed to fork(2) [#{$!}]")
|
137
|
+
@logger.error("failed to fork(2) [#{$!} (#{$!.class})]")
|
105
138
|
@logger.debug($!) if @logger.debug?
|
106
139
|
nil
|
107
140
|
end
|
@@ -111,7 +144,7 @@ module Riser
|
|
111
144
|
begin
|
112
145
|
io.gets
|
113
146
|
rescue
|
114
|
-
@logger.error("failed to get line from #{io.inspect} [#{$!}]")
|
147
|
+
@logger.error("failed to get line from #{io.inspect} [#{$!} (#{$!.class})]")
|
115
148
|
@logger.debug($!) if @logger.debug?
|
116
149
|
nil
|
117
150
|
end
|
@@ -122,7 +155,17 @@ module Riser
|
|
122
155
|
io.close
|
123
156
|
io
|
124
157
|
rescue
|
125
|
-
@logger.error("failed to close(2) #{io.inspect} [#{$!}]")
|
158
|
+
@logger.error("failed to close(2) #{io.inspect} [#{$!} (#{$!.class})]")
|
159
|
+
@logger.debug($!) if @logger.debug?
|
160
|
+
nil
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def unlink(path)
|
165
|
+
begin
|
166
|
+
File.unlink(path)
|
167
|
+
rescue
|
168
|
+
@logger.error("failed to unlink(2): #{path} [#{$!} (#{$!.class})]")
|
126
169
|
@logger.debug($!) if @logger.debug?
|
127
170
|
nil
|
128
171
|
end
|
@@ -258,7 +301,7 @@ module Riser
|
|
258
301
|
Signal.trap(SIGNAL_STAT_GET_NO_RESET) { server.signal_stat_get(reset: false) }
|
259
302
|
Signal.trap(SIGNAL_STAT_STOP) { server.signal_stat_stop }
|
260
303
|
rescue
|
261
|
-
@logger.error("failed to setup server (pid: #{$$}) [#{$!}]")
|
304
|
+
@logger.error("failed to setup server (pid: #{$$}) [#{$!} (#{$!.class})]")
|
262
305
|
@logger.debug($!) if @logger.debug?
|
263
306
|
raise
|
264
307
|
end
|
@@ -313,139 +356,242 @@ module Riser
|
|
313
356
|
end
|
314
357
|
@logger.info("open server socket: #{server_socket.local_address.inspect_sockaddr}")
|
315
358
|
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
until (@stop_state)
|
324
|
-
server_polling_sleep
|
325
|
-
if (server_pid) then
|
326
|
-
@logger.debug("server polling... (pid: #{server_pid})") if @logger.debug?
|
359
|
+
begin
|
360
|
+
if (server_address.backlog) then
|
361
|
+
if (@sysop.listen(server_socket, server_address.backlog)) then
|
362
|
+
@logger.info("server socket backlog: #{server_address.backlog}")
|
363
|
+
else
|
364
|
+
@logger.warn('server socket backlog is not changed.')
|
365
|
+
end
|
327
366
|
else
|
328
|
-
@logger.
|
367
|
+
@logger.info('server socket backlog is default.')
|
329
368
|
end
|
330
369
|
|
331
|
-
if (
|
332
|
-
if (
|
333
|
-
@
|
370
|
+
if (server_address.type == :unix) then
|
371
|
+
if (server_address.mode) then
|
372
|
+
if (@sysop.chmod(server_address.mode, server_address.path)) then
|
373
|
+
@logger.info("unix domain server socket mode: #{'%04o' % server_address.mode}")
|
374
|
+
else
|
375
|
+
@logger.warn('unix domain server socket mode is not changed.')
|
376
|
+
end
|
334
377
|
else
|
335
|
-
@logger.
|
378
|
+
@logger.info('unix domain server socket mode is default.')
|
336
379
|
end
|
337
|
-
|
338
|
-
|
380
|
+
|
381
|
+
if (server_address.owner || server_address.group) then
|
382
|
+
if (@sysop.chown(server_address.owner, server_address.group, server_address.path)) then
|
383
|
+
@logger.info("unix domain server socket ownership: <#{server_address.owner}> <#{server_address.group}>")
|
384
|
+
else
|
385
|
+
@logger.warn('unix domain server socket ownership is not changed.')
|
386
|
+
end
|
387
|
+
else
|
388
|
+
@logger.info('unix domain server socket ownership is default.')
|
339
389
|
end
|
340
390
|
end
|
341
391
|
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
392
|
+
unless (server_pid = run_server(server_socket)) then
|
393
|
+
@logger.fatal('failed to start daemon.')
|
394
|
+
return 1
|
395
|
+
end
|
396
|
+
@logger.info("server process start (pid: #{server_pid})")
|
397
|
+
|
398
|
+
@logger.info("start server polling (interval seconds: #{@server_polling_interval_seconds})")
|
399
|
+
until (@stop_state)
|
400
|
+
server_polling_sleep
|
401
|
+
if (server_pid) then
|
402
|
+
@logger.debug("server polling... (pid: #{server_pid})") if @logger.debug?
|
403
|
+
else
|
404
|
+
@logger.debug('server polling...') if @logger.debug?
|
405
|
+
end
|
406
|
+
|
407
|
+
if (! server_pid || @sysop.wait(server_pid, Process::WNOHANG)) then
|
408
|
+
if (server_pid) then
|
409
|
+
@logger.warn("found server down (pid: #{server_pid}) and restart server.")
|
357
410
|
else
|
358
|
-
@logger.warn(
|
411
|
+
@logger.warn('found server down and restart server.')
|
412
|
+
end
|
413
|
+
if (server_pid = run_server(server_socket)) then
|
414
|
+
@logger.info("server process start (pid: #{server_pid})")
|
359
415
|
end
|
416
|
+
end
|
360
417
|
|
418
|
+
while (! @stop_state && server_pid && sig_ope = @signal_operation_queue.shift)
|
361
419
|
case (sig_ope)
|
362
|
-
when :restart_graceful
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
420
|
+
when :restart_graceful, :restart_forced
|
421
|
+
if (next_server_address = @sysop.get_server_address(@sockaddr_get)) then
|
422
|
+
if (next_server_address.to_address == server_address.to_address) then
|
423
|
+
if (next_server_address.to_option != server_address.to_option) then
|
424
|
+
server_address = next_server_address
|
425
|
+
end
|
426
|
+
else
|
427
|
+
if (next_server_socket = @sysop.get_server_socket(next_server_address)) then
|
428
|
+
@logger.info("open server socket: #{next_server_socket.local_address.inspect_sockaddr}")
|
429
|
+
server_socket_local_address = server_socket.local_address # get local_address before close(2)
|
430
|
+
if (@sysop.close(server_socket)) then
|
431
|
+
@logger.info("close server socket: #{server_socket_local_address.inspect_sockaddr}")
|
432
|
+
else
|
433
|
+
@logger.warn("failed to close server socket: #{server_socket_local_address.inspect_sockaddr}")
|
434
|
+
end
|
435
|
+
if (server_address.type == :unix) then
|
436
|
+
if (@sysop.unlink(server_address.path)) then
|
437
|
+
@logger.info("delete unix server socket: #{server_address}")
|
438
|
+
else
|
439
|
+
@logger.warn("failed to delete unix server socket: #{server_address}")
|
440
|
+
end
|
441
|
+
end
|
442
|
+
server_socket = next_server_socket
|
443
|
+
server_address = next_server_address
|
444
|
+
else
|
445
|
+
@logger.warn("server socket continue: #{server_socket.local_address.inspect_sockaddr}")
|
446
|
+
end
|
447
|
+
end
|
448
|
+
else
|
449
|
+
@logger.warn("server socket continue: #{server_socket.local_address.inspect_sockaddr}")
|
450
|
+
end
|
369
451
|
|
370
|
-
|
371
|
-
|
452
|
+
if (server_address.backlog) then
|
453
|
+
if (@sysop.listen(server_socket, server_address.backlog)) then
|
454
|
+
@logger.info("server socket backlog: #{server_address.backlog}")
|
455
|
+
else
|
456
|
+
@logger.warn('server socket backlog is not changed.')
|
457
|
+
end
|
458
|
+
else
|
459
|
+
@logger.info('server socket backlog is default.')
|
460
|
+
end
|
461
|
+
|
462
|
+
if (server_address.type == :unix) then
|
463
|
+
if (server_address.mode) then
|
464
|
+
if (@sysop.chmod(server_address.mode, server_address.path)) then
|
465
|
+
@logger.info("unix domain server socket mode: #{'%04o' % server_address.mode}")
|
466
|
+
else
|
467
|
+
@logger.warn('unix domain server socket mode is not changed.')
|
468
|
+
end
|
469
|
+
else
|
470
|
+
@logger.info('unix domain server socket mode is default.')
|
471
|
+
end
|
372
472
|
|
373
|
-
|
374
|
-
|
375
|
-
|
473
|
+
if (server_address.owner || server_address.group) then
|
474
|
+
if (@sysop.chown(server_address.owner, server_address.group, server_address.path)) then
|
475
|
+
@logger.info("unix domain server socket ownership: <#{server_address.owner}> <#{server_address.group}>")
|
476
|
+
else
|
477
|
+
@logger.warn('unix domain server socket ownership is not changed.')
|
478
|
+
end
|
479
|
+
else
|
480
|
+
@logger.info('unix domain server socket ownership is default.')
|
481
|
+
end
|
376
482
|
end
|
377
483
|
|
378
484
|
case (sig_ope)
|
379
485
|
when :restart_graceful
|
380
|
-
@logger.info("server graceful
|
381
|
-
server_stop_graceful(server_pid)
|
486
|
+
@logger.info("server graceful restart (pid: #{server_pid})")
|
382
487
|
when :restart_forced
|
383
|
-
@logger.info("server forced
|
384
|
-
server_stop_forced(server_pid)
|
488
|
+
@logger.info("server forced restart (pid: #{server_pid})")
|
385
489
|
else
|
386
490
|
@logger.warn("internal warning: unknown signal operation <#{sig_ope.inspect}>")
|
387
491
|
end
|
388
492
|
|
389
|
-
|
390
|
-
|
493
|
+
if (next_pid = run_server(server_socket)) then
|
494
|
+
@logger.info("server process start (pid: #{next_pid})")
|
495
|
+
|
496
|
+
if (@server_restart_overlap_seconds > 0) then
|
497
|
+
@logger.info("server restart overlap (interval seconds: #{@server_restart_overlap_seconds})")
|
498
|
+
sleep(@server_restart_overlap_seconds)
|
499
|
+
end
|
500
|
+
|
501
|
+
case (sig_ope)
|
502
|
+
when :restart_graceful
|
503
|
+
@logger.info("server graceful stop (pid: #{server_pid})")
|
504
|
+
server_stop_graceful(server_pid)
|
505
|
+
when :restart_forced
|
506
|
+
@logger.info("server forced stop (pid: #{server_pid})")
|
507
|
+
server_stop_forced(server_pid)
|
508
|
+
else
|
509
|
+
@logger.warn("internal warning: unknown signal operation <#{sig_ope.inspect}>")
|
510
|
+
end
|
511
|
+
|
512
|
+
@process_wait_count_table[server_pid] = 0
|
513
|
+
server_pid = next_pid
|
514
|
+
else
|
515
|
+
@logger.warn("server continue (pid: #{server_pid})")
|
516
|
+
end
|
517
|
+
when :stat_get_and_reset
|
518
|
+
if (@sysop.send_signal(server_pid, SIGNAL_STAT_GET_AND_RESET)) then
|
519
|
+
@logger.info("stat get(reset: true) (pid: #{server_pid})")
|
520
|
+
else
|
521
|
+
@logger.error("failed to stat get(reset: true) (pid: #{server_pid})")
|
522
|
+
end
|
523
|
+
when :stat_get_no_reset
|
524
|
+
if (@sysop.send_signal(server_pid, SIGNAL_STAT_GET_NO_RESET)) then
|
525
|
+
@logger.info("stat get(reset: false) (pid: #{server_pid})")
|
526
|
+
else
|
527
|
+
@logger.error("failed to stat get(reset: false) (pid: #{server_pid})")
|
528
|
+
end
|
529
|
+
when :stat_stop
|
530
|
+
if (@sysop.send_signal(server_pid, SIGNAL_STAT_STOP)) then
|
531
|
+
@logger.info("stat stop (pid: #{server_pid})")
|
532
|
+
else
|
533
|
+
@logger.error("failed to stat stop (pid: #{server_pid})")
|
534
|
+
end
|
391
535
|
else
|
392
|
-
@logger.warn("
|
536
|
+
@logger.warn("internal warning: unknown signal operation <#{sig_ope.inspect}>")
|
393
537
|
end
|
394
|
-
when :stat_get_and_reset
|
395
|
-
@logger.info("stat get(reset: true) (pid: #{server_pid})")
|
396
|
-
@sysop.send_signal(server_pid, SIGNAL_STAT_GET_AND_RESET) or @logger.error("failed to stat get(reset: true) (pid: #{server_pid})")
|
397
|
-
when :stat_get_no_reset
|
398
|
-
@logger.info("stat get(reset: false) (pid: #{server_pid})")
|
399
|
-
@sysop.send_signal(server_pid, SIGNAL_STAT_GET_NO_RESET) or @logger.error("failed to stat get(reset: false) (pid: #{server_pid})")
|
400
|
-
when :stat_stop
|
401
|
-
@logger.info("stat stop (pid: #{server_pid})")
|
402
|
-
@sysop.send_signal(server_pid, SIGNAL_STAT_STOP) or @logger.error("failed to stat stop (pid: #{server_pid})")
|
403
|
-
else
|
404
|
-
@logger.warn("internal warning: unknown signal operation <#{sig_ope.inspect}>")
|
405
538
|
end
|
406
|
-
end
|
407
539
|
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
540
|
+
for pid in @process_wait_count_table.keys
|
541
|
+
if (@sysop.wait(pid, Process::WNOHANG)) then
|
542
|
+
@logger.info("server stop completed (pid: #{pid})")
|
543
|
+
@process_wait_count_table.delete(pid)
|
544
|
+
else
|
545
|
+
@process_wait_count_table[pid] += 1
|
546
|
+
if (@process_wait_count_table[pid] >= 2) then
|
547
|
+
@logger.warn("not stopped server process (pid: #{pid})")
|
548
|
+
end
|
416
549
|
end
|
417
550
|
end
|
418
551
|
end
|
419
|
-
end
|
420
552
|
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
553
|
+
if (server_pid) then
|
554
|
+
case (@stop_state)
|
555
|
+
when :graceful
|
556
|
+
@logger.info("server graceful stop (pid: #{server_pid})")
|
557
|
+
unless (server_stop_graceful(server_pid)) then
|
558
|
+
@logger.fatal('failed to stop daemon.')
|
559
|
+
return 1
|
560
|
+
end
|
561
|
+
unless (@sysop.wait(server_pid)) then
|
562
|
+
@logger.fatal('failed to stop daemon.')
|
563
|
+
return 1
|
564
|
+
end
|
565
|
+
when :forced
|
566
|
+
@logger.info("server forced stop (pid: #{server_pid})")
|
567
|
+
unless (server_stop_forced(server_pid)) then
|
568
|
+
@logger.fatal('failed to stop daemon.')
|
569
|
+
return 1
|
570
|
+
end
|
571
|
+
unless (@sysop.wait(server_pid)) then
|
572
|
+
@logger.fatal('failed to stop daemon.')
|
573
|
+
return 1
|
574
|
+
end
|
575
|
+
else
|
576
|
+
@logger.warn("internal warning: unknown stop state <#{@stop_state.inspect}>")
|
442
577
|
end
|
443
578
|
else
|
444
|
-
@logger.
|
445
|
-
|
579
|
+
@logger.warn('no server to stop.')
|
580
|
+
end
|
581
|
+
ensure
|
582
|
+
server_socket_local_address = server_socket.local_address # get local_address before close(2)
|
583
|
+
if (@sysop.close(server_socket)) then
|
584
|
+
@logger.info("close server socket: #{server_socket_local_address.inspect_sockaddr}")
|
585
|
+
else
|
586
|
+
@logger.warn("failed to close server socket: #{server_socket_local_address.inspect_sockaddr}")
|
587
|
+
end
|
588
|
+
if (server_address.type == :unix) then
|
589
|
+
if (@sysop.unlink(server_address.path)) then
|
590
|
+
@logger.info("delete unix server socket: #{server_address}")
|
591
|
+
else
|
592
|
+
@logger.warn("failed to delete unix server socket: #{server_address}")
|
593
|
+
end
|
446
594
|
end
|
447
|
-
else
|
448
|
-
@logger.warn('no server to stop.')
|
449
595
|
end
|
450
596
|
|
451
597
|
@logger.info('daemon stop.')
|
data/lib/riser/server.rb
CHANGED
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
require 'io/wait'
|
4
4
|
require 'socket'
|
5
|
-
require 'uri'
|
6
5
|
|
7
6
|
module Riser
|
8
7
|
class TimeoutSizedQueue
|
@@ -164,123 +163,6 @@ module Riser
|
|
164
163
|
end
|
165
164
|
end
|
166
165
|
|
167
|
-
class SocketAddress
|
168
|
-
def initialize(type)
|
169
|
-
@type = type
|
170
|
-
end
|
171
|
-
|
172
|
-
attr_reader :type
|
173
|
-
|
174
|
-
def to_a
|
175
|
-
[ @type ]
|
176
|
-
end
|
177
|
-
|
178
|
-
def to_s
|
179
|
-
to_a.map{|s|
|
180
|
-
if (s.to_s.include? ':') then
|
181
|
-
"[#{s}]"
|
182
|
-
else
|
183
|
-
s
|
184
|
-
end
|
185
|
-
}.join(':')
|
186
|
-
end
|
187
|
-
|
188
|
-
def ==(other)
|
189
|
-
if (other.is_a? SocketAddress) then
|
190
|
-
self.to_a == other.to_a
|
191
|
-
else
|
192
|
-
false
|
193
|
-
end
|
194
|
-
end
|
195
|
-
|
196
|
-
def eql?(other)
|
197
|
-
self == other
|
198
|
-
end
|
199
|
-
|
200
|
-
def hash
|
201
|
-
to_a.hash ^ self.class.hash
|
202
|
-
end
|
203
|
-
|
204
|
-
def self.parse(config)
|
205
|
-
unsquare = proc{|s| s.sub(/\A \[/x, '').sub(/\] \z/x, '') }
|
206
|
-
case (config)
|
207
|
-
when String
|
208
|
-
case (config)
|
209
|
-
when /\A tcp:/x
|
210
|
-
uri = URI(config)
|
211
|
-
if (uri.host && uri.port) then
|
212
|
-
return TCPSocketAddress.new(unsquare.call(uri.host), uri.port)
|
213
|
-
end
|
214
|
-
when /\A unix:/x
|
215
|
-
uri = URI(config)
|
216
|
-
if (uri.path && ! uri.path.empty?) then
|
217
|
-
return UNIXSocketAddress.new(uri.path)
|
218
|
-
end
|
219
|
-
when %r"\A [A-Za-z]+:/"x
|
220
|
-
# unknown URI scheme
|
221
|
-
when /\A (\S+):(\d+) \z/x
|
222
|
-
host = $1
|
223
|
-
port = $2.to_i
|
224
|
-
return TCPSocketAddress.new(unsquare.call(host), port)
|
225
|
-
end
|
226
|
-
when Hash
|
227
|
-
if (type = config[:type] || config['type']) then
|
228
|
-
case (type.to_s)
|
229
|
-
when 'tcp'
|
230
|
-
host = config[:host] || config['host']
|
231
|
-
port = config[:port] || config['port']
|
232
|
-
if (host && (host.is_a? String) && port && (port.is_a? Integer)) then
|
233
|
-
return TCPSocketAddress.new(unsquare.call(host), port)
|
234
|
-
end
|
235
|
-
when 'unix'
|
236
|
-
path = config[:path] || config['path']
|
237
|
-
if (path && (path.is_a? String) && ! path.empty?) then
|
238
|
-
return UNIXSocketAddress.new(path)
|
239
|
-
end
|
240
|
-
end
|
241
|
-
end
|
242
|
-
end
|
243
|
-
|
244
|
-
return
|
245
|
-
end
|
246
|
-
end
|
247
|
-
|
248
|
-
class TCPSocketAddress < SocketAddress
|
249
|
-
def initialize(host, port)
|
250
|
-
super(:tcp)
|
251
|
-
@host = host
|
252
|
-
@port = port
|
253
|
-
end
|
254
|
-
|
255
|
-
attr_reader :host
|
256
|
-
attr_reader :port
|
257
|
-
|
258
|
-
def to_a
|
259
|
-
super << @host << @port
|
260
|
-
end
|
261
|
-
|
262
|
-
def open_server
|
263
|
-
TCPServer.new(@host, @port)
|
264
|
-
end
|
265
|
-
end
|
266
|
-
|
267
|
-
class UNIXSocketAddress < SocketAddress
|
268
|
-
def initialize(path)
|
269
|
-
super(:unix)
|
270
|
-
@path = path
|
271
|
-
end
|
272
|
-
|
273
|
-
attr_reader :path
|
274
|
-
|
275
|
-
def to_a
|
276
|
-
super << @path
|
277
|
-
end
|
278
|
-
|
279
|
-
def open_server
|
280
|
-
UNIXServer.new(@path)
|
281
|
-
end
|
282
|
-
end
|
283
|
-
|
284
166
|
module ServerSignal
|
285
167
|
SIGNAL_STOP_GRACEFUL = :TERM
|
286
168
|
SIGNAL_STOP_FORCED = :INT
|
@@ -417,6 +299,9 @@ module Riser
|
|
417
299
|
# _server_socket is a dummy argument to call like
|
418
300
|
# SocketProcessDispatcher#start.
|
419
301
|
def start(_server_socket=nil)
|
302
|
+
error_lock = Mutex.new
|
303
|
+
last_error = nil
|
304
|
+
|
420
305
|
@preprocess.call
|
421
306
|
begin
|
422
307
|
queue = TimeoutSizedQueue.new(@thread_queue_size, name: @thread_queue_name)
|
@@ -424,13 +309,19 @@ module Riser
|
|
424
309
|
thread_list = []
|
425
310
|
@thread_num.times{|i|
|
426
311
|
thread_list << Thread.new{
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
312
|
+
begin
|
313
|
+
Thread.current[:number] = i
|
314
|
+
while (socket = queue.pop)
|
315
|
+
begin
|
316
|
+
@dispatch.call(socket)
|
317
|
+
ensure
|
318
|
+
socket.close unless socket.closed?
|
319
|
+
end
|
433
320
|
end
|
321
|
+
rescue
|
322
|
+
error_lock.synchronize{
|
323
|
+
last_error = $!
|
324
|
+
}
|
434
325
|
end
|
435
326
|
}
|
436
327
|
}
|
@@ -438,12 +329,14 @@ module Riser
|
|
438
329
|
catch (:end_of_server) {
|
439
330
|
while (true)
|
440
331
|
begin
|
332
|
+
error_lock.synchronize{ last_error } and @stop_state = :forced
|
441
333
|
@stop_state and throw(:end_of_server)
|
442
334
|
apply_signal_stat(queue)
|
443
335
|
socket = @accept.call
|
444
336
|
end until (socket)
|
445
337
|
|
446
338
|
until (queue.push(socket, @thread_queue_polling_timeout_seconds))
|
339
|
+
error_lock.synchronize{ last_error } and @stop_state = :forced
|
447
340
|
if (@stop_state == :forced) then
|
448
341
|
socket.close
|
449
342
|
@accept_return.call
|
@@ -475,6 +368,12 @@ module Riser
|
|
475
368
|
@postprocess.call
|
476
369
|
end
|
477
370
|
|
371
|
+
error_lock.synchronize{
|
372
|
+
if (last_error) then
|
373
|
+
raise last_error
|
374
|
+
end
|
375
|
+
}
|
376
|
+
|
478
377
|
nil
|
479
378
|
end
|
480
379
|
end
|
@@ -678,7 +577,11 @@ module Riser
|
|
678
577
|
|
679
578
|
@process_dispatcher.accept{
|
680
579
|
if (server_socket.wait_readable(@accept_polling_timeout_seconds) != nil) then
|
681
|
-
|
580
|
+
begin
|
581
|
+
server_socket.accept_nonblock
|
582
|
+
rescue IO::WaitReadable
|
583
|
+
nil # to avoid conflicting accept(2) at server restart overlap
|
584
|
+
end
|
682
585
|
end
|
683
586
|
}
|
684
587
|
@process_dispatcher.accept_return(&NO_CALL)
|
@@ -834,7 +737,11 @@ module Riser
|
|
834
737
|
@dispatcher.postprocess(&@postprocess)
|
835
738
|
@dispatcher.accept{
|
836
739
|
if (server_socket.wait_readable(@accept_polling_timeout_seconds) != nil) then
|
837
|
-
|
740
|
+
begin
|
741
|
+
server_socket.accept_nonblock
|
742
|
+
rescue IO::WaitReadable
|
743
|
+
nil # to avoid conflicting accept(2) at server restart overlap
|
744
|
+
end
|
838
745
|
end
|
839
746
|
}
|
840
747
|
@dispatcher.accept_return(&NO_CALL)
|
@@ -0,0 +1,165 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'socket'
|
4
|
+
require 'uri'
|
5
|
+
|
6
|
+
module Riser
|
7
|
+
class SocketAddress
|
8
|
+
def initialize(type, backlog=nil)
|
9
|
+
@type = type
|
10
|
+
@backlog = backlog
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :type
|
14
|
+
attr_reader :backlog
|
15
|
+
|
16
|
+
def to_address
|
17
|
+
[ @type ]
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_option
|
21
|
+
option = {}
|
22
|
+
option[:backlog] = @backlog if @backlog
|
23
|
+
option
|
24
|
+
end
|
25
|
+
|
26
|
+
def ==(other)
|
27
|
+
if (other.is_a? SocketAddress) then
|
28
|
+
[ self.to_address, self.to_option ] == [ other.to_address, other.to_option ]
|
29
|
+
else
|
30
|
+
false
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def eql?(other)
|
35
|
+
self == other
|
36
|
+
end
|
37
|
+
|
38
|
+
def hash
|
39
|
+
[ to_address, to_option ].hash ^ self.class.hash
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.parse(config)
|
43
|
+
unsquare = proc{|s| s.sub(/\A \[/x, '').sub(/\] \z/x, '') }
|
44
|
+
case (config)
|
45
|
+
when String
|
46
|
+
case (config)
|
47
|
+
when /\A tcp:/x
|
48
|
+
uri = URI(config)
|
49
|
+
if (uri.host && uri.port) then
|
50
|
+
return TCPSocketAddress.new(unsquare.call(uri.host), uri.port)
|
51
|
+
end
|
52
|
+
when /\A unix:/x
|
53
|
+
uri = URI(config)
|
54
|
+
if (uri.path && ! uri.path.empty?) then
|
55
|
+
return UNIXSocketAddress.new(uri.path)
|
56
|
+
end
|
57
|
+
when %r"\A [A-Za-z]+:/"x
|
58
|
+
# unknown URI scheme
|
59
|
+
when /\A (\S+):(\d+) \z/x
|
60
|
+
host = $1
|
61
|
+
port = $2.to_i
|
62
|
+
return TCPSocketAddress.new(unsquare.call(host), port)
|
63
|
+
end
|
64
|
+
when Hash
|
65
|
+
if (type = config[:type] || config['type']) then
|
66
|
+
case (type.to_s)
|
67
|
+
when 'tcp'
|
68
|
+
host = config[:host] || config['host']
|
69
|
+
port = config[:port] || config['port']
|
70
|
+
backlog = config[:backlog] || config['backlog']
|
71
|
+
if ((host && (host.is_a? String) && ! host.empty?) &&
|
72
|
+
(port && (port.is_a? Integer)) &&
|
73
|
+
(backlog.nil? || (backlog.is_a? Integer)))
|
74
|
+
then
|
75
|
+
return TCPSocketAddress.new(unsquare.call(host), port, backlog)
|
76
|
+
end
|
77
|
+
when 'unix'
|
78
|
+
path = config[:path] || config['path']
|
79
|
+
backlog = config[:backlog] || config['backlog']
|
80
|
+
mode = config[:mode] || config['mode']
|
81
|
+
owner = config[:owner] || config['owner']
|
82
|
+
group = config[:group] || config['group']
|
83
|
+
if ((path && (path.is_a? String) && ! path.empty?) &&
|
84
|
+
(backlog.nil? || (backlog.is_a? Integer)) &&
|
85
|
+
(mode.nil? || (mode.is_a? Integer)) &&
|
86
|
+
(owner.nil? || (owner.is_a? Integer) || ((owner.is_a? String) && ! owner.empty?)) &&
|
87
|
+
(group.nil? || (group.is_a? Integer) || ((group.is_a? String) && ! group.empty?)))
|
88
|
+
then
|
89
|
+
return UNIXSocketAddress.new(path, backlog, mode, owner, group)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
return
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
class TCPSocketAddress < SocketAddress
|
100
|
+
def initialize(host, port, backlog=nil)
|
101
|
+
super(:tcp, backlog)
|
102
|
+
@host = host
|
103
|
+
@port = port
|
104
|
+
end
|
105
|
+
|
106
|
+
attr_reader :host
|
107
|
+
attr_reader :port
|
108
|
+
|
109
|
+
def to_address
|
110
|
+
super << @host << @port
|
111
|
+
end
|
112
|
+
|
113
|
+
def to_s
|
114
|
+
if (@host.include? ':') then
|
115
|
+
"tcp://[#{host}]:#{port}"
|
116
|
+
else
|
117
|
+
"tcp://#{host}:#{port}"
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def open_server
|
122
|
+
TCPServer.new(@host, @port)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
class UNIXSocketAddress < SocketAddress
|
127
|
+
def initialize(path, backlog=nil, mode=nil, owner=nil, group=nil)
|
128
|
+
super(:unix, backlog)
|
129
|
+
@path = path
|
130
|
+
@mode = mode
|
131
|
+
@owner = owner
|
132
|
+
@group = group
|
133
|
+
end
|
134
|
+
|
135
|
+
attr_reader :path
|
136
|
+
attr_reader :mode
|
137
|
+
attr_reader :owner
|
138
|
+
attr_reader :group
|
139
|
+
|
140
|
+
def to_address
|
141
|
+
super << @path
|
142
|
+
end
|
143
|
+
|
144
|
+
def to_option
|
145
|
+
option = super
|
146
|
+
option[:mode] = @mode if @mode
|
147
|
+
option[:owner] = @owner if @owner
|
148
|
+
option[:group] = @group if @group
|
149
|
+
option
|
150
|
+
end
|
151
|
+
|
152
|
+
def to_s
|
153
|
+
"unix:#{@path}"
|
154
|
+
end
|
155
|
+
|
156
|
+
def open_server
|
157
|
+
UNIXServer.new(@path)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# Local Variables:
|
163
|
+
# mode: Ruby
|
164
|
+
# indent-tabs-mode: nil
|
165
|
+
# End:
|
data/lib/riser/version.rb
CHANGED
data/lib/riser.rb
CHANGED
@@ -13,7 +13,7 @@ module Riser
|
|
13
13
|
autoload :ResourceSet, 'riser/resource'
|
14
14
|
autoload :RootProcess, 'riser/daemon'
|
15
15
|
autoload :ServerSignal, 'riser/server'
|
16
|
-
autoload :SocketAddress, 'riser/
|
16
|
+
autoload :SocketAddress, 'riser/sockaddr'
|
17
17
|
autoload :SocketServer, 'riser/server'
|
18
18
|
autoload :StatusFile, 'riser/daemon'
|
19
19
|
autoload :Stream, 'riser/stream'
|
data/riser.gemspec
CHANGED
@@ -10,8 +10,11 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.authors = ["TOKI Yoshinori"]
|
11
11
|
spec.email = ["toki@freedom.ne.jp"]
|
12
12
|
|
13
|
-
spec.summary = %q{Riser}
|
14
|
-
spec.description =
|
13
|
+
spec.summary = %q{Riser is a library of Ruby Infrastructure for cooperative multi-thread/multi-process SERver}
|
14
|
+
spec.description = <<-'EOF'
|
15
|
+
Riser is a library of Ruby Infrastructure for cooperative multi-thread/multi-process SERver.
|
16
|
+
This library is useful to make multi-thread/multi-process socket server and daemon.
|
17
|
+
EOF
|
15
18
|
spec.homepage = "https://github.com/y10k/riser"
|
16
19
|
spec.license = "MIT"
|
17
20
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: riser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- TOKI Yoshinori
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-02-
|
11
|
+
date: 2019-02-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -66,8 +66,9 @@ dependencies:
|
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
|
-
description: Riser is a library of Ruby Infrastructure for cooperative multi-thread/multi-process
|
70
|
-
SERver
|
69
|
+
description: " Riser is a library of Ruby Infrastructure for cooperative multi-thread/multi-process
|
70
|
+
SERver.\n This library is useful to make multi-thread/multi-process socket server
|
71
|
+
and daemon. \n"
|
71
72
|
email:
|
72
73
|
- toki@freedom.ne.jp
|
73
74
|
executables: []
|
@@ -98,6 +99,7 @@ files:
|
|
98
99
|
- lib/riser/resource.rb
|
99
100
|
- lib/riser/server.rb
|
100
101
|
- lib/riser/services.rb
|
102
|
+
- lib/riser/sockaddr.rb
|
101
103
|
- lib/riser/stream.rb
|
102
104
|
- lib/riser/temppath.rb
|
103
105
|
- lib/riser/test.rb
|
@@ -125,5 +127,6 @@ requirements: []
|
|
125
127
|
rubygems_version: 3.0.1
|
126
128
|
signing_key:
|
127
129
|
specification_version: 4
|
128
|
-
summary: Riser
|
130
|
+
summary: Riser is a library of Ruby Infrastructure for cooperative multi-thread/multi-process
|
131
|
+
SERver
|
129
132
|
test_files: []
|