ReactPHP Keep-Alive连接引发内存占用过高问题及禁用咨询
ReactPHP + FastRoute API 长连接内存泄漏问题
我用ReactPHP和FastRoute开发了一个REST API,发现当Web界面请求带Connection: keep-alive头时,内存会持续增长直到崩溃;换成Connection: close就没有内存泄漏。想问两个问题:
- 这个问题的成因是什么?
- 能不能在ReactPHP服务器里禁用Keep-Alive?
我的服务器代码如下:
$server = new Server($loop, new ErrorHandler(), new JsonRequestDecoder(), new CorsMiddleware([ 'allow_origin' => [ '*', 'http://localhost:3000' ], 'allow_methods' => ['GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'OPTIONS'], 'allow_headers' => ['DNT','X-Custom-Header','Keep-Alive','User-Agent','X-Requested-With','If-Modified-Since','Cache-Control','Content-Type','Content-Range','Range'], 'expose_headers' => ['DNT','X-Custom-Header','Keep-Alive','User-Agent','X-Requested-With','If-Modified-Since','Cache-Control','Content-Type','Content-Range','Range'] ]), new Router($routes));
问题成因
- 长连接资源未释放:Keep-Alive会维持TCP连接供后续请求复用,如果你的代码在处理请求后没有正确释放对应资源(比如数据库连接、文件句柄、临时变量、事件监听器等),这些资源会随着长连接的持续存在不断累积,导致内存占用攀升。
- 请求上下文残留引用:ReactPHP的异步模型中,长连接会关联请求上下文。如果业务逻辑里的闭包、回调持有上下文对象的引用,或者没有清理请求级别的缓存/数据,这些引用会阻止PHP垃圾回收机制回收内存,最终造成泄漏。
- 中间件/路由逻辑内存泄漏:检查使用的中间件(如
JsonRequestDecoder、CorsMiddleware)或自定义路由处理函数,是否在每次请求后留下未清理的资源。比如某些中间件可能会在请求对象上附加数据,但长连接复用请求对象时未重置这些数据,导致内存堆积。
如何禁用Keep-Alive
ReactPHP的Server没有直接的配置开关,但可以通过两种方式强制关闭长连接:
方法1:在所有响应中添加Connection: close头
在路由处理函数或全局逻辑中,给每个响应主动设置该头,告知客户端不要复用连接:
// 示例路由处理逻辑 $routes->addRoute('GET', '/api/data', function ($request) { $data = ['status' => 'ok', 'data' => []]; return new Response(200, [ 'Connection' => 'close', 'Content-Type' => 'application/json' ], json_encode($data)); });
方法2:自定义中间件强制关闭长连接
编写全局中间件,统一处理请求和响应,确保强制关闭连接:
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Message\ResponseInterface; class DisableKeepAliveMiddleware implements MiddlewareInterface { public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { // 移除请求中的Keep-Alive相关头 $request = $request->withoutHeader('Connection'); // 处理请求 $response = $handler->handle($request); // 添加响应头强制关闭连接 return $response->withHeader('Connection', 'close'); } } // 将该中间件加入Server的中间件链(建议放在最前面) $server = new Server($loop, new ErrorHandler(), new DisableKeepAliveMiddleware(), new JsonRequestDecoder(), new CorsMiddleware([ // 你的Cors配置 ]), new Router($routes));
注意:如果使用CorsMiddleware,需确保expose_headers包含Connection,避免跨域读取头的限制问题。
内容的提问来源于stack exchange,提问作者acastro
相关产品推荐
相关产品推荐

