raptor 0.11.0 → 0.13.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/.buildkite/pipeline.yml +100 -0
- data/CHANGELOG.md +11 -0
- data/README.md +47 -31
- data/docs/raptor-vs-puma.md +81 -63
- data/ext/raptor_bpf/reuseport_select.bpf.c +8 -3
- data/ext/raptor_native/raptor_native.c +16 -2
- data/lib/raptor/binder.rb +37 -87
- data/lib/raptor/cli.rb +18 -25
- data/lib/raptor/cluster.rb +385 -82
- data/lib/raptor/http.rb +18 -0
- data/lib/raptor/http1.rb +244 -233
- data/lib/raptor/http2.rb +42 -63
- data/lib/raptor/reactor.rb +21 -52
- data/lib/raptor/reuseport_bpf.rb +27 -5
- data/lib/raptor/server.rb +24 -42
- data/lib/raptor/stats.rb +3 -8
- data/lib/raptor/systemd.rb +1 -1
- data/lib/raptor/version.rb +1 -1
- data/sig/generated/raptor/binder.rbs +34 -80
- data/sig/generated/raptor/cli.rbs +9 -25
- data/sig/generated/raptor/cluster.rbs +183 -85
- data/sig/generated/raptor/http.rbs +10 -0
- data/sig/generated/raptor/http1.rbs +129 -113
- data/sig/generated/raptor/http2.rbs +31 -48
- data/sig/generated/raptor/reactor.rbs +21 -52
- data/sig/generated/raptor/reuseport_bpf.rbs +20 -2
- data/sig/generated/raptor/server.rbs +19 -39
- data/sig/generated/raptor/stats.rbs +3 -8
- metadata +1 -1
data/lib/raptor/binder.rb
CHANGED
|
@@ -5,30 +5,9 @@ require "socket"
|
|
|
5
5
|
require "uri"
|
|
6
6
|
|
|
7
7
|
module Raptor
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
# server sockets, and managing socket options for optimal performance. It
|
|
12
|
-
# supports binding to multiple addresses simultaneously.
|
|
13
|
-
#
|
|
14
|
-
# @example TCP binding
|
|
15
|
-
# binder = Binder.new(["tcp://0.0.0.0:3000", "tcp://[::1]:3000"])
|
|
16
|
-
# puts binder.addresses #=> ["0.0.0.0:3000", "[::1]:3000"]
|
|
17
|
-
# binder.close
|
|
18
|
-
#
|
|
19
|
-
# @example Unix socket binding
|
|
20
|
-
# binder = Binder.new(["unix:///tmp/raptor.sock"])
|
|
21
|
-
# puts binder.addresses #=> ["/tmp/raptor.sock"]
|
|
22
|
-
# binder.close
|
|
23
|
-
#
|
|
24
|
-
# @example SSL binding
|
|
25
|
-
# binder = Binder.new(["ssl://0.0.0.0:443?cert=/path/to.crt&key=/path/to.key"])
|
|
26
|
-
# puts binder.addresses #=> ["ssl://0.0.0.0:443"]
|
|
27
|
-
# binder.close
|
|
28
|
-
#
|
|
29
|
-
# @example Localhost binding
|
|
30
|
-
# binder = Binder.new(["tcp://localhost:8080"])
|
|
31
|
-
# # Binds to both IPv4 and IPv6 loopback addresses
|
|
8
|
+
# Binds `tcp://`, `unix://`, and `ssl://` URIs to listening sockets and
|
|
9
|
+
# holds them for the server. Reconstructs listeners from inherited file
|
|
10
|
+
# descriptors when provided (systemd socket activation, hot restart).
|
|
32
11
|
#
|
|
33
12
|
class Binder
|
|
34
13
|
SOCKET_BACKLOG = 1024
|
|
@@ -38,11 +17,8 @@ module Raptor
|
|
|
38
17
|
def initialize(scheme) = super("unknown scheme: #{scheme.inspect}")
|
|
39
18
|
end
|
|
40
19
|
|
|
41
|
-
#
|
|
42
|
-
#
|
|
43
|
-
# Holds both the underlying TCP server and the SSL context together so
|
|
44
|
-
# the server thread can accept a TCP connection and then perform the SSL
|
|
45
|
-
# handshake in a single coordinated step.
|
|
20
|
+
# Pairs a TCPServer with the SSL context to use for accepted
|
|
21
|
+
# connections.
|
|
46
22
|
#
|
|
47
23
|
SslListener = Data.define(:tcp_server, :ssl_context) do
|
|
48
24
|
# @rbs () -> TCPServer
|
|
@@ -61,28 +37,24 @@ module Raptor
|
|
|
61
37
|
# @rbs @listeners: Array[TCPServer | UNIXServer | SslListener]
|
|
62
38
|
# @rbs @uri_listeners: Hash[String, Array[TCPServer | UNIXServer | SslListener]]
|
|
63
39
|
|
|
64
|
-
#
|
|
40
|
+
# Returns the array of bind URIs.
|
|
65
41
|
#
|
|
66
|
-
# @return [Array<String>]
|
|
42
|
+
# @return [Array<String>]
|
|
67
43
|
attr_reader :bind_uris
|
|
68
44
|
|
|
69
|
-
#
|
|
45
|
+
# Returns the kernel `listen()` queue depth for TCP/SSL listeners.
|
|
70
46
|
#
|
|
71
|
-
# @return [Integer]
|
|
47
|
+
# @return [Integer]
|
|
72
48
|
attr_reader :socket_backlog
|
|
73
49
|
|
|
74
|
-
#
|
|
50
|
+
# Returns the array of listening sockets.
|
|
75
51
|
#
|
|
76
|
-
# @return [Array<TCPServer, UNIXServer, SslListener>]
|
|
52
|
+
# @return [Array<TCPServer, UNIXServer, SslListener>]
|
|
77
53
|
attr_reader :listeners
|
|
78
54
|
|
|
79
|
-
# Creates a new Binder
|
|
80
|
-
#
|
|
81
|
-
#
|
|
82
|
-
# Supports tcp://, unix://, and ssl:// schemes. Localhost is expanded to
|
|
83
|
-
# all available loopback addresses (both IPv4 and IPv6). When `inherited_fds`
|
|
84
|
-
# supplies file descriptors for a URI, the listener is reconstructed from
|
|
85
|
-
# those FDs instead of binding fresh.
|
|
55
|
+
# Creates a new Binder and binds each URI. `localhost` expands to both
|
|
56
|
+
# IPv4 and IPv6 loopback; when `inherited_fds` supplies file descriptors
|
|
57
|
+
# for a URI the listener is rebuilt from those instead of binding fresh.
|
|
86
58
|
#
|
|
87
59
|
# @param bind_uris [Array<String>] array of URI strings to bind to
|
|
88
60
|
# @param socket_backlog [Integer] kernel listen() queue depth for TCP/SSL listeners
|
|
@@ -90,9 +62,6 @@ module Raptor
|
|
|
90
62
|
# @return [void]
|
|
91
63
|
# @raise [UnknownBindSchemeError] if a URI has an unsupported scheme
|
|
92
64
|
#
|
|
93
|
-
# @example
|
|
94
|
-
# binder = Binder.new(["tcp://0.0.0.0:3000", "unix:///tmp/raptor.sock"])
|
|
95
|
-
#
|
|
96
65
|
# @rbs (Array[String] bind_uris, ?socket_backlog: Integer, ?inherited_fds: Hash[String, Array[Integer]]) -> void
|
|
97
66
|
def initialize(bind_uris, socket_backlog: SOCKET_BACKLOG, inherited_fds: {})
|
|
98
67
|
@bind_uris = bind_uris
|
|
@@ -103,15 +72,10 @@ module Raptor
|
|
|
103
72
|
parse
|
|
104
73
|
end
|
|
105
74
|
|
|
106
|
-
# Returns the bound addresses as strings
|
|
75
|
+
# Returns the bound addresses as strings: TCP as `host:port`, Unix as
|
|
76
|
+
# the socket path, SSL as `ssl://host:port`.
|
|
107
77
|
#
|
|
108
|
-
#
|
|
109
|
-
# path, and SSL listeners as "ssl://host:port".
|
|
110
|
-
#
|
|
111
|
-
# @return [Array<String>] address strings for each bound listener
|
|
112
|
-
#
|
|
113
|
-
# @example
|
|
114
|
-
# binder.addresses #=> ["127.0.0.1:3000", "/tmp/raptor.sock", "ssl://0.0.0.0:443"]
|
|
78
|
+
# @return [Array<String>]
|
|
115
79
|
#
|
|
116
80
|
# @rbs () -> Array[String]
|
|
117
81
|
def addresses
|
|
@@ -129,12 +93,10 @@ module Raptor
|
|
|
129
93
|
end
|
|
130
94
|
end
|
|
131
95
|
|
|
132
|
-
# Returns the port
|
|
96
|
+
# Returns the port of the first TCP or SSL listener, or 0 when none
|
|
97
|
+
# is configured.
|
|
133
98
|
#
|
|
134
|
-
#
|
|
135
|
-
# if no TCP or SSL listener is configured (e.g., Unix socket only).
|
|
136
|
-
#
|
|
137
|
-
# @return [Integer] the port number, or 0 if no TCP listener exists
|
|
99
|
+
# @return [Integer]
|
|
138
100
|
#
|
|
139
101
|
# @rbs () -> Integer
|
|
140
102
|
def server_port
|
|
@@ -153,9 +115,7 @@ module Raptor
|
|
|
153
115
|
@listeners.each(&:close)
|
|
154
116
|
end
|
|
155
117
|
|
|
156
|
-
# Returns the file descriptors of every listener, grouped by
|
|
157
|
-
# they were created from. The result is the payload to hand to a successor
|
|
158
|
-
# process via the `inherited_fds:` constructor argument.
|
|
118
|
+
# Returns the file descriptors of every listener, grouped by bind URI.
|
|
159
119
|
#
|
|
160
120
|
# @return [Hash{String => Array<Integer>}]
|
|
161
121
|
#
|
|
@@ -185,24 +145,21 @@ module Raptor
|
|
|
185
145
|
# @rbs () -> void
|
|
186
146
|
def parse
|
|
187
147
|
@uri_listeners = @bind_uris.to_h do |bind_uri|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
[bind_uri, create_listeners(bind_uri)]
|
|
192
|
-
end
|
|
148
|
+
uri = URI.parse(bind_uri)
|
|
149
|
+
filenos = @inherited_fds[bind_uri]
|
|
150
|
+
[bind_uri, filenos ? restore_listeners(uri, filenos) : create_listeners(uri)]
|
|
193
151
|
end
|
|
194
152
|
@listeners = @uri_listeners.values.flatten
|
|
195
153
|
end
|
|
196
154
|
|
|
197
|
-
# Creates fresh listeners for the given
|
|
155
|
+
# Creates fresh listeners for the given URI.
|
|
198
156
|
#
|
|
199
|
-
# @param
|
|
157
|
+
# @param uri [URI] the parsed bind URI
|
|
200
158
|
# @return [Array<TCPServer, UNIXServer, SslListener>]
|
|
201
159
|
# @raise [UnknownBindSchemeError] if the URI scheme is not supported
|
|
202
160
|
#
|
|
203
|
-
# @rbs (
|
|
204
|
-
def create_listeners(
|
|
205
|
-
uri = URI.parse(bind_uri)
|
|
161
|
+
# @rbs (URI::Generic uri) -> Array[TCPServer | UNIXServer | SslListener]
|
|
162
|
+
def create_listeners(uri)
|
|
206
163
|
case uri.scheme
|
|
207
164
|
when "tcp"
|
|
208
165
|
create_tcp_listeners(uri.host, uri.port)
|
|
@@ -215,17 +172,16 @@ module Raptor
|
|
|
215
172
|
end
|
|
216
173
|
end
|
|
217
174
|
|
|
218
|
-
# Reconstructs listeners for the given
|
|
175
|
+
# Reconstructs listeners for the given URI from inherited file
|
|
219
176
|
# descriptors.
|
|
220
177
|
#
|
|
221
|
-
# @param
|
|
178
|
+
# @param uri [URI] the parsed bind URI the FDs were bound to
|
|
222
179
|
# @param filenos [Array<Integer>] file descriptors to wrap
|
|
223
180
|
# @return [Array<TCPServer, UNIXServer, SslListener>]
|
|
224
181
|
# @raise [UnknownBindSchemeError] if the URI scheme is not supported
|
|
225
182
|
#
|
|
226
|
-
# @rbs (
|
|
227
|
-
def restore_listeners(
|
|
228
|
-
uri = URI.parse(bind_uri)
|
|
183
|
+
# @rbs (URI::Generic uri, Array[Integer] filenos) -> Array[TCPServer | UNIXServer | SslListener]
|
|
184
|
+
def restore_listeners(uri, filenos)
|
|
229
185
|
case uri.scheme
|
|
230
186
|
when "tcp"
|
|
231
187
|
filenos.map { |fileno| TCPServer.for_fd(fileno) }
|
|
@@ -267,11 +223,9 @@ module Raptor
|
|
|
267
223
|
[tcp_server]
|
|
268
224
|
end
|
|
269
225
|
|
|
270
|
-
# Creates a Unix domain server socket at the given path.
|
|
271
|
-
#
|
|
272
|
-
#
|
|
273
|
-
# is not currently in use). Registers an at_exit hook to clean up the
|
|
274
|
-
# socket file on normal process exit.
|
|
226
|
+
# Creates a Unix domain server socket at the given path. Removes stale
|
|
227
|
+
# socket files left by crashed processes, and cleans the file up on
|
|
228
|
+
# the master's clean exit.
|
|
275
229
|
#
|
|
276
230
|
# @param path [String] filesystem path for the Unix socket
|
|
277
231
|
# @return [Array<UNIXServer>] array containing the created Unix server socket
|
|
@@ -306,15 +260,11 @@ module Raptor
|
|
|
306
260
|
at_exit { File.delete(path) rescue nil if Process.pid == master_pid }
|
|
307
261
|
end
|
|
308
262
|
|
|
309
|
-
# Creates SSL server sockets for the given host
|
|
310
|
-
#
|
|
311
|
-
# Wraps each TCP listener with an SSL context to produce SslListener objects.
|
|
312
|
-
# The ssl_params hash must include "cert" and "key" entries pointing to the
|
|
313
|
-
# certificate and private key files respectively.
|
|
263
|
+
# Creates SSL server sockets for the given host and port.
|
|
314
264
|
#
|
|
315
265
|
# @param host [String, nil] hostname or IP address to bind to
|
|
316
266
|
# @param port [Integer, nil] port number to bind to
|
|
317
|
-
# @param ssl_params [Hash<String, String>] SSL options (
|
|
267
|
+
# @param ssl_params [Hash<String, String>] SSL options (`cert` and `key` file paths)
|
|
318
268
|
# @return [Array<SslListener>] array containing the created SSL listener(s)
|
|
319
269
|
#
|
|
320
270
|
# @rbs (String? host, Integer? port, Hash[String, String] ssl_params) -> Array[SslListener]
|
data/lib/raptor/cli.rb
CHANGED
|
@@ -56,6 +56,11 @@ module Raptor
|
|
|
56
56
|
worker_timeout: 60,
|
|
57
57
|
worker_drain_timeout: 25,
|
|
58
58
|
worker_shutdown_timeout: 30,
|
|
59
|
+
refork_after: (RUBY_PLATFORM.include?("linux") ? 1000 : nil),
|
|
60
|
+
before_fork: [].freeze,
|
|
61
|
+
before_worker_boot: [].freeze,
|
|
62
|
+
before_worker_shutdown: [].freeze,
|
|
63
|
+
before_refork: [].freeze,
|
|
59
64
|
stats_file: "tmp/raptor.json",
|
|
60
65
|
pid_file: nil,
|
|
61
66
|
stdout_file: nil,
|
|
@@ -65,11 +70,9 @@ module Raptor
|
|
|
65
70
|
|
|
66
71
|
DEFAULT_CONFIG_PATHS = ["raptor.rb", "config/raptor.rb"].freeze
|
|
67
72
|
|
|
68
|
-
# Loads a
|
|
69
|
-
#
|
|
70
|
-
#
|
|
71
|
-
# the same as in a regular Ruby script. The final expression must be a Hash
|
|
72
|
-
# of cluster options (the same keys accepted by {Raptor::Cluster#initialize}).
|
|
73
|
+
# Loads a Ruby config file and returns the options hash it evaluates
|
|
74
|
+
# to. Evaluated at the top level so `Raptor::*` constants resolve the
|
|
75
|
+
# same as in a regular script.
|
|
73
76
|
#
|
|
74
77
|
# @param path [String] path to a Ruby file that evaluates to a Hash
|
|
75
78
|
# @return [Hash{Symbol => untyped}] cluster options
|
|
@@ -86,9 +89,6 @@ module Raptor
|
|
|
86
89
|
# Returns the first existing path in {DEFAULT_CONFIG_PATHS} resolved
|
|
87
90
|
# against `root`, or nil if none exist.
|
|
88
91
|
#
|
|
89
|
-
# Used to pick up a project-local config file when no `-c`/`--config`
|
|
90
|
-
# flag was supplied.
|
|
91
|
-
#
|
|
92
92
|
# @param root [String] directory to resolve the default paths against
|
|
93
93
|
# @return [String, nil] the config path, or nil if no default file exists
|
|
94
94
|
#
|
|
@@ -101,22 +101,14 @@ module Raptor
|
|
|
101
101
|
# @rbs @options: Hash[Symbol, untyped]
|
|
102
102
|
# @rbs @parser: OptionParser
|
|
103
103
|
|
|
104
|
-
# Creates a new CLI instance
|
|
105
|
-
#
|
|
106
|
-
#
|
|
107
|
-
# options accordingly. A rackup file can be provided as the first
|
|
108
|
-
# positional argument (defaults to config.ru).
|
|
104
|
+
# Creates a new CLI instance from `argv`. A rackup file may be given
|
|
105
|
+
# as the first positional argument; every other value is parsed as a
|
|
106
|
+
# flag.
|
|
109
107
|
#
|
|
110
108
|
# @param argv [Array<String>] command-line arguments to parse
|
|
111
109
|
# @return [void]
|
|
112
110
|
# @raise [OptionParser::ParseError] if invalid options are provided
|
|
113
111
|
#
|
|
114
|
-
# @example With rackup file
|
|
115
|
-
# cli = CLI.new(["my_app.ru", "-w", "4"])
|
|
116
|
-
#
|
|
117
|
-
# @example With options only
|
|
118
|
-
# cli = CLI.new(["-t", "8", "-r", "2"])
|
|
119
|
-
#
|
|
120
112
|
# @rbs (Array[String] argv) -> void
|
|
121
113
|
def initialize(argv)
|
|
122
114
|
@options = DEFAULT_OPTIONS.dup
|
|
@@ -175,12 +167,9 @@ module Raptor
|
|
|
175
167
|
end
|
|
176
168
|
end
|
|
177
169
|
|
|
178
|
-
#
|
|
179
|
-
#
|
|
180
|
-
#
|
|
181
|
-
# can be applied as a base layer that CLI args then override. All four
|
|
182
|
-
# OptionParser-accepted forms (`-c PATH`, `-cPATH`, `--config PATH`,
|
|
183
|
-
# `--config=PATH`) are recognized.
|
|
170
|
+
# Returns the path from a `-c`/`--config` flag in `argv`, recognising
|
|
171
|
+
# all four OptionParser-accepted forms (`-c PATH`, `-cPATH`,
|
|
172
|
+
# `--config PATH`, `--config=PATH`).
|
|
184
173
|
#
|
|
185
174
|
# @param argv [Array<String>] command-line arguments to scan
|
|
186
175
|
# @return [String, nil] the config path, or nil if no flag was supplied
|
|
@@ -312,6 +301,10 @@ module Raptor
|
|
|
312
301
|
@options[:worker_shutdown_timeout] = timeout
|
|
313
302
|
end
|
|
314
303
|
|
|
304
|
+
opts.on("--refork-after NUM", Integer, "Refork workers from a warmed source after any worker crosses NUM requests; 0 disables (default: 1000)") do |num|
|
|
305
|
+
@options[:refork_after] = num
|
|
306
|
+
end
|
|
307
|
+
|
|
315
308
|
opts.on("--stats-file PATH", String, "Stats file path (default: tmp/raptor.json)") do |path|
|
|
316
309
|
@options[:stats_file] = path
|
|
317
310
|
end
|