[nginx] upstream结束和keepalive实现

来源:互联网 发布:ni usb数据采集卡介绍 编辑:程序博客网 时间:2024/06/08 13:17
  • upstream请求结束:数据交互出错、后端关闭socket时nginx接收到FIN、content-length数据已接收到
  • upstream结束的主函数ngx_http_upstream_finalize_request()
  • keepalive机制:nginx长连接

一、upstream finalize

upstream请求结束分三种情况:

  1. read()、write()过程中出现NGX_ERROR/NGX_ABORT,表示请求过程中发生异常,设置p->upstream_error = 1
  2. read()函数时后端关闭socket,后端系统发送FIN分节到nginx,接收到该信号则设置rev->ready = 0p->upstream_eof = 1
  3. 正常情况下,upstream接收Header时会记录content-length,然后接收body时减小p->length的值,当所有body接收完时p->length=0,并设置p->upstream_done = 1

分析ngx_http_upstream_finalize_request()

  • 清除cleanup指针,因为在upstream的前面阶段:create、init、发送请求、接收Headers时出现异常则会调用ngx_http_finalize_request(),并执行cleanup()的调用。所以这里首先要清除cleanup指针;
  • u->finalize_request(),实际调用ngx_http_proxy_finalize_request,没有任何操作;
  • u->peer.free(),实际调用’ngx_http_upstream_keepalive_close_handler()’进行peer的“关闭”操作;
  • 如果peer->connection不为空,则关闭connection,原因是上一步有可能是keepalive里把connection放到cache里后置为空,所以不用关闭connection;
  • ngx_http_send_special(),设置一个带有last标志的特殊buffer,告知数据发送环节需要将之前缓存而没有发送的buffer chain,发送出去;
  • ngx_http_finalize_request(),结束请求
    • 如果还没数据没发送完c->buffered不为0,则设置write句柄为ngx_http_write,表示要继续发送数据,在函数ngx_http_writengx_htt_finalize_request()是最后执行,形成递归;
    • 若数据发送完了,则直接干到这一行r->done = 1,标记结束,并把client的write句柄关闭
    • ngx_http_post_action(),若无后续操作,则无实际执行
    • 如果socket关闭了,会发送FIN分节,标记c->read->eof,执行ngx_http_close_request()关闭socket,????
    • ngx_http_finalize_connection(),关闭链接
      • 检查请求的引用计数器,不等于1说明还有多个动作在操作着请求???(请求创建时ngx_http_create_request()该值赋为1);
      • 引用计数器为1时,检查keepalive是否超时,接着结束keepalive请求返回;
      • 调用ngx_http_set_lingering_close(),默认lingering超时是5s
        • 设置ngx_http_lingering_close_handler()读事件,意思客户端在lingering_timeout时间内没有进行任何操作,那么就会关闭与客户端的连接,如果有操作,也有总时间lingering_time超时,然后调用ngx_http_close_request()
        • ngx_shutdown_socket(fd, NGX_WRITE_SHUTDOWN)关闭socket写端;
        • 调用ngx_http_lingering_close_handler(),进一步会ngx_http_free_request()打印请求日志、释放request,ngx_http_close_connection()中释放connection,并ngx_close_socket(fd)回收描述符。lingering机制详见nginx lingering_close,进一步也可以了解与之相关的case:nginx的延迟关闭。

二、upstream keepalive

启用nginx到upstream的keepalive,需要设置proxy http\1.1和覆盖connection为空(nginx默认会置为close)。

2.1 数据发送

  • ngx_http_proxy_create_request(),nginx首先准备好发向upstream的数据;
  • 调用uscf->peer.init(),实际调用ngx_http_upstream_init_keepalive_peer() ==> ngx_http_upstream_init_round_robin_peer(),通过负载均衡引擎获取一个后端服务节点;
  • 是否连接该服务节点取决于keepalive queue中是否能从cache队列线性查找到该节点,若查找到则从队列中移出该节点,并将节点放到空闲队列free;
  • ngx_http_upstream_send_request(),向该节点发送数据。

2.2 数据接收

  • 先接收upstream发来的Header,一边接收一边解析,直到完整接收到Header
  • nginx立即将Headers发送给client,再接收Body,一边从upstream接收一边发往client,但这里底层实际上是先buffer住,等所有数据接收完才发往client发送数据;
  • 其中p->length记录了待接收数据的长度(解析header时用content-length赋值给u->length然后调用ngx_http_proxy_input_filter_init()赋值给p->length),每次收到数据会减小,直到p->length为0时,会判断connection_close(在http\1.0下默认为1否则为0),当upstream返回connection:close时设置为1,以决定是否keepalive;
  • 若遇到readv()返回0表示upstream发送了FIN信号而关闭的远端socket,则nginx也后续相应关闭链接;
  • 最后若对方socket未关闭、socket未出错、keepalive,则会把后端节点的连接对象存放到cache队列头部,如果队列满了,则释放列队尾部的一个元素,LRU淘汰策略。
0 0
原创粉丝点击