nginx keepalive连接回收机制

本文探讨了nginx中的keepalive连接回收机制,重点讲解了ngx_http_set_keepalive和ngx_http_keepalive_handler函数如何调用ngx_reusable_connection来管理可重用连接队列,以优化性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

nginx keepalive连接回收机制
=================================================================
并发量大的时候,一些keepalive的连接会被新连接给挤掉
关于这个问题,切入点在ngx_get_connection,并发足够大时,使连接池迅速耗尽

从代码角度上看:
c = ngx_cycle->free_connections;
// 此时连接池已用尽
if (c == NULL) {
ngx_drain_connections(); // 将一些keepalive连接给释放掉
c = ngx_cycle->free_connections;
}


static void
ngx_drain_connections(void)
{
    ngx_int_t          i;
    ngx_queue_t       *q;
    ngx_connection_t  *c;
    
    // 清理32个keepalive连接,以回收一些连接池供新连接使用
    for (i = 0; i < 32; i++) {
        if (ngx_queue_empty(&ngx_cycle->reusable_connections_queue)) {
            break;
        }
        
        // reusable连接队列是从头插入的,意味着越靠近队列尾部的连接,空闲未被
        // 使用的时间就越长,这种情况下,优先回收它,类似LRU
        q = ngx_queue_last(&ngx_cycle->reusable_connections_queue);
        c = ngx_queue_data(q, ngx_connection_t, queue);


        ngx_log_debug0(NGX_LOG_DEBUG_CORE, c->log, 0,
                       "reusing connection");
        
        /*这里的handler是ngx_http_keepalive_handler,由于close被置1,所以会执行ngx_http_close_connection来释放连接,这样也就发生了keepalive连接被强制断掉的现象了。*/
        c->close = 1;
        c->read->handler(c->read);
    }
}


那么上面提到的reusable连接队列是如何建立的呢?这个比较简单,这里大体说一下:

在ngx_http_set_keepalive函数处理的最后阶段,会以第二个为1的形式调用ngx_reusable_connection函数。

因此当需要将c从ngx_cycle->reusable_connections_queue中去除时候,以第二个为0的形式调用ngx_reusable_connection函数即可。而在ngx_http_keepalive_handler函数中会以参数0,来调用ngx_reusable_connection。

void
ngx_reusable_connection(ngx_connection_t *c, ngx_uint_t reusable)
{
    // 一旦一个keepalive的连接正常处理了,就将其从reusable队列中移除
    if (c->reusable) {
        ngx_queue_remove(&c->queue);
    }


    // 在ngx_http_set_keepalive中会将reusable置为1,reusable为1的直接效果
    // 就是将该连接插到reusable_connections_queue中
    c->reusable = reusable;
    
    // 当reusable为0时,意味着该keepalive被正常的处理掉了,不应该被再次添加
    // 到reusable队列中了。
    if (reusable) {
        /* need cast as ngx_cycle is volatile */
        // 这里使用头插法,较新的连接靠近头部,时间越久未被处理的连接越靠尾
        ngx_queue_insert_head(
            (ngx_queue_t *) &ngx_cycle->reusable_connections_queue, &c->queue);
    }
}
从高性能服务器的角度来说,nginx这样做也有它的合理之处,如果一个keepalive连接长时间不交互数据,
而新连接到来又拿不到连接,处理这些占着坑不XX的家伙也是无奈之举,当然擅自drain掉连接始终是不对的,
这里就得做权衡了,毕竟鱼和熊掌不能兼得。

=================================================================



### keepalive connection closed 的原因及解决方案 在排查 `keepalive connection closed` 问题时,通常涉及多个层面的配置和实现,包括网络层、应用层(如 Nginx、Spring Framework)以及数据库连接池等。以下是常见的原因分析和解决方案。 #### 1. Nginx 配置导致连接关闭 在某些场景下,`keepalive_timeout` 被设置为 `0`,这会导致 Nginx 在 HTTP 响应头中加入 `Connection: close` 字段,从而强制关闭 TCP 连接。这种情况下,客户端(如 k6)可能不会继续发起 HTTP 请求,即使 TCP 握手已经完成。从抓包结果来看,Nginx 会响应 SYN 包的 ACK,但客户端未发送 HTTP 请求,最终表现为连接超时(i/otimeout)[^1]。 **解决方案:** 修改 Nginx 配置文件中的 `keepalive_timeout` 值,确保其不为 `0`,例如: ```nginx http { keepalive_timeout 65; ... } ``` 同时,可以启用 `keepalive_requests` 来限制单个连接上允许的最大请求数: ```nginx keepalive_requests 100; ``` #### 2. Spring WebClient 主动关闭连接 在 Spring Framework 的某些版本中,当发生取消操作(cancellation)时,`WebClient` 会主动关闭连接释放资源。这种行为可能导致连接被关闭,表现为 `Connection is closed` 错误[^2]。 **解决方案:** 确保在调用 `WebClient` 时正确处理取消逻辑,避免不必要的连接释放。可以使用 `doOnCancel` 或 `doOnError` 来进行资源清理,而不是依赖框架自动关闭连接。 ```java webClient.get() .uri("/api") .retrieve() .bodyToMono(String.class) .doOnCancel(() -> { // 自定义取消处理逻辑 }) .subscribe(); ``` #### 3. 数据库连接空闲超时 当数据库连接空闲时间超过其配置的超时时间(如 MySQL 的 `wait_timeout`)时,连接会被数据库主动断开。Spring Boot 应用如果没有及时检测到连接状态,可能会在使用该连接时抛出 `Connection is closed` 异常。 一种解决方案是设置合理的 `keepalive-time`,例如 5 分钟(300000 ms),并通过心跳检测保持连接活跃[^3]。 **解决方案:** 在 Spring Boot 配置文件中设置连接池的 `keepalive-time` 和 `validation-timeout`: ```yaml spring: datasource: hikari: keepalive-time: 300000 validation-timeout: 5000 ``` 同时,确保数据库的 `wait_timeout` 设置与应用层保持一致,避免连接被数据库主动关闭。 #### 4. 长事务导致连接被回收 在 Spring 中,如果事务执行时间过长(例如处理大文件解析),可能会导致连接连接池强制回收(如 Druid 的 `removeAbandoned` 机制)。此时事务上下文仍然存在,但底层连接已被释放,后续数据库操作将失败,抛出 `Connection is closed` 异常[^4]。 **解决方案:** 避免长事务,将耗时操作移出事务边界。若必须执行长事务,可适当调整连接池的 `removeAbandonedTimeout` 和 `logAbandoned` 参数: ```yaml spring: datasource: druid: remove-abandoned: true remove-abandoned-timeout: 120 log-abandoned: true ``` 此外,可以在事务开始前显式获取连接,并在操作完成后释放资源,避免连接被误回收。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值