Skip to content

Commit 44286d9

Browse files
feat: multi-core via SO_REUSEPORT + Process.fork
- Each CPU core gets its own Kemal server instance with SO_REUSEPORT - Bypasses Kemal.run to manually create HTTP::Server with reuse_port: true - Properly wires up 404 error handler (setup_404 equivalent) - Parent process manages worker lifecycle with signal forwarding - All 18 validation tests passing
1 parent 98a6da9 commit 44286d9

1 file changed

Lines changed: 40 additions & 2 deletions

File tree

frameworks/kemal/src/server.cr

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,9 +187,47 @@ post "/upload" do |env|
187187
end
188188

189189
# ---------------------------------------------------------------------------
190-
# Server config
190+
# Server config — multi-core via SO_REUSEPORT + fork
191191
# ---------------------------------------------------------------------------
192192

193193
Kemal.config.port = 8080
194194
Kemal.config.env = "production"
195-
Kemal.run
195+
196+
# Disable Kemal's default signal handlers and logging for workers
197+
Kemal.config.shutdown_message = false
198+
Kemal.config.logging = false
199+
200+
worker_count = System.cpu_count.to_i
201+
worker_count = 1 if worker_count < 1
202+
203+
def start_server
204+
# Build Kemal's handler chain manually
205+
Kemal.config.setup
206+
207+
# Register 404 handler (normally done by Kemal.run)
208+
unless Kemal.config.error_handlers.has_key?(404)
209+
error 404 do
210+
render_404
211+
end
212+
end
213+
214+
handlers = Kemal.config.handlers
215+
216+
# Create server with SO_REUSEPORT
217+
server = HTTP::Server.new(handlers)
218+
server.bind_tcp("0.0.0.0", 8080, reuse_port: true)
219+
server.listen
220+
end
221+
222+
if worker_count > 1
223+
pids = [] of Int64
224+
worker_count.times do
225+
child = Process.fork { start_server }
226+
pids << child.pid
227+
end
228+
Signal::INT.trap { pids.each { |p| Process.signal(Signal::TERM, p) rescue nil }; exit }
229+
Signal::TERM.trap { pids.each { |p| Process.signal(Signal::TERM, p) rescue nil }; exit }
230+
sleep
231+
else
232+
start_server
233+
end

0 commit comments

Comments
 (0)