nginx处理http

ngx_http_read_client_request_body 

  HTTP包体的长度有可能非常大,如果试图一次性调用并读取完所有的包体,那么多半会阻塞Nginx进程。HTTP框架提供了一种方法来异步地接收包体:

    ngx_int_t ngx_http_read_client_request_body(ngx_http_request_t *r, ngx_http_client_body_handler_pt post_handler);
    ngx_http_read_client_request_body是一个异步方法,调用它只是说明要求Nginx开始接收请求的包体,并不表示是否已经接收完,当接收完所有的包体内容后,post_handler指向的回调方法会被调用。因此,即使在调用了ngx_http_read_client_request_body方法后它已经返回,也无法确定这时是否已经调用过post_handler指向的方法。换句话说,ngx_http_read_client_request_body返回时既有可能已经接收完请求中所有的包体(假如包体的长度很小),也有可能还没开始接收包体。如果ngx_http_read_client_request_body是在ngx_http_mytest_handler处理方法中调用的,那么后者一般要返回NGX_DONE,因为下一步就是将它的返回值作为参数传给ngx_http_finalize_request。NGX_DONE的意义如下:
NGX_DONE:表示到此为止,同时HTTP框架将暂时不再继续执行这个请求的后续部分。事实上,这时会检查连接的类型,如果是keepalive类型的用户请求,就会保持住HTTP连接,然后把控制权交给Nginx。这个返回码很有用,考虑以下场景:在一个请求中我们必须访问一个耗时极长的操作(比如某个网络调用),这样会阻塞住Nginx,又因为我们没有把控制权交还给Nginx,而是在ngx_http_mytest_handler中让Nginx worker进程休眠了(如等待网络的回包),所以,这就会导致Nginx出现性能问题,该进程上的其他用户请求也得不到响应。可如果我们把这个耗时极长的操作分为上下两个部分(就像Linux内核中对中断处理的划分),上半部分和下半部分都是无阻塞的(耗时很少的操作),这样,在ngx_http_mytest_handler进入时调用上半部分,然后返回NGX_DONE,把控制交还给Nginx,从而让Nginx继续处理其他请求。在下半部分被触发时(这里不探讨具体的实现方式,事实上使用upstream方式做反向代理时用的就是这种思想),再回调下半部分处理方法,这样就可以保证Nginx的高性能特性了。如果需要彻底了解NGX_DONE的意义,那么必须学习第11章内容,其中还涉及请求的引用计数内容。
    下面看一下包体接收完毕后的回调方法原型ngx_http_client_body_handler_pt是如何定义的:
    typedef void (*ngx_http_client_body_handler_pt)(ngx_http_request_t *r);
    其中,有参数ngx_http_request_t *r,这个请求的信息都可以从r中获得。这样可以定义一个方法void func(ngx_http_request_t *r),在Nginx接收完包体时调用它,另外,后续的流程也都会写在这个方法中,例如:
    void ngx_http_mytest_body_handler(ngx_http_request_t *r)
    {
     …
    }
    注意 ngx_http_mytest_body_handler的返回类型是void,Nginx不会根据返回值做一些收尾工作,因此,我们在该方法里处理完请求时必须要主动调用ngx_http_finalize_request方法来结束请求。
    接收包体时可以这样写:
            ngx_int_t rc = ngx_http_read_client_request_body(r, ngx_http_mytest_body_handler);

            if (rc >= NGX_http_SPECIAL_RESPONSE) {
                return rc;
            }
            return NGX_DONE;
    Nginx异步接收HTTP请求的包体的内容将在11.8节中详述。
    如果不想处理请求中的包体,那么可以调用ngx_http_discard_request_body方法将接收自客户端的HTTP包体丢弃掉。例如:
    ngx_int_t rc = ngx_http_discard_request_body(r);
    if (rc != NGX_OK) {
        return rc;
    }
    ngx_http_discard_request_body只是丢弃包体,不处理包体不就行了吗?何必还要调用ngx_http_discard_request_body方法呢?其实这一步非常有意义,因为有些客户端可能会一直试图发送包体,而如果HTTP模块不接收发来的TCP流,有可能造成客户端发送超时。

    接收完请求的包体后,可以在r->request_body->temp_file->file中获取临时文件(假定将r->request_body_in_file_only标志位设为1,那就一定可以在这个变量获取到包体。更复杂的接收包体的方式本节暂不讨论)。file是一个ngx_file_t类型。这里,我们可以从r->request_body->temp_file->file.name中获取Nginx接收到的请求包体所在文件的名称(包括路径)。

返回值的意义

 每个阶段都有一个与之相关的handler的列表。一旦把handler注册到对应的阶段,那么handler就会返回某个下面的值:
NGX_OK:请求已经成功处理,请求将会传到下一个阶段。
NGX_DECLINED:请求需要被转发到本阶段的下一个handler
NGX_AGAIN,NGX_DONE:请求已经被正确处理,同时请求被挂起,直到某个事件(子请求结束、socket可写或超时等)到来,handler才会再次被调用。

//函数功能:解析请求行。
static void ngx_http_process_request_line(ngx_event_t *rev)
{
    。。。
    rc = NGX_AGAIN;
    for ( ;; ) {
        if (rc == NGX_AGAIN) {   // NO.1
            n = ngx_http_read_request_header(r);  
 //recv()读取请求头信息
            if (n == NGX_AGAIN || n == NGX_ERROR) {
                return;
            }
        }

        //记录请求行中的请求方法(Method),请求uri以及http协议版本在缓冲区中的起始位置
        rc = ngx_http_parse_request_line(r, r->header_in);
        if (rc == NGX_OK) {
            …
            c->log->action = "reading client request headers";
            rev->handler = ngx_http_process_request_headers;
            ngx_http_process_request_headers(rev);
            return;
        }
        。。。
    }
}

//函数功能:处理HTTP请求头。
static void  ngx_http_process_request_headers(ngx_event_t *rev)
{
   。。。
    if (rev->timedout) {  // 是否超时
        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
        c->timedout = 1;
        ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT);
        return;
    }
    。。。
    rc = NGX_AGAIN;
    for ( ;; ) {
        if (rc == NGX_AGAIN) {
            if (r->header_in->pos == r->header_in->end) {  //一般不需要
                rv = ngx_http_alloc_large_header_buffer(r, 0);
                。。。
            }
            n = ngx_http_read_request_header(r);   //快速返回了,因为已经读过
            if (n == NGX_AGAIN || n == NGX_ERROR) {
                return;
            }
        }
        rc = ngx_http_parse_header_line(r, r->header_in,
                                 cscf->underscores_in_headers);  //解析HTTP头部
        if (rc == NGX_OK) {
            if (r->invalid_header && cscf->ignore_invalid_headers) {
                。。。
            //以key 和value的格式保存HTTP头
            h = ngx_list_push(&r->headers_in.headers);   //ngx_http_headers_in_t ,存放key,value
            if (h == NULL) {
                ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
                return;
            }
            h->hash = r->header_hash;
            h->key.len = r->header_name_end - r->header_name_start;
            h->key.data = r->header_name_start; // "User-Agent",host
            h->key.data[h->key.len] = '\0';
            h->value.len = r->header_end - r->header_start;
            h->value.data = r->header_start;     
            。。。
            if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {  //检验函数
                return;
            }
            continue;
        }
        if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
            /* a whole header has been parsed successfully */
            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http header done");
            r->request_length += r->header_in->pos - r->header_in->start;
            r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE;
            rc = ngx_http_process_request_header(r);  //检验头信息如1.1协议需要host信息?
            if (rc != NGX_OK) {
                return;
            }
            ngx_http_process_request(r);  
            return;
        }
       。。。。
    }
}

//函数功能:处理请求。
static voidngx_http_process_request(ngx_http_request_t *r)
{
    。。。
    if (c->read->timer_set) {      // 超时,过了一会儿,就在这里了
        ngx_del_timer(c->read);  //here
    }
    c->read->handler = ngx_http_request_handler;
    c->write->handler = ngx_http_request_handler;
    r->read_event_handler = ngx_http_block_reading;
    ngx_http_handler(r);  //核心是ngx_http_core_run_phases,进入各阶段处理函数
    ngx_http_run_posted_requests(c);
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值