nginx源码分析(7)——请求处理

        在建立连接过程中,对于nginx监听到的每个客户端连接,都会将它的读事件的handler设置为ngx_http_init_request函数,这个函数就是请求处理的入口。在处理请求时,主要就是要解析http请求,比如:uri,请求行等,然后再根据请求生成响应。下面看一下nginx处理的具体过程。


1. ngx_http_init_request

        在ngx_http_init_connection函数中,将连接的读事件的handler设置为这个函数,在客户端发送请求时会被调用。

    /* ngx_event_t的data域存放事件对应的连接句柄 */
    c = rev->data;

    /* 在ngx_init_connection中对读事件添加了timer,超时直接返回*/
    if (rev->timedout) {
        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");

        ngx_http_close_connection(c);
        return;
    }

    /* 连接处理的request的计数 */
    c->requests++;

    /* ngx_http_connection_t */
    hc = c->data;

    if (hc == NULL) {
        hc = ngx_pcalloc(c->pool, sizeof(ngx_http_connection_t));
        if (hc == NULL) {
            ngx_http_close_connection(c);
            return;
        }
    }

    r = hc->request;

    if (r) {
        ngx_memzero(r, sizeof(ngx_http_request_t));

        r->pipeline = hc->pipeline;

        if (hc->nbusy) {
            r->header_in = hc->busy[0];
        }

    } else {
    	/* 为request分配空间 */
        r = ngx_pcalloc(c->pool, sizeof(ngx_http_request_t));
        if (r == NULL) {
            ngx_http_close_connection(c);
            return;
        }

        hc->request = r;
    }

    c->data = r;
    r->http_connection = hc;

    c->sent = 0;
    r->signature = NGX_HTTP_MODULE;

        上面一段代码完成获取连接、请求并进行一部分初始化工作,比如会为新的请求分配内存。

    /**
     * ngx_listening_t的servers存放监听同一端口的server,但它们的监听的地址可以不同。
     * 
     * 		      port
     * 		   /   |   \
     * 	        addr1 addr2 addr3 	   
     *           |     |     |
     *          conf1 conf2  conf3
     */
    port = c->listening->servers;

    r->connection = c;

    if (port->naddrs > 1) {

        /*
         * there are several addresses on this port and one of them
         * is an "*:port" wildcard so getsockname() in ngx_http_server_addr()
         * is required to determine a server address
         */

    	/* 获取连接c的socket绑定的本地地址 */
        if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {
            ngx_http_close_connection(c);
            return;
        }

        /* 根据连接c的socket地址匹配port->addrs,找到对应的address:port */
        switch (c->local_sockaddr->sa_family) {

#if (NGX_HAVE_INET6)
        case AF_INET6:
            sin6 = (struct sockaddr_in6 *) c->local_sockaddr;

            addr6 = port->addrs;

            /* the last address is "*" */

            for (i = 0; i < port->naddrs - 1; i++) {
                if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) {
                    break;
                }
            }

            addr_conf = &addr6[i].conf;

            break;
#endif

        default: /* AF_INET */
            sin = (struct sockaddr_in *) c->local_sockaddr;

            addr = port->addrs;

            /* the last address is "*" */

            for (i = 0; i < port->naddrs - 1; i++) {
                if (addr[i].addr == sin->sin_addr.s_addr) {
                    break;
                }
            }

            addr_conf = &addr[i].conf;

            break;
        }

    } else {

        switch (c->local_sockaddr->sa_family) {

#if (NGX_HAVE_INET6)
        case AF_INET6:
            addr6 = port->addrs;
            addr_conf = &addr6[0].conf;
            break;
#endif

        default: /* AF_INET */
            addr = port->addrs;
            addr_conf = &addr[0].conf;
            break;
        }
    }

    /* virtual hosts based on the address:port */
    r->virtual_names = addr_conf->virtual_names;

    /* the default server configuration for the address:port */
    cscf = addr_conf->default_server;

    /* 初始化为default server的配置,后续虚拟主机匹配成功会采用对应的配置 */
    r->main_conf = cscf->ctx->main_conf;
    r->srv_conf = cscf->ctx->srv_conf;
    r->loc_conf = cscf->ctx->loc_conf;

        注释中解释的很清楚,这段代码是为地址addr:port匹配server config,从而确定该请求的配置。由于nginx支持虚拟主机,所以这里确定了r->virtual_names是该addr:port对应的虚拟主机数组,后面会根据请求的HOST匹配对应的虚拟主机从而确定最终的配置。nginx的每个请求都有执行环境,这个环境就是ngx_request_t请求的main_conf、srv_conf和loc_conf。请求的执行环境就是nginx各个模块的配置信息,根据这些信息的不同请求的处理效果是不一样的。待解释完虚拟主机匹配后,再详细说明执行环境查找。

    rev->handler = ngx_http_process_request_line;
    r->read_event_handler = ngx_http_block_reading;
        将连接读事件的handler设置为ngx_http_process_request_line,在本方法的最后会直接调用rev->handler(rev)。为什么这么做?

        我的猜测是,在客户端第一次请求时,该连接的读事件的handler是ngx_init_request,用于处理话请求,后续请求直接复用,所以不需要执行ngx_init_request,所以需要将rev->handler设置成ngx_http_process_request_line,最后直接调用handler是为了在ngx_init_request中处理第一个请求。不知道这样对不对?

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
    c->log->file = clcf->error_log->file;
    if (!(c->log->log_level & NGX_LOG_DEBUG_CONNECTION)) {
        c->log->log_level = clcf->error_log->log_level;
    }

    /* initialise the temporary buffer for the request's connection */
    if (c->buffer 
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值