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_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的连接会被新连接给挤掉
关于这个问题,切入点在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。
voidngx_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掉连接始终是不对的,
这里就得做权衡了,毕竟鱼和熊掌不能兼得。
=================================================================