序
本文主要研究一下nginx的keepalive_time参数
keepalive_time
Syntax: keepalive_time time;
Default:
keepalive_time 1h;
Context: http, server, location
This directive appeared in version 1.19.10.
nginx的1.19.10版本新增了keepalive_time参数,用于限制一个keep-alive连接处理请求的最长时间。当达到这个时间后,连接会在后续请求处理完成后关闭。
ngx_http_core_module
nginx/src/http/ngx_http_core_module.c
void
ngx_http_update_location_config(ngx_http_request_t *r)
{
ngx_http_core_loc_conf_t *clcf;
//......
if (r->keepalive) {
if (clcf->keepalive_timeout == 0) {
r->keepalive = 0;
} else if (r->connection->requests >= clcf->keepalive_requests) {
r->keepalive = 0;
} else if (ngx_current_msec - r->connection->start_time
> clcf->keepalive_time)
{
r->keepalive = 0;
} else if (r->headers_in.msie6
&& r->method == NGX_HTTP_POST
&& (clcf->keepalive_disable
& NGX_HTTP_KEEPALIVE_DISABLE_MSIE6))
{
/*
* MSIE may wait for some time if an response for
* a POST request was sent over a keepalive connection
*/
r->keepalive = 0;
} else if (r->headers_in.safari
&& (clcf->keepalive_disable
& NGX_HTTP_KEEPALIVE_DISABLE_SAFARI))
{
/*
* Safari may send a POST request to a closed keepalive
* connection and may stall for some time, see
* https://bugs.webkit.org/show_bug.cgi?id=5760
*/
r->keepalive = 0;
}
}
//......
}
ngx_http_core_module的ngx_http_update_location_config方法在开启keepalive时会判断connection的存活时间,若大于keepalive_time则关闭keepalive(
ngx_current_msec - r->connection->start_time > clcf->keepalive_time
)
ngx_http_core_keepalive
nginx/src/http/ngx_http_core_module.c
static char *
ngx_http_core_keepalive(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_core_loc_conf_t *clcf = conf;
ngx_str_t *value;
if (clcf->keepalive_timeout != NGX_CONF_UNSET_MSEC) {
return "is duplicate";
}
value = cf->args->elts;
clcf->keepalive_timeout = ngx_parse_time(&value[1], 0);
if (clcf->keepalive_timeout == (ngx_msec_t) NGX_ERROR) {
return "invalid value";
}
if (cf->args->nelts == 2) {
return NGX_CONF_OK;
}
clcf->keepalive_header = ngx_parse_time(&value[2], 1);
if (clcf->keepalive_header == (time_t) NGX_ERROR) {
return "invalid value";
}
return NGX_CONF_OK;
}
ngx_http_core_module的ngx_http_core_keepalive方法会解析nginx配置文件的keepalive_timeout配置,第一个参数为keepalive_timeout参数,第二参数为header_timeout
ngx_http_header_filter_module
nginx/src/http/ngx_http_header_filter_module.c
static ngx_int_t
ngx_http_header_filter(ngx_http_request_t *r)
{
u_char *p;
size_t len;
ngx_str_t host, *status_line;
ngx_buf_t *b;
ngx_uint_t status, i, port;
ngx_chain_t out;
ngx_list_part_t *part;
ngx_table_elt_t *header;
ngx_connection_t *c;
ngx_http_core_loc_conf_t *clcf;
ngx_http_core_srv_conf_t *cscf;
u_char addr[NGX_SOCKADDR_STRLEN];
if (r->header_sent) {
return NGX_OK;
}
//......
if (r->headers_out.status == NGX_HTTP_SWITCHING_PROTOCOLS) {
b->last = ngx_cpymem(b->last, "Connection: upgrade" CRLF,
sizeof("Connection: upgrade" CRLF) - 1);
} else if (r->keepalive) {
b->last = ngx_cpymem(b->last, "Connection: keep-alive" CRLF,
sizeof("Connection: keep-alive" CRLF) - 1);
if (clcf->keepalive_header) {
b->last = ngx_sprintf(b->last, "Keep-Alive: timeout=%T" CRLF,
clcf->keepalive_header);
}
} else {
b->last = ngx_cpymem(b->last, "Connection: close" CRLF,
sizeof("Connection: close" CRLF) - 1);
}
//......
}
ngx_http_header_filter_module的ngx_http_header_filter方法在开启keepalive的时候会写入
Connection: keep-alive
,若keepalive_header的值大于0则写入Keep-Alive: timeout=%T
,可以看到这个值是固定的
ngx_http_set_keepalive
nginx/src/http/ngx_http_request.c
static void
ngx_http_set_keepalive(ngx_http_request_t *r)
{
int tcp_nodelay;
ngx_buf_t *b, *f;
ngx_chain_t *cl, *ln;
ngx_event_t *rev, *wev;
ngx_connection_t *c;
ngx_http_connection_t *hc;
ngx_http_core_loc_conf_t *clcf;
//......
wev = c->write;
wev->handler = ngx_http_empty_handler;
if (b->pos < b->last) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "pipelined request");
c->log->action = "reading client pipelined request line";
r = ngx_http_create_request(c);
if (r == NULL) {
ngx_http_close_connection(c);
return;
}
r->pipeline = 1;
c->data = r;
c->sent = 0;
c->destroyed = 0;
c->pipeline = 1;
if (rev->timer_set) {
ngx_del_timer(rev);
}
rev->handler = ngx_http_process_request_line;
ngx_post_event(rev, &ngx_posted_events);
return;
}
//......
rev->handler = ngx_http_keepalive_handler;
//......
c->idle = 1;
ngx_reusable_connection(c, 1);
ngx_add_timer(rev, clcf->keepalive_timeout);
if (rev->ready) {
ngx_post_event(rev, &ngx_posted_events);
}
}
ngx_http_request的ngx_http_set_keepalive方法,在
b->pos < b->last
会尝试读取request line然后执行ngx_http_create_request,若能读到数据则判断是否有timer,有则执行ngx_del_timer(rev)删除timer,然后返回;若进入keepalive逻辑,则会通过ngx_add_timer添加一个定时事件,在keepalive_timeout之后触发
ngx_http_keepalive_handler
nginx/src/http/ngx_http_request.c
static void
ngx_http_keepalive_handler(ngx_event_t *rev)
{
size_t size;
ssize_t n;
ngx_buf_t *b;
ngx_connection_t *c;
c = rev->data;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http keepalive handler");
if (rev->timedout || c->close) {
ngx_http_close_connection(c);
return;
}
#if (NGX_HAVE_KQUEUE)
if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
if (rev->pending_eof) {
c->log->handler = NULL;
ngx_log_error(NGX_LOG_INFO, c->log, rev->kq_errno,
"kevent() reported that client %V closed "
"keepalive connection", &c->addr_text);
#if (NGX_HTTP_SSL)
if (c->ssl) {
c->ssl->no_send_shutdown = 1;
}
#endif
ngx_http_close_connection(c);
return;
}
}
#endif
b = c->buffer;
size = b->end - b->start;
if (b->pos == NULL) {
/*
* The c->buffer's memory was freed by ngx_http_set_keepalive().
* However, the c->buffer->start and c->buffer->end were not changed
* to keep the buffer size.
*/
b->pos = ngx_palloc(c->pool, size);
if (b->pos == NULL) {
ngx_http_close_connection(c);
return;
}
b->start = b->pos;
b->last = b->pos;
b->end = b->pos + size;
}
/*
* MSIE closes a keepalive connection with RST flag
* so we ignore ECONNRESET here.
*/
c->log_error = NGX_ERROR_IGNORE_ECONNRESET;
ngx_set_socket_errno(0);
n = c->recv(c, b->last, size);
c->log_error = NGX_ERROR_INFO;
if (n == NGX_AGAIN) {
if (ngx_handle_read_event(rev, 0) != NGX_OK) {
ngx_http_close_connection(c);
return;
}
/*
* Like ngx_http_set_keepalive() we are trying to not hold
* c->buffer's memory for a keepalive connection.
*/
if (ngx_pfree(c->pool, b->start) == NGX_OK) {
/*
* the special note that c->buffer's memory was freed
*/
b->pos = NULL;
}
return;
}
if (n == NGX_ERROR) {
ngx_http_close_connection(c);
return;
}
c->log->handler = NULL;
if (n == 0) {
ngx_log_error(NGX_LOG_INFO, c->log, ngx_socket_errno,
"client %V closed keepalive connection", &c->addr_text);
ngx_http_close_connection(c);
return;
}
b->last += n;
c->log->handler = ngx_http_log_error;
c->log->action = "reading client request line";
c->idle = 0;
ngx_reusable_connection(c, 0);
c->data = ngx_http_create_request(c);
if (c->data == NULL) {
ngx_http_close_connection(c);
return;
}
c->sent = 0;
c->destroyed = 0;
ngx_del_timer(rev);
rev->handler = ngx_http_process_request_line;
ngx_http_process_request_line(rev);
}
ngx_http_request的ngx_http_keepalive_handler会在
rev->timedout || c->close
的时候执行ngx_http_close_connection然后返回,若还能读到请求数据则执行ngx_del_timer(rev)删除定时任务
小结
nginx的1.19.10版本新增了keepalive_time参数(默认1h
),用于限制一个keep-alive连接处理请求的最长时间(即指定connection的最大存活时间
),当达到这个时间后,连接会在后续请求处理完成后关闭。而keepalive_timeout参数(默认75s
)则是用于指定connection最大的空闲时间,nginx内部有会给该连接设定一个timer,在keepalive_timeout之后触发,若连接还是空闲则关闭连接。