Nginx专题(十、HTTP框架初始化)

一节事件模块好像并没有搞的太懂,由于时间的关系,还是继续到nginx中一个重要的知识点http模块,作为web服务器这个是nginx中最常用的模块了,遇到啥不懂的,再回去看看事件模块了。

10.1 HTTP框架概述

这个老生长谈了,不说那么多了,贴代码

static ngx_command_t  ngx_http_commands[] = {

    { ngx_string("http"),
      NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
      ngx_http_block,
      0,
      0,
      NULL },

      ngx_null_command
};


static ngx_core_module_t  ngx_http_module_ctx = {
    ngx_string("http"),
    NULL,
    NULL
};


ngx_module_t  ngx_http_module = {
    NGX_MODULE_V1,
    &ngx_http_module_ctx,                  /* module context */
    ngx_http_commands,                     /* module directives */
    NGX_CORE_MODULE,                       /* module type */
    NULL,                                  /* init master */
    NULL,                                  /* init module */
    NULL,                                  /* init process */
    NULL,                                  /* init thread */
    NULL,                                  /* exit thread */
    NULL,                                  /* exit process */
    NULL,                                  /* exit master */
    NGX_MODULE_V1_PADDING
};

这就是http的核心模块,接下来我们在看看http模块独立的结构体

typedef struct {
    ngx_int_t   (*preconfiguration)(ngx_conf_t *cf);
    ngx_int_t   (*postconfiguration)(ngx_conf_t *cf);

    void       *(*create_main_conf)(ngx_conf_t *cf);
    char       *(*init_main_conf)(ngx_conf_t *cf, void *conf);

    void       *(*create_srv_conf)(ngx_conf_t *cf);
    char       *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);

    void       *(*create_loc_conf)(ngx_conf_t *cf);
    char       *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);
} ngx_http_module_t;

就是http模块单独的结构体,专门处理配置文件的

接下 在看看配置文件的结构体

typedef struct {
    void        **main_conf;
    void        **srv_conf;
    void        **loc_conf;
} ngx_http_conf_ctx_t;

之前开发过http模块的都知道配置文件有main.srv.loc,这里正好对应这三个结构,接下来就好好分析一下http模块。

10.2 管理HTTP模块的配置项

http的配置项确实很多,接下来看看nginx是怎么管理的

10.2.1 管理main级别下的配置项

话不多说上图

在这里插入图片描述

10.2.2 管理server级别下的配置项

在这里插入图片描述

有点乱,nginx这个配置文件设置的是递归的方式,确实有点难受,不过对着源码看的话,应该还能看的懂。

10.2.3 管理loc级别下的配置项

在这里插入图片描述

sever下的loc的配置项,是使用了双向链表来连接起来的

10.2.4 不同级别配置项的合并

先看看调用的时候“

for (m = 0; cf->cycle->modules[m]; m++) {
        if (cf->cycle->modules[m]->type != NGX_HTTP_MODULE) {
            continue;
        }

    	
        module = cf->cycle->modules[m]->ctx;
        //ctx_index是这个HTTP模块在所有HTTTP模块中的序号
    	mi = cf->cycle->modules[m]->ctx_index;
		//合并这个http块下的第几个配置
        rv = ngx_http_merge_servers(cf, cmcf, module, mi);
        if (rv != NGX_CONF_OK) {
            goto failed;
        }
    }

看看真正合并的函数

static char *
ngx_http_merge_servers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf,
    ngx_http_module_t *module, ngx_uint_t ctx_index)
{
    char                        *rv;
    ngx_uint_t                   s;
    ngx_http_conf_ctx_t         *ctx, saved;
    ngx_http_core_loc_conf_t    *clcf;
    ngx_http_core_srv_conf_t   **cscfp;

    cscfp = cmcf->servers.elts;
    ctx = (ngx_http_conf_ctx_t *) cf->ctx;
    saved = *ctx;
    rv = NGX_CONF_OK;
	//遍历所有server下对应的ngx_http_core_srv_conf_t结构体
    for (s = 0; s < cmcf->servers.nelts; s++) {

        /* merge the server{}s' srv_conf's */

        ctx->srv_conf = cscfp[s]->ctx->srv_conf;

        //进行main和srv合并
        if (module->merge_srv_conf) {
            rv = module->merge_srv_conf(cf, saved.srv_conf[ctx_index],
                                        cscfp[s]->ctx->srv_conf[ctx_index]);
            if (rv != NGX_CONF_OK) {
                goto failed;
            }
        }

        if (module->merge_loc_conf) {

            /* merge the server{}'s loc_conf */

            ctx->loc_conf = cscfp[s]->ctx->loc_conf;
			//首先是main和loc合并
            rv = module->merge_loc_conf(cf, saved.loc_conf[ctx_index],
                                        cscfp[s]->ctx->loc_conf[ctx_index]);
            if (rv != NGX_CONF_OK) {
                goto failed;
            }

            /* merge the locations{}' loc_conf's */

            clcf = cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index];
			//server块下的loc与loc合并
            rv = ngx_http_merge_locations(cf, clcf->locations,
                                          cscfp[s]->ctx->loc_conf,
                                          module, ctx_index);
            if (rv != NGX_CONF_OK) {
                goto failed;
            }
        }
    }

failed:

    *ctx = saved;

    return rv;
}

10.3 监听端口的管理

每监听一个TCP端口,都将使用一个独立ngx_htttp_conf_port_t结构表示,

typedef struct {
    ngx_int_t                  family;
    in_port_t                  port;
    ngx_array_t                addrs;     /* array of ngx_http_conf_addr_t */
} ngx_http_conf_port_t;

这个结构会由全局的ngx_http_core_main_conf_t结构体保存

typedef struct {
    ngx_array_t                servers;         /* ngx_http_core_srv_conf_t */


    ngx_array_t               *ports;

} ngx_http_core_main_conf_t;

ngx_http_conf_port_t结构中的addrs是一个动态数组,里面存储了监听的多个server地址,我们看看数组成员:

typedef struct {
    ngx_http_listen_opt_t      opt;

    ngx_hash_t                 hash;
    ngx_hash_wildcard_t       *wc_head;
    ngx_hash_wildcard_t       *wc_tail;

#if (NGX_PCRE)
    ngx_uint_t                 nregex;
    ngx_http_server_name_t    *regex;
#endif

    /* the default server configuration for this address:port */
    //该监听端口下对应的默认server虚拟主机
    ngx_http_core_srv_conf_t  *default_server;
    //servers动态数组成员指向ngx_http_srv_conf_结构体
    ngx_array_t                servers;  /* array of ngx_http_core_srv_conf_t */
} ngx_http_conf_addr_t;

我们要快速检索sever,是可以通过hash,wc_head。wc_tail成员直接获取的,在前面见过怎么初始化这些哈希表。

loc的快速检索,是在初始化的时候,有动态生成了一个完全平衡的二叉树,因为这是是不需要改变的,所以不需要用红黑树,直接自动生成即可。

10.4 HTTP请求的11个处理阶段

HTP框架依据常见的处理流程将处理阶段划分为11个阶段,其中每个处理阶段都可以由任意多个HTP模块流水式的处理请求。

typedef enum {
	//在接收到完整的HTTP头部后处理的HTTP阶段
    NGX_HTTP_POST_READ_PHASE = 0,
	//在将请求的URI与location表达式匹配前,修改请求的URI是一个独立的HTTP阶段
    NGX_HTTP_SERVER_REWRITE_PHASE,
	//根据请求的URI寻找匹配的loc表达式,这个阶段只能有ngx_http_core_module模块实现
    NGX_HTTP_FIND_CONFIG_PHASE,
    //在NGX_HTTP_FIND_CONFIG_PHASE阶段寻找到匹配的loc之后再修改请求的URI
    NGX_HTTP_REWRITE_PHASE,
    //这一阶段是用于在rewrite重写urL后,防止错误的nginx.conf配置导致死循环
    NGX_HTTP_POST_REWRITE_PHASE,
	//表示在处理ngx_HTTP_ACCESS_PHASE阶段决定请求的访问权限前,HTTP模块可以介入的处理阶段
    NGX_HTTP_PREACCESS_PHASE,
	//这个阶段用于让HTTP模块判断是否允许这个请求访问nginx服务器
    NGX_HTTP_ACCESS_PHASE,
    //这个阶段是给NGX_HTTP_ACCESS_PHASE阶段收尾
    NGX_HTTP_POST_ACCESS_PHASE,
	//
    NGX_HTTP_PRECONTENT_PHASE,
	//用于处理HTTP请求内容的阶段,这是大部分HTTP模块最愿意介入的阶段
    NGX_HTTP_CONTENT_PHASE,
	//处理完请求后记录日志阶段
    NGX_HTTP_LOG_PHASE
} ngx_http_phases;

我们在回到http初始化过程,

static ngx_int_t
ngx_http_init_phases(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf)
{
    if (ngx_array_init(&cmcf->phases[NGX_HTTP_POST_READ_PHASE].handlers,
                       cf->pool, 1, sizeof(ngx_http_handler_pt))
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    if (ngx_array_init(&cmcf->phases[NGX_HTTP_SERVER_REWRITE_PHASE].handlers,
                       cf->pool, 1, sizeof(ngx_http_handler_pt))
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    if (ngx_array_init(&cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers,
                       cf->pool, 1, sizeof(ngx_http_handler_pt))
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    if (ngx_array_init(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers,
                       cf->pool, 1, sizeof(ngx_http_handler_pt))
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    if (ngx_array_init(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers,
                       cf->pool, 2, sizeof(ngx_http_handler_pt))
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    if (ngx_array_init(&cmcf->phases[NGX_HTTP_PRECONTENT_PHASE].handlers,
                       cf->pool, 2, sizeof(ngx_http_handler_pt))
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    if (ngx_array_init(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers,
                       cf->pool, 4, sizeof(ngx_http_handler_pt))
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    if (ngx_array_init(&cmcf->phases[NGX_HTTP_LOG_PHASE].handlers,
                       cf->pool, 1, sizeof(ngx_http_handler_pt))
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    return NGX_OK;
}

我们来看看phase的意义:

typedef struct {
    ngx_array_t                servers;         /* ngx_http_core_srv_conf_t */
	//由下面各阶段处理方法构成的phases数组构建的阶段引擎才是流水式处理HTTP请求的实际数据结构
    ngx_http_phase_engine_t    phase_engine;

	...

    ngx_array_t               *ports;
	//用于在HTTP框架初始化时帮组各个HTTP模块在任意阶段中添加HTTP处理方法
    ngx_http_phase_t           phases[NGX_HTTP_LOG_PHASE + 1];
} ngx_http_core_main_conf_t;

用在HTTP的11个阶段的过程中,插入的回调函数的方法。

在哪里插入到这个结构中呢?

 for (m = 0; cf->cycle->modules[m]; m++) {
        if (cf->cycle->modules[m]->type != NGX_HTTP_MODULE) {
            continue;
        }

        module = cf->cycle->modules[m]->ctx;

        if (module->postconfiguration) {
            if (module->postconfiguration(cf) != NGX_OK) {
                return NGX_CONF_ERROR;
            }
        }
    }

http框架代码会调用postconfiguration这个函数,这时候,需要介入处理的话,就需要实现这个函数,并添加到phase中,我们看看官方有一个实现ngx_http_realip_init:

static ngx_int_t
ngx_http_realip_init(ngx_conf_t *cf)
{
    ngx_http_handler_pt        *h;
    ngx_http_core_main_conf_t  *cmcf;

    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);

    h = ngx_array_push(&cmcf->phases[NGX_HTTP_POST_READ_PHASE].handlers);
    if (h == NULL) {
        return NGX_ERROR;
    }

    *h = ngx_http_realip_handler;

    h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
    if (h == NULL) {
        return NGX_ERROR;
    }

    *h = ngx_http_realip_handler;

    return NGX_OK;
}

看到的结果就是往这个数组中填充回调函数

我们跟着框架代码继续走,还有这个函数ngx_http_init_phase_handlers处理phase有关的:

static ngx_int_t
ngx_http_init_phase_handlers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf)
{
    ngx_int_t                   j;
    ngx_uint_t                  i, n;
    ngx_uint_t                  find_config_index, use_rewrite, use_access;
    ngx_http_handler_pt        *h;
    ngx_http_phase_handler_t   *ph;
    ngx_http_phase_handler_pt   checker;

    cmcf->phase_engine.server_rewrite_index = (ngx_uint_t) -1;
    cmcf->phase_engine.location_rewrite_index = (ngx_uint_t) -1;
    find_config_index = 0;
    use_rewrite = cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers.nelts ? 1 : 0;
    use_access = cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers.nelts ? 1 : 0;

    n = 1                  /* find config phase */
        + use_rewrite      /* post rewrite phase */
        + use_access;      /* post access phase */

    for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {
        n += cmcf->phases[i].handlers.nelts;
    }

    ph = ngx_pcalloc(cf->pool,
                     n * sizeof(ngx_http_phase_handler_t) + sizeof(void *));
    if (ph == NULL) {
        return NGX_ERROR;
    }

    cmcf->phase_engine.handlers = ph;
    n = 0;

    for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {
        h = cmcf->phases[i].handlers.elts;

        switch (i) {

        case NGX_HTTP_SERVER_REWRITE_PHASE:
            if (cmcf->phase_engine.server_rewrite_index == (ngx_uint_t) -1) {
                cmcf->phase_engine.server_rewrite_index = n;
            }
            checker = ngx_http_core_rewrite_phase;

            break;

        case NGX_HTTP_FIND_CONFIG_PHASE:
            find_config_index = n;

            ph->checker = ngx_http_core_find_config_phase;
            n++;
            ph++;

            continue;

        case NGX_HTTP_REWRITE_PHASE:
            if (cmcf->phase_engine.location_rewrite_index == (ngx_uint_t) -1) {
                cmcf->phase_engine.location_rewrite_index = n;
            }
            checker = ngx_http_core_rewrite_phase;

            break;

        case NGX_HTTP_POST_REWRITE_PHASE:
            if (use_rewrite) {
                ph->checker = ngx_http_core_post_rewrite_phase;
                ph->next = find_config_index;
                n++;
                ph++;
            }

            continue;

        case NGX_HTTP_ACCESS_PHASE:
            checker = ngx_http_core_access_phase;
            n++;
            break;

        case NGX_HTTP_POST_ACCESS_PHASE:
            if (use_access) {
                ph->checker = ngx_http_core_post_access_phase;
                ph->next = n;
                ph++;
            }

            continue;

        case NGX_HTTP_CONTENT_PHASE:
            checker = ngx_http_core_content_phase;
            break;

        default:
            checker = ngx_http_core_generic_phase;
        }

        n += cmcf->phases[i].handlers.nelts;

        for (j = cmcf->phases[i].handlers.nelts - 1; j >= 0; j--) {
            ph->checker = checker;
            ph->handler = h[j];
            ph->next = n;
            ph++;
        }
    }

    return NGX_OK;
}

我们先看phase_engine这个成员:

typedef struct {
    //handler是由ngx_http_phase_handler_t构成的数组首地址,它表示一个请求可能经历的所有ngx_http_handler_pt处理方法
    ngx_http_phase_handler_t  *handlers;
    //表示NGX_HTTP_SERVER_REWRITE_PHASE阶段第1个ngx_http_phase_handler_t处理方法在handlers数组的序号,用在执行请求快速跳转到NGX_HTTP_SERVER_REWRITE_PHASE这个阶段
    ngx_uint_t                 server_rewrite_index;
    //表示NGX_HTTP_REWRITE_PHASE阶段第1个
    ngx_uint_t                 location_rewrite_index;
} ngx_http_phase_engine_t;

struct ngx_http_phase_handler_s {
    //在处理某一个HTTP阶段时,HTTP框架将会在checker方法已实现的前提下首先调用checker方法来处理请求。而不是直接调用任何阶段中的handler方法,只会在checker方法中才会去调用handler方法
    ngx_http_phase_handler_pt  checker;
    //只能通过定义handler方法才能介入某一个HTTP处理阶段以处理请求
    ngx_http_handler_pt        handler;
    //将要执行下一个HTTP处理阶段的序号
    ngx_uint_t                 next;
};

看到这里才明白,phase_engine控制运行过程中一个HTTP请求所要经过的HTTP处理阶段,而ohase数组更像是一个临时变量,实际上也仅在nginx启动过程中用到。

但是nginx框架并没有直接调用handler,而是调用checker,我们看看一个例子:

ngx_int_t
ngx_http_core_content_phase(ngx_http_request_t *r,
    ngx_http_phase_handler_t *ph)
{
    size_t     root;
    ngx_int_t  rc;
    ngx_str_t  path;

    if (r->content_handler) {
        r->write_event_handler = ngx_http_request_empty_handler;
        ngx_http_finalize_request(r, r->content_handler(r));
        return NGX_OK;
    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "content phase: %ui", r->phase_handler);

    rc = ph->handler(r);		//这个就是绑定的回调函数

    if (rc != NGX_DECLINED) {
        ngx_http_finalize_request(r, rc);
        return NGX_OK;
    }

    /* rc == NGX_DECLINED */

    ph++;

    if (ph->checker) {
        r->phase_handler++;
        return NGX_AGAIN;
    }

    /* no content handler was found */

    if (r->uri.data[r->uri.len - 1] == '/') {

        if (ngx_http_map_uri_to_path(r, &path, &root, 0) != NULL) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                          "directory index of \"%s\" is forbidden", path.data);
        }

        ngx_http_finalize_request(r, NGX_HTTP_FORBIDDEN);
        return NGX_OK;
    }

    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "no handler found");

    ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND);
    return NGX_OK;
}

这里是不是奇怪,因为会先判断r->content_handler这个函数,然后再调用我们绑定的handler,这是因为NGX_HTTP_CONTENT_PHASE阶段可以用另种方式介入到这个阶段的处理中,第一种就是我们上面说的,第二种就是我们之前实现的HTTP模块的时候,绑定到ngx_http_core_loc_conf_t结构体的handler指针中,nginx框架会把这个handler绑定到ngx_http_request_t的content_handler,这样就可以做到介入了。

如果希望这个ngx_http_handler_pt方法应用于所有的用户请求,则应该放到phases[NGX_HTTP_CONTENT_PHASE]胴体数组中;反之,如果希望这个方式仅应用于URI匹配了某些location的用户请求,则应该在一个location写的回到方法中。

10.5 HTTP框架的初始化流程

懒的写了,就这样了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值