nginx http框架执行流程(7) - 发送数据

2018-07-29 21:29:04

CONTENT 阶段是 http处理引擎的最后一个处理阶段(LOG 阶段只记录日志,不处理请求),nginx 会使用过滤引擎发送响应数据,这是将再次监控连接上的写事件,但关注的重点是可写,持续地发送被缓冲的数据。

nginx 利用定时器实现了限速功能,处理逻辑主要是集中在 ngx_http_write_filter_module 里,这里暂不研究。

ngx_http_set_write_handler() 设置了发生响应数据时的写事件处理函数 

static ngx_int_t
ngx_http_set_write_handler(ngx_http_request_t *r)
{
    ngx_event_t               *wev;
    ngx_http_core_loc_conf_t  *clcf;

    // 当前的状态是正在发送数据
    r->http_state = NGX_HTTP_WRITING_REQUEST_STATE;

    // 如果当前是丢弃请求体,那么读handler是ngx_http_discarded_request_body_handler
    // 否则是ngx_http_test_reading
    r->read_event_handler = r->discard_body ?
                                ngx_http_discarded_request_body_handler:
                                ngx_http_test_reading;

    // 写事件handler是ngx_http_writer
    //
    // 真正的向客户端发送数据,调用send_chain
    // 如果数据发送不完,就保存在r->out里,返回again
    // 需要再次发生可写事件才能发送
    // 不是last、flush,且数据量较小(默认1460)
    // 那么这次就不真正调用write发送,减少系统调用的次数,提高性能
    // 在此函数里处理限速
    r->write_event_handler = ngx_http_writer;

    // 连接里的写事件
    wev = r->connection->write;

    // 如果此时是可写的,或者需要限速,那么直接返回
    // 不需要加入epoll事件
    if (wev->ready && wev->delayed) {
        return NGX_OK;
    }

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    // 不限速,需要加入发送超时
    // 即send_timeout时间内socket不可写则报错
    if (!wev->delayed) {
        ngx_add_timer(wev, clcf->send_timeout);
    }

    return NGX_OK;
}
static void
ngx_http_writer(ngx_http_request_t *r)
{
    ngx_int_t                  rc;
    ngx_event_t               *wev;
    ngx_connection_t          *c;
    ngx_http_core_loc_conf_t  *clcf;

    // 从请求里获得连接和写事件
    c = r->connection;
    wev = c->write;

    clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module);

    // 检查写事件是否已经超时
    if (wev->timedout) {
        c->timedout = 1;

        ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT);
        return;
    }

    // 没有超时, 但限速
    // 那么继续等待写事件
    if (wev->delayed || r->aio) {
        if (!wev->delayed) {
            ngx_add_timer(wev, clcf->send_timeout);
        }
        return;
    }

    // 调用过滤链表发送数据
    //
    // 真正的向客户端发送数据,调用send_chain
    // 如果数据发送不完,就保存在r->out里,返回again
    // 需要再次发生可写事件才能发送
    // 不是last、flush,且数据量较小(默认1460)
    // 那么这次就不真正调用write发送,而是先存起来待后面一起发送
    // 在此函数里处理限速
    rc = ngx_http_output_filter(r, NULL);

    // 出错直接结束请求
    if (rc == NGX_ERROR) {
        ngx_http_finalize_request(r, rc);
        return;
    }

    // rc == NGX_AGAIN/NGX_OK

    // 有数据被缓存,没有完全发送
    // 加上超时等待,注册写事件,等socket可写再发送
    // rc == NGX_AGAIN
    if (r->buffered || r->postponed || (r == r->main && c->buffered)) {

        if (!wev->delayed) {
            ngx_add_timer(wev, clcf->send_timeout);
        }

        return;
    }

    // rc == NGX_OK
    // 数据已经发送完毕

    // 设置写事件处理函数,不再有任何发送动作
    r->write_event_handler = ngx_http_request_empty_handler;

    // 结束请求,注意传入的rc,会有其他判断动作
    // 写事件处理函数也可能在这里再变为其他函数
    ngx_http_finalize_request(r, rc);
}


备注

1.测试环境centos7 64位,nginx版本为 1.14.0。
2..原文地址http://www.freecls.com/a/2712/e7


©著作权归作者所有
收藏
推荐阅读
简介
天降大任于斯人也,必先苦其心志。