详解Nginx的核心配置模块中对于请求体的接受流程

2019-10-17 19:57:32王旭

ngx_http_read_discarded_request_body()函数非常简单,它循环的从链接中读取数据并丢弃,直到读完接收缓冲区的所有数据,如果请求体已经被读完了,该函数会设置读事件的处理函数为ngx_http_block_reading,这个函数仅仅删除水平触发的读事件,防止同一事件不断被触发。
再来看一下读事件的处理函数ngx_http_discarded_request_body_handler,这个函数每次读事件来时会被调用,先看一下它的源码:

void 
ngx_http_discarded_request_body_handler(ngx_http_request_t *r) 
{ 
  ... 
 
  c = r->connection; 
  rev = c->read; 
 
  if (rev->timedout) { 
    c->timedout = 1; 
    c->error = 1; 
    ngx_http_finalize_request(r, NGX_ERROR); 
    return; 
  } 
 
  if (r->lingering_time) { 
    timer = (ngx_msec_t) (r->lingering_time - ngx_time()); 
 
    if (timer <= 0) { 
      r->discard_body = 0; 
      r->lingering_close = 0; 
      ngx_http_finalize_request(r, NGX_ERROR); 
      return; 
    } 
 
  } else { 
    timer = 0; 
  } 
 
  rc = ngx_http_read_discarded_request_body(r); 
 
  if (rc == NGX_OK) { 
    r->discard_body = 0; 
    r->lingering_close = 0; 
    ngx_http_finalize_request(r, NGX_DONE); 
    return; 
  } 
 
  /* rc == NGX_AGAIN */ 
 
  if (ngx_handle_read_event(rev, 0) != NGX_OK) { 
    c->error = 1; 
    ngx_http_finalize_request(r, NGX_ERROR); 
    return; 
  } 
 
  if (timer) { 
 
    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); 
 
    timer *= 1000; 
 
    if (timer > clcf->lingering_timeout) { 
      timer = clcf->lingering_timeout; 
    } 
 
    ngx_add_timer(rev, timer); 
  } 
} 

函数一开始就处理了读事件超时的情况,之前说到在ngx_http_discard_request_body()函数中已经删除了读事件的定时器,那么什么时候会设置定时器呢?答案就是在nginx已经处理完该请求,但是又没有完全将该请求的请求体丢弃的时候(客户端可能还没有发送过来),在ngx_http_finalize_connection()函数中,如果检查到还有未丢弃的请求体时,nginx会添加一个读事件定时器,它的时长为lingering_timeout指令所指定,默认为5秒,不过这个时间仅仅两次读事件之间的超时时间,等待请求体的总时长为lingering_time指令所指定,默认为30秒。这种情况中,该函数如果检测到超时事件则直接返回并断开连接。同样,还需要控制整个丢弃请求体的时长不能超过lingering_time设置的时间,如果超过了最大时长,也会直接返回并断开连接。
如果读事件发生在请求处理完之前,则不用处理超时事件,也不用设置定时器,函数只是简单的调用ngx_http_read_discarded_request_body()来读取并丢弃数据。