Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >nginx的timeout(基于nginx1.17.9)

nginx的timeout(基于nginx1.17.9)

作者头像
theanarkh
发布于 2020-06-19 03:01:35
发布于 2020-06-19 03:01:35
82500
代码可运行
举报
文章被收录于专栏:原创分享原创分享
运行总次数:0
代码可运行

nginx中使用timeout的地方非常多,本文主要分析客户端和nginx通信时涉及到的几个timeout。

  1. 连接建立成功,接收业务数据超时
  2. 接收http报文的超时

1 连接建立成功,接收业务数据超时 这个逻辑从ngx_event_accept函数开始分析,ngx_event_accept是nginx在监听某个端口时,底层建立tcp连接成功后回调的函数。我们首先需要了解,在nginx中。一个连接是使用ngx_connection_t表示。每个ngx_connection_t对应两个ngx_event_t结构体,一个读,一个写。他们之前有内在的字段关联起来。另外一个ngx_connection_t也会关联到一个ngx_listening_t结构体,ngx_listening_t是用于表示一个被监听的ip端口,比如我们设置的127.0.0.1 80。ngx_connection_t指向的ngx_listening_t,表示该连接来自哪个监听的ip端口。下面看看该函数的主要逻辑。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void ngx_event_accept(ngx_event_t *ev)
{
    socklen_t          socklen;
    ngx_err_t          err;
    ngx_log_t         *log;
    ngx_uint_t         level;
    ngx_socket_t       s;
    ngx_event_t       *rev, *wev;
    ngx_sockaddr_t     sa;
    ngx_listening_t   *ls;
    ngx_connection_t  *c, *lc;
    ngx_event_conf_t  *ecf;

    ecf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_event_core_module);

    // 对应的ngx_connection_t
    lc = ev->data;
    // 对应的ngx_listening_t,即来自哪个监听的socket
    ls = lc->listening;
    ev->ready = 0;

    do {
        socklen = sizeof(ngx_sockaddr_t);
        s = accept(lc->fd, &sa.sockaddr, &socklen);
        // 为通信socket,获取一个connection结构体数组
        c = ngx_get_connection(s, ev->log);
        // TCP
        c->type = SOCK_STREAM;
        // 一个连接分配一个内存池
        c->pool = ngx_create_pool(ls->pool_size, ev->log);
        // 为通信socket设置读写数据的函数,由事件驱动模块提供
        c->recv = ngx_recv;
        c->send = ngx_send;
        c->recv_chain = ngx_recv_chain;
        c->send_chain = ngx_send_chain;
        // 标识该连接来自哪个监听的socket,即ngx_listening_t
        c->listening = ls;
        // 读写事件的结构体
        rev = c->read;
        wev = c->write;
        /*
            执行连接到来时的回调,对于http模块是ngx_http_init_connection,
            会注册等待读事件,等待数据到来
        */
        ls->handler(c);
    } while (ev->available); // 是否连续accept(尽可能),可能会没有等待accept的节点了
}

ngx_event_accept会分配一个ngx_connection_t去表示一个连接,然后执行ngx_http_init_connection回调。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 有连接到来时的处理函数
void ngx_http_init_connection(ngx_connection_t *c)
{
    ngx_uint_t              i;
    ngx_event_t            *rev;
    struct sockaddr_in     *sin;
    ngx_http_port_t        *port;
    ngx_http_in_addr_t     *addr;
    ngx_http_log_ctx_t     *ctx;
    ngx_http_connection_t  *hc;

    // 读事件结构体
    rev = c->read;
    // 设置新的回调函数,在http报文到达时执行
    rev->handler = ngx_http_wait_request_handler;
    c->write->handler = ngx_http_empty_handler;
    /*
        建立连接后,post_accept_timeout这么长时间还没有数据到来则超时,
        post_accept_timeout的值等于nginx.conf中client_header_timeout字段的值
    */
    ngx_add_timer(rev, c->listening->post_accept_timeout);
    // 注册读事件,等到http报文到来
    if (ngx_handle_read_event(rev, 0) != NGX_OK) {
        ngx_http_close_connection(c);
        return;
    }
}

ngx_http_init_connection函数做了两个事情 1 设置定时器,如果超时后还没有收到http报文,则关闭连接。 2 设置下一步回调函数为ngx_http_wait_request_handler。 我们继续来到ngx_http_wait_request_handler函数。该函数在http报文到来时被执行。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 连接上有请求到来时的处理函数
static void ngx_http_wait_request_handler(ngx_event_t *rev)
{
    u_char                    *p;
    size_t                     size;
    ssize_t                    n;
    ngx_buf_t                 *b;
    ngx_connection_t          *c;
    ngx_http_connection_t     *hc;
    ngx_http_core_srv_conf_t  *cscf;

    c = rev->data;
    // 已经超时,http报文还没有到达,关闭连接
    if (rev->timedout) {
        ngx_http_close_connection(c);
        return;
    }

    hc = c->data;
    cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module);
    // 分配内存接收数据,client_header_buffer_size来自nginx.conf
    size = cscf->client_header_buffer_size;

    b = c->buffer;
    // 创建一个buf用于接收http报文
    if (b == NULL) {
        b = ngx_create_temp_buf(c->pool, size);
        c->buffer = b;
    } 
    // 接收数据,即读取http报文
    n = c->recv(c, b->last, size);
    // 更新可写指针的位置,n是读取的http报文字节数
    b->last += n;
    // 创建一个ngx_http_request_t结构体表示本次http请求
    c->data = ngx_http_create_request(c);
    // 开始处理http报文
    rev->handler = ngx_http_process_request_line;
    ngx_http_process_request_line(rev);
}

该函数主要是 1 在超时时仍然没有收到http报文,则处理超时。 2 分配结构体ngx_http_request_t用于表示一个http请求 3 分配保存http报文的buffer,然后开始解析http报文 刚才分析了建立连接和收到http报文间有一个超时时间。nginx还定义了另外一个超时。就是尽管有部分http报文已经到达,但是如果整个http报文到达过慢,也可能会导致超时问题。现在我们继续来看一下ngx_http_process_request_line函数。该函数用于解析http报文。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 处理http数据包的请求报文
static void ngx_http_process_request_line(ngx_event_t *rev)
{
    ssize_t              n;
    ngx_int_t            rc, rv;
    ngx_str_t            host;
    ngx_connection_t    *c;
    ngx_http_request_t  *r;

    c = rev->data;
    r = c->data;

    // 超时了,关闭请求
    if (rev->timedout) {
        c->timedout = 1;
        ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT);
        return;
    }

    rc = NGX_AGAIN;

    for ( ;; ) {
        // 读取http报文到r的buffer里
        n = ngx_http_read_request_header(r);
        // 解析http请求行,r->header_in的内容由ngx_http_read_request_header设置
        rc = ngx_http_parse_request_line(r, r->header_in);
        // 请求行解析完
        if (rc == NGX_OK) {
           ngx_list_init(&r->headers_in.headers, r->pool, 20, sizeof(ngx_table_elt_t))
            // 开始处理http头
            rev->handler = ngx_http_process_request_headers;
            ngx_http_process_request_headers(rev);
            break;
        }
    }
}

ngx_http_process_request_line函数主要是处理http报文到达超时的问题和读取http请求行,然后解析。那我们看一下这个timeout字段是怎么设置的。我们看读取http报文的函数ngx_http_read_request_header。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static ssize_t ngx_http_read_request_header(ngx_http_request_t *r)
{
    ssize_t                    n;
    ngx_event_t               *rev;
    ngx_connection_t          *c;
    ngx_http_core_srv_conf_t  *cscf;

    c = r->connection;
    rev = c->read;
    // 读取http报文
    n = c->recv(c, r->header_in->last, r->header_in->end - r->header_in->last);
    // 没有数据可读
    if (n == NGX_AGAIN) {
        // 还没有设置定时器(还没有插入定时器红黑树)
        if (!rev->timer_set) {
            cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
            // 根据nginx配置设置读取头部的超时时间,client_header_timeout来自nginx.conf
            ngx_add_timer(rev, cscf->client_header_timeout);
        }
        // 注册读事件
        if (ngx_handle_read_event(rev, 0) != NGX_OK) {
            ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
            return NGX_ERROR;
        }

        return NGX_AGAIN;
    }

    // 读取成功,更新last指针
    r->header_in->last += n;

    return n;
}

ngx_http_read_request_header读取http报文的时候,如果没有数据可读则设置超时时间。超时时会执行ngx_http_process_request_line关闭请求。 总结:本文介绍了两个timeout,在nginx中很多地方都使用了定时器,后面有空再分析。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-06-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编程杂技 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Nginx(二): worker 进程处理流程框架解析
Nginx 启动起来之后,会有几个进程运行:1. master 进程接收用户命令并做出响应; 2. worker 进程负责处理各网络事件,并同时接收来自master的处理协调命令;
烂猪皮
2021/01/28
1.4K0
Nginx(二): worker 进程处理流程框架解析
nginx源码阅读(6)http处理流程
Nginx作为一款开源的、高性能的HTTP服务器和反向代理服务器而闻名,本文基于nginx-1.15.0,将为读者简要介绍其HTTP处理流程。
golangLeetcode
2022/08/02
1.1K0
nginx源码阅读(6)http处理流程
Nginx(三):http模块的处理流程解析之正向代理
无疑,在nginx的核心服务中,http服务占据了相当大的份量。那么,要想多了解nginx多一点,则必须要了解其http模块的工作机制。
烂猪皮
2021/01/28
2K0
Nginx(三):http模块的处理流程解析之正向代理
nginx0.1.0之event模块初始化源码分析(4)
event的配置解析相关的代码已经分析完毕。下面分析一下另一个流程中event模块的实现。即在nginx创建进程,并且开始执行进程里的代码的时候。入口函数是ngx_worker_process_cycle。
theanarkh
2019/03/06
3900
nginx事件模块源码分析
事件处理框架所要解决的问题是如何收集,管理,分发事件。这里所说的事件,主要以网络事件和定时器事件为主,而网络事件中又以TCP网络事件为主。由于网络事件与网卡中断处理程序,内核提供的系统调用密切相关,所以网络事件的驱动取决于不同的操作系统平台,在同一操作系统中也受制于不同的操作系统内核版本。因此不同操作系统有不同的事件驱动机制。
stan1ey
2021/08/11
6900
nginx事件模块源码分析
聊聊nginx的keepalive_time参数
nginx/src/http/ngx_http_header_filter_module.c
code4it
2023/12/05
3050
万字多图,搞懂 Nginx 高性能网络工作原理!
在单进程的网络编程模型中。所有的网络相关的动作都是在一个进程里完成的,如监听 socket 的创建, bind、listen。再比如 epoll 的创建、要监听事件的添加,以及 epoll_wait 等待时间发生。这些统统都是在一个进程里搞定。
开发内功修炼
2022/05/09
5570
万字多图,搞懂 Nginx 高性能网络工作原理!
nginx dns解析源码分析
在使用同步IO的情况下,调用gethostbyname()或者gethostbyname_r()就可以根据域名查询到对应的IP地址,。
stan1ey
2021/06/07
1.8K0
nginx dns解析源码分析
Nginx 的异步非阻塞体现在哪里?从理论分析到源码验证
2、那业务层面的异步是怎么个异步法?同步异步的概念我就不说了,前面文章有。异步最重要的标志就是通知,通知,通知!!!
看、未来
2021/10/13
9270
nginx源码阅读(4)单进程epoll流程解析
我们这里以单进程启动为例 nginx.c中的main 函数调用ngx_single_process_cycle
golangLeetcode
2022/08/02
4330
nginx的延迟关闭
最近业务方反馈线上 Nginx 经常会打出一些『奇怪』的 access 日志,奇怪之处在于这些日志的 request_time 值总是正好 upstream_response_time 的值大5秒,于是我就帮他们查看了一下导致这个问题的原因,本文记录一下最终调查的结论以及过程。
后端技术探索
2019/04/25
3.7K0
深入理解nginx的https alpn机制
  应用层协议协商(Application-Layer Protocol Negotiation,简称ALPN)是一个传输层安全协议(TLS) 的扩展, ALPN 使得应用层可以协商在安全连接层之上使用什么协议, 避免了额外的往返通讯, 并且独立于应用层协议。ALPN 用于 HTTP/2 连接, 和HTTP/1.x 相比, HTTP 2的使用增强了网页的压缩率减少了网络延时。ALPN 和 HTTP/2 协议是伴随着 Google 开发 SPDY 协议出现的。
码农心语
2024/04/09
6810
深入理解nginx的https alpn机制
nginx流程源码分析
nginx作为一个web服务器肯定是要监听套接字的,监听套接字用于接收HTTP请求,其是根据配置文件的内容来创建的。
stan1ey
2021/08/11
5570
nginx流程源码分析
nginx upstream模块完整逻辑源码分析
1.启动upstream。 2.连接上游服务器。 3.向上游发送请求。 4.接收上游响应(包头/包体)。 5.结束请求。
stan1ey
2021/01/23
3K0
深入理解nginx stream proxy 模块的ssl连接原理
  我一直来对ssl建立连接的过程一知半解,以前分析nginx代码的时候一旦碰到ssl连接部分的代码都是直接跳过,前面在分析ngx_http_upstream_dynamic_module的时候正好想到了是不是可以给它添加一个能够支持https健康检查的功能,所以今天决定沉下心来仔细分析一下nginx本身的与上游服务器建立连接的实现逻辑。
码农心语
2024/04/09
1K0
深入理解nginx stream proxy 模块的ssl连接原理
nginx源代码分析–event事件驱动初始化
2.进入函数ngx_init_cycle,调用每一个核心模块的create_conf
全栈程序员站长
2022/07/13
3530
Nginx(五):http反向代理的实现
上一篇nginx的文章中,我们理解了整个http正向代理的运行流程原理,主要就是事件机制接入,header解析,body解析,然后遍历各种checker,以及详细讲解了其正向代理的具体实现过程。这已经让我们对整个nginx有了较深入的了解,但nginx核心固然重要,但其扩展功能才是其吸引大家的地方。而它的扩展功能又是无穷无尽的,这是好事又是坏事,好事是功能特别多,坏事是我们不可能都能探究其每个模块。
烂猪皮
2021/01/28
9970
Nginx(五):http反向代理的实现
nginx源码阅读(1)
Nginx框架是围绕着ngx_cycle_t结构体运行的。ngx_cycle_t结构体中包含的信息主要可以分为以下部分:
golangLeetcode
2022/08/02
7480
通过源码理解http层和tcp层的keep-alive
很久没更新文章了,今天突然想到这个问题,打算深入理解一下。我们知道建立tcp连接的代价是比较昂贵的,三次握手,慢开始,或者建立一个连接只为了传少量数据。这时候如果能保存连接,那会大大提高效率。下面我们通过源码来看看keep-alive的原理。本文分成两个部分
theanarkh
2020/06/19
8530
深入理解nginx的https sni机制
  SNI(Server Name Indication)是一种TLS(Transport Layer Security)协议的扩展,用于在建立加密连接时指定服务器的主机名。在使用单个IP地址和端口提供多个域名的服务时,SNI是非常有用的。  当客户端发起TLS握手时,它会发送一个包含所请求主机名的扩展,这样服务器就可以根据这个主机名选择合适的证书来完成握手。这使得服务器能够在同一IP地址和端口上为多个域名提供加密连接,而不需要为每个域名分配一个独立的IP地址。  对于HTTPS网站来说,SNI是至关重要的,因为它允许服务器在同一IP地址上为多个域名提供加密连接,不需要为每个域名单独部署一台服务器,从而降低了运维成本并提高了灵活性。  在使用SNI时,服务器端必须能够根据客户端发送的SNI信息来选择正确的证书进行握手。通常,服务器端配置会包含多个虚拟主机的证书信息,以便根据收到的SNI信息选择正确的证书来完成握手。  总的来说,SNI允许客户端在TLS握手期间指定所请求的主机名,从而使服务器能够根据主机名选择正确的证书,实现一个IP地址上多个域名的加密连接。
码农心语
2024/04/09
3.1K1
深入理解nginx的https sni机制
相关推荐
Nginx(二): worker 进程处理流程框架解析
更多 >
领券
社区富文本编辑器全新改版!诚邀体验~
全新交互,全新视觉,新增快捷键、悬浮工具栏、高亮块等功能并同时优化现有功能,全面提升创作效率和体验
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验