uninterruptible 1.1.1 → 2.0.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/CHANGELOG.md +6 -0
- data/README.md +6 -4
- data/lib/uninterruptible/server.rb +51 -16
- data/lib/uninterruptible/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7f8d400309d6f9bf1354f04b9002aad29d077d2e
|
4
|
+
data.tar.gz: 3cb256b49089b46fdf7820594a6919c71e9d4d9f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4649cdbf7b3eff9fff3709f343513cf36a21814e03c1092c0d09d4a6ff4905bfdfb32ae61866a41caf513867623689429c44dfe5a8df57e824c2173699c52c63
|
7
|
+
data.tar.gz: d05d7c375ab9bd546691516abbbc7205a4221cfa60ffa8ddd3ea072fa9028fb12e51dc1951f83053605e7dd37ef9793dbf9c06e29631d7d00425128495ff82bb
|
data/CHANGELOG.md
ADDED
data/README.md
CHANGED
@@ -90,18 +90,20 @@ completed.
|
|
90
90
|
By default, Uninterruptible operates on a very simple one thread per connection concurrency model. If you'd like to use
|
91
91
|
something more advanced such as a threadpool or an event driven pattern you can define this in your server class.
|
92
92
|
|
93
|
-
By overriding `
|
94
|
-
call `process_request` from this method and still implement `handle_request` to do the bulk of the work since
|
93
|
+
By overriding `accept_client_connection` you can change how connections are accepted and handled. It is recommended
|
94
|
+
that you call `process_request` from this method and still implement `handle_request` to do the bulk of the work since
|
95
95
|
`process_request` tracks the number of active connections to the server.
|
96
96
|
|
97
|
+
`accept_client_connection` is called whenever a connection is waiting to be accepted on the socket server.
|
98
|
+
|
97
99
|
If you wanted to implement a threadpool to process your requests you could do the following:
|
98
100
|
|
99
101
|
```ruby
|
100
102
|
class EchoServer
|
101
103
|
# ...
|
102
104
|
|
103
|
-
def
|
104
|
-
|
105
|
+
def accept_client_connection
|
106
|
+
@worker_threads ||= 4.times.map do
|
105
107
|
Thread.new { worker_loop }
|
106
108
|
end
|
107
109
|
|
@@ -30,7 +30,7 @@ module Uninterruptible
|
|
30
30
|
module Server
|
31
31
|
def self.included(base)
|
32
32
|
base.class_eval do
|
33
|
-
attr_reader :active_connections, :socket_server, :mutex
|
33
|
+
attr_reader :active_connections, :socket_server, :signal_pipe_r, :signal_pipe_w, :mutex
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
@@ -55,7 +55,7 @@ module Uninterruptible
|
|
55
55
|
establish_socket_server
|
56
56
|
write_pidfile
|
57
57
|
setup_signal_traps
|
58
|
-
|
58
|
+
select_loop
|
59
59
|
end
|
60
60
|
|
61
61
|
# @abstract Override this method to process incoming requests. Each request is handled in it's own thread.
|
@@ -68,17 +68,32 @@ module Uninterruptible
|
|
68
68
|
|
69
69
|
private
|
70
70
|
|
71
|
-
# Start a blocking loop which
|
72
|
-
#
|
73
|
-
def
|
71
|
+
# Start a blocking loop which awaits new connections before calling #accept_client_connection. Also monitors
|
72
|
+
# signal_pipe_r for processing any signals sent to the process.
|
73
|
+
def select_loop
|
74
74
|
loop do
|
75
|
-
|
76
|
-
|
77
|
-
|
75
|
+
readable, = IO.select([socket_server, signal_pipe_r])
|
76
|
+
readable.each do |reader|
|
77
|
+
if reader == socket_server
|
78
|
+
accept_client_connection
|
79
|
+
elsif reader == signal_pipe_r
|
80
|
+
signal = reader.gets.chomp
|
81
|
+
process_signal(signal)
|
82
|
+
end
|
78
83
|
end
|
79
84
|
end
|
80
85
|
end
|
81
86
|
|
87
|
+
# Accept a waiting connection. Should only be called when it is known a connection is waiting, from an IO.select
|
88
|
+
# loop for example. By default this creates one thread per connection. Override this method to provide a new
|
89
|
+
# concurrency model.
|
90
|
+
def accept_client_connection
|
91
|
+
Thread.start(socket_server.accept_nonblock) do |client_socket|
|
92
|
+
logger.debug "Accepted connection from #{client_socket.peeraddr.last}"
|
93
|
+
process_request(client_socket)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
82
97
|
# Keeps a track of the number of active connections and passes the client connection to #handle_request for the
|
83
98
|
# user to do with as they wish. Automatically closes a connection once #handle_request has completed.
|
84
99
|
#
|
@@ -121,18 +136,35 @@ module Uninterruptible
|
|
121
136
|
File.write(server_configuration.pidfile_path, Process.pid.to_s)
|
122
137
|
end
|
123
138
|
|
124
|
-
# Catch TERM and USR1 signals which control the lifecycle of the server.
|
139
|
+
# Catch TERM and USR1 signals which control the lifecycle of the server. These get written to an internal pipe
|
140
|
+
# which will be picked up by the main accept_connection loop and passed to #process_signal
|
125
141
|
def setup_signal_traps
|
126
|
-
|
127
|
-
Signal.trap('TERM') do
|
128
|
-
Process.exit(1) if $shutdown
|
142
|
+
@signal_pipe_r, @signal_pipe_w = IO.pipe
|
129
143
|
|
130
|
-
|
131
|
-
|
144
|
+
%w(TERM USR1).each do |signal_name|
|
145
|
+
trap(signal_name) do
|
146
|
+
@signal_pipe_w.puts(signal_name)
|
147
|
+
end
|
132
148
|
end
|
149
|
+
end
|
133
150
|
|
134
|
-
|
135
|
-
|
151
|
+
# When a signal has been caught, it should be passed here for the appropriate action to be taken
|
152
|
+
# On TERM begin a graceful shutdown, if a second TERM is received shutdown immediately with an exit code of 1
|
153
|
+
# On USR1 begin a hot restart which will bring up a new copy of the server and then shut down the old one
|
154
|
+
#
|
155
|
+
# @param [String] signal_name Signal to process
|
156
|
+
def process_signal(signal_name)
|
157
|
+
if signal_name == 'TERM'
|
158
|
+
if $shutdown
|
159
|
+
logger.info "TERM received again, exiting immediately"
|
160
|
+
Process.exit(1) if $shutdown
|
161
|
+
else
|
162
|
+
logger.info "TERM received, starting graceful shutdown"
|
163
|
+
$shutdown = true
|
164
|
+
graceful_shutdown
|
165
|
+
end
|
166
|
+
elsif signal_name == 'USR1'
|
167
|
+
logger.info "USR1 received, hot restart in progress"
|
136
168
|
hot_restart
|
137
169
|
end
|
138
170
|
end
|
@@ -142,9 +174,12 @@ module Uninterruptible
|
|
142
174
|
socket_server.close unless socket_server.closed?
|
143
175
|
|
144
176
|
until active_connections.zero?
|
177
|
+
logger.debug "#{active_connections} connections still active"
|
145
178
|
sleep 0.5
|
146
179
|
end
|
147
180
|
|
181
|
+
logger.debug "No more active connections. Exiting'"
|
182
|
+
|
148
183
|
Process.exit(0)
|
149
184
|
end
|
150
185
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: uninterruptible
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dan Wentworth
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-07-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -65,6 +65,7 @@ files:
|
|
65
65
|
- ".gitignore"
|
66
66
|
- ".rspec"
|
67
67
|
- ".travis.yml"
|
68
|
+
- CHANGELOG.md
|
68
69
|
- Gemfile
|
69
70
|
- LICENSE.txt
|
70
71
|
- README.md
|