mirror of
https://github.com/valyala/fasthttp.git
synced 2026-06-24 17:35:53 +03:00
481e579af9
* feat(prefork): add WatchMaster and callback support for child process management * feat(prefork): add CommandProducer for customizable child process commands * refactor(prefork): improve comments and parameter order in ListenAndServeTLS * refactor(prefork): enhance logging message and clarify OnChildRecover callback comment * fix(prefork): add Windows support to watchMaster On Windows, os.Getppid() returns a static PID that doesn't change when the parent exits (no reparenting). Use FindProcess+Wait instead, which correctly detects parent exit. Also document why masterPID comparison works for Docker containers (master PID 1 case). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(prefork): extract listenAsChild to eliminate DRY violation The three ListenAndServe* methods had identical child setup code (listen, set ln, watch master). Extract to listenAsChild() for cleaner code. Also add comment for the magic file descriptor number 3. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(prefork): restore upstream ListenAndServeTLS parameter order Keep upstream's (addr, certKey, certFile) signature to avoid breaking callers. Fix the doc comment to match the actual parameter order instead. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(prefork): address lint errors and review feedback Lint fixes: - Remove unused Reuseport field write in test (govet/unusedwrite) - Replace fmt.Errorf with errors.New for static errors (perfsprint) Review feedback (Copilot): - Validate CommandProducer returns a started command (nil/Process check) - Clarify ListenAndServeTLS doc: parameter order and internal forwarding - Use hermetic test binary re-exec instead of external 'go' binary - Rename misleading test to reflect what it actually asserts Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(prefork): address maintainer review feedback - watchMaster: log errors from FindProcess/Wait instead of swallowing - watchMaster: don't call OnMasterDeath if FindProcess fails - OnChildRecover: change signature to func(pid int), drop unused error return - OnChildSpawn: add comment clarifying deferred cleanup handles the child - CommandProducer: improve docs describing contract and use cases Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(prefork): address erikdubbelboer review feedback - OnChildRecover: signature changed to func(oldPid, newPid int) so callers can track which process was replaced - OnChildSpawn: also called for recovered children (a recovered child is still a spawned child) - watchMaster: call OnMasterDeath when FindProcess fails (process is most likely gone) - CommandProducer: document that FASTHTTP_PREFORK_CHILD=1 must be set in the child env, and what the default does when nil Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(prefork): avoid zombie processes and replace shallow tests - Move Wait() goroutine before OnChildSpawn so Kill()+Wait() works correctly if a callback fails and the deferred cleanup runs - Add Wait() call in deferred cleanup after Kill() to reap children - Same fix in recovery loop - Remove shallow callback tests that only tested Go compiler - Add Test_Prefork_Lifecycle: runs full prefork with CommandProducer, verifies callbacks fire in correct order with correct arguments Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(prefork): ensure recovery default stays positive * test(prefork): isolate lifecycle tests * fix(prefork): tighten recovery callback flow --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Prefork
Server prefork implementation.
Preforks master process between several child processes increases performance, because Go doesn't have to share and manage memory between cores.
WARNING: using prefork prevents the use of any global state!. Things like in-memory caches won't work.
- How it works:
import (
"github.com/valyala/fasthttp"
"github.com/valyala/fasthttp/prefork"
)
server := &fasthttp.Server{
// Your configuration
}
// Wraps the server with prefork
preforkServer := prefork.New(server)
if err := preforkServer.ListenAndServe(":8080"); err != nil {
panic(err)
}
Benchmarks
Environment:
- Machine: MacBook Pro 13-inch, 2017
- OS: MacOS 10.15.3
- Go: go1.13.6 darwin/amd64
Handler code:
func requestHandler(ctx *fasthttp.RequestCtx) {
// Simulates some hard work
time.Sleep(100 * time.Millisecond)
}
Test command:
$ wrk -H 'Host: localhost' -H 'Accept: text/plain,text/html;q=0.9,application/xhtml+xml;q=0.9,application/xml;q=0.8,*/*;q=0.7' -H 'Connection: keep-alive' --latency -d 15 -c 512 --timeout 8 -t 4 http://localhost:8080
Results:
- prefork
Running 15s test @ http://localhost:8080
4 threads and 512 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 4.75ms 4.27ms 126.24ms 97.45%
Req/Sec 26.46k 4.16k 71.18k 88.72%
Latency Distribution
50% 4.55ms
75% 4.82ms
90% 5.46ms
99% 15.49ms
1581916 requests in 15.09s, 140.30MB read
Socket errors: connect 0, read 318, write 0, timeout 0
Requests/sec: 104861.58
Transfer/sec: 9.30MB
- non-prefork
Running 15s test @ http://localhost:8080
4 threads and 512 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 6.42ms 11.83ms 177.19ms 96.42%
Req/Sec 24.96k 5.83k 56.83k 82.93%
Latency Distribution
50% 4.53ms
75% 4.93ms
90% 6.94ms
99% 74.54ms
1472441 requests in 15.09s, 130.59MB read
Socket errors: connect 0, read 265, write 0, timeout 0
Requests/sec: 97553.34
Transfer/sec: 8.65MB