nginx源码分析—-解析upstream配置

  • Post author:
  • Post category:其他


nignx解析配置文件时,当遇到一下配置时,会做哪些事情?

upstream test.balancer.com {
			1.1.1.1:80;
			2.2.2.2:80;
}



一、 存储upstream配置的三大结构体

首先需要解决upstream配置的存储问题,nginx是通过ngx_http_upstream_main_conf_t,ngx_http_upstream_srv_conf_t,ngx_http_upstream_server_t 三个结构体来存储upstream配置的。

其中ngx_http_upstream_main_conf_t的定义如下:

typedef struct {
    ngx_hash_t                       headers_in_hash;
    ngx_array_t                      upstreams;            //每一个upstream都存储upstreams数组里。
                                             /* ngx_http_upstream_srv_conf_t */
} ngx_http_upstream_main_conf_t;

其中的upstream成员是一个数组,数组中的每个元素又是ngx_http_upstream_srv_conf_t类型的。

ngx_http_upstream_srv_conf_t定义如下:

struct ngx_http_upstream_srv_conf_s {
    ngx_http_upstream_peer_t         peer;
    void                           **srv_conf;

    ngx_array_t                     *servers;  /* ngx_http_upstream_server_t */

    ngx_uint_t                       flags;
    ngx_str_t                        host;
    u_char                          *file_name;
    ngx_uint_t                       line;
    in_port_t                        port;
    ngx_uint_t                       no_port;  /* unsigned no_port:1 */

#if (NGX_HTTP_UPSTREAM_ZONE)
    ngx_shm_zone_t                  *shm_zone;
#endif
};

其中的servers字段又保存了该upstream配置中每个server的详细信息,包括ip、weight,max_conns,down等信息。

typedef struct {
    ngx_str_t                        name;
    ngx_addr_t                      *addrs;
    ngx_uint_t                       naddrs;
    ngx_uint_t                       weight;
    ngx_uint_t                       max_conns;
    ngx_uint_t                       max_fails;
    time_t                           fail_timeout;
    ngx_msec_t                       slow_start;
    ngx_uint_t                       down;

    unsigned                         backup:1;

    NGX_COMPAT_BEGIN(6)
    NGX_COMPAT_END
} ngx_http_upstream_server_t;



二 解析upstream配置的过程

没解析到一个指令,nginx主框架都会遍历所有的模块,查看他们的commands数组,看哪个模块对该配置指令感兴趣,并调用对应的handler方法去处理。

而对”upstream”配置感兴趣的模块是ngx_http_upstream_module,并且定义的handler方法是ngx_http_upstream。

那么,接下来看看ngx_http_upstream做了哪些事情:

1 调用ngx_http_upstream_add这个方法,为新解析到的upstream创建对应的ngx_http_upstream_srv_conf_t结构体,并将其添加到 ngx_http_upstream_main_conf_t的数组中。

2 创建新的ngx_http_conf_ctx_t 结构体,这个结构体在nginx启动流程中用到,它存储了所有的main、srv、loc的配置结构体。

3 将当前配置项的ctx的main_conf直接赋值给新的ctx,因为解析upstream配置项是不会影响到main的配置结构体的,因此可以直接赋值,延用之前的main_conf。

4 遍历cycle的module数组,调用所有http模块的create_srv_conf和create_loc_conf方法,生成新的各HTTP模块的配置结构体,并添加到新ctx的src_conf和loc_conf数组中。

5 在第1步中返回的是ngx_http_upstream_srv_conf_t结构体,它只初始化了一些该upstream的基本信息,并未涉及其servers成员,接下来就是创建ngx_http_upstream_server_t结构体,为接下来解析每个server做准备。

6 将新的ctx替换掉旧的cf的ctx成员。可见,每次解析一个配置项,ngx_conf_t cf的ctx成员都会不停更新,来保存最新的配置上下文信息。

7 调用ngx_conf_parse函数,继续向下解析。

注意第4步:遍历所有的http模块,其中包括了ngx_http_core_module,以及ngx_http_upstream_module自身,调用他们的create_srv_conf和create_loc_conf方法来重新生成对应的存储配置项的结构体,这些操作在之前就已经做过了。

static char *
ngx_http_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
{
    char *rv;
    void *mconf;
    ngx_str_t *value;
    ngx_url_t u;
    ngx_uint_t m;
    ngx_conf_t pcf;
    ngx_http_module_t *module;
    ngx_http_conf_ctx_t *ctx, *http_ctx;
    ngx_http_upstream_srv_conf_t *uscf;

    ngx_memzero(&u, sizeof(ngx_url_t));

    value = cf->args->elts;
    u.host = value[1];
    u.no_resolve = 1;
    u.no_port = 1;
    /*每解析一个upstream,就生成一个ngx_http_upstream_srv_conf_t对象,当然也会查重*/    
    uscf = ngx_http_upstream_add(cf, &u, NGX_HTTP_UPSTREAM_CREATE | NGX_HTTP_UPSTREAM_WEIGHT | NGX_HTTP_UPSTREAM_MAX_CONNS | NGX_HTTP_UPSTREAM_MAX_FAILS | NGX_HTTP_UPSTREAM_FAIL_TIMEOUT | NGX_HTTP_UPSTREAM_DOWN | NGX_HTTP_UPSTREAM_BACKUP);
    if (uscf == NULL)
    {
        return NGX_CONF_ERROR;
    }

    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
    if (ctx == NULL)
    {
        return NGX_CONF_ERROR;
    }

    http_ctx = cf->ctx;
    ctx->main_conf = http_ctx->main_conf;

    /* the upstream{}'s srv_conf */
    ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
    if (ctx->srv_conf == NULL)
    {
        return NGX_CONF_ERROR;
    }
    // ngx_http_upstream_module.ctx_index 表示upstream模块在所有的HTTP模块中的序号
    ctx->srv_conf[ngx_http_upstream_module.ctx_index] = uscf;

    uscf->srv_conf = ctx->srv_conf;

    /* the upstream{}'s loc_conf */

    ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
    if (ctx->loc_conf == NULL)
    {
        return NGX_CONF_ERROR;
    }

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

        module = cf->cycle->modules[m]->ctx;
        //重新调用每个HTTP模块的create_srv_conf方法来填充ctx->srv_conf数组
        if (module->create_srv_conf)
        {
            mconf = module->create_srv_conf(cf);
            if (mconf == NULL)
            {
                return NGX_CONF_ERROR;
            }

            ctx->srv_conf[cf->cycle->modules[m]->ctx_index] = mconf;
        }
        // 重新调用每个HTTP模块的create_loc_conf方法来填充ctx->loc_conf数组
        if (module->create_loc_conf)
        {
            mconf = module->create_loc_conf(cf);
            if (mconf == NULL)
            {
                return NGX_CONF_ERROR;
            }

            ctx->loc_conf[cf->cycle->modules[m]->ctx_index] = mconf;
        }
    }
    //接下来再解析每个upstream里的server
    uscf->servers = ngx_array_create(cf->pool, 4,
                                     sizeof(ngx_http_upstream_server_t));
    if (uscf->servers == NULL)
    {
        return NGX_CONF_ERROR;
    }

    /* parse inside upstream{} */

    pcf = *cf;
    //用新的ctx替换掉旧的cf->ctx
    cf->ctx = ctx;
    cf->cmd_type = NGX_HTTP_UPS_CONF;

    // 第二个参数传的是NULL,即filename=NULL
    rv = ngx_conf_parse(cf, NULL);

    *cf = pcf;

    if (rv != NGX_CONF_OK)
    {
        return rv;
    }

    if (uscf->servers->nelts == 0)
    {
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                           "no servers are inside upstream");
        return NGX_CONF_ERROR;
    }

    return rv;
}

调用ngx_http_upstream_add函数将新的upstream添加到ngx_http_upstream_main_conf_t 的upstream数组中。

ngx_http_upstream_add主要干了一下几件事情:

1 检查是否有重复的upstream,如果有则返回NULL,添加失败。

2 创建新的ngx_http_upstream_srv_conf_t 结构体来保存新upstream的配置,并添加到ngx_http_upstream_main_conf_t 的upstream数组中。

3 返回添加成功后的ngx_http_upstream_srv_conf_t 结构体。

ngx_http_upstream_add(ngx_conf_t *cf, ngx_url_t *u, ngx_uint_t flags)
{
    ngx_uint_t i;
    ngx_http_upstream_server_t *us;
    ngx_http_upstream_srv_conf_t *uscf, **uscfp;
    ngx_http_upstream_main_conf_t *umcf;
	...
	// 1 获取upstream的main配置的结构体,查看现有的upstreams数组中有没有重名的upstream
    umcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_upstream_module);

    uscfp = umcf->upstreams.elts;

    for (i = 0; i < umcf->upstreams.nelts; i++)
    {
        // 查找当前保存的upstream中,有没有重名的
        if (uscfp[i]->host.len != u->host.len || ngx_strncasecmp(uscfp[i]->host.data, u->host.data, u->host.len) != 0)
        {
            continue;
        }
        // 找到重名的upstream,检查标识位flags是否等于NGX_HTTP_UPSTREAM_CRTEATE
        if ((flags & NGX_HTTP_UPSTREAM_CREATE) && (uscfp[i]->flags & NGX_HTTP_UPSTREAM_CREATE))
        {
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                               "duplicate upstream \"%V\"", &u->host);
            return NULL;
        }

        if ((uscfp[i]->flags & NGX_HTTP_UPSTREAM_CREATE) && !u->no_port)
        {
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                               "upstream \"%V\" may not have port %d",
                               &u->host, u->port);
            return NULL;
        }

        if ((flags & NGX_HTTP_UPSTREAM_CREATE) && !uscfp[i]->no_port)
        {
            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
                          "upstream \"%V\" may not have port %d in %s:%ui",
                          &u->host, uscfp[i]->port,
                          uscfp[i]->file_name, uscfp[i]->line);
            return NULL;
        }

        if (uscfp[i]->port && u->port && uscfp[i]->port != u->port)
        {
            continue;
        }

        if (flags & NGX_HTTP_UPSTREAM_CREATE)
        {
            uscfp[i]->flags = flags;
            uscfp[i]->port = 0;
        }

        return uscfp[i];
    }
	// 2 没有重名的,说明这是一个新的upstream,则需要创建一个ngx_http_upstream_srv_conf_t
    uscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_srv_conf_t));
    if (uscf == NULL)
    {
        return NULL;
    }

    uscf->flags = flags;
    uscf->host = u->host;
    uscf->file_name = cf->conf_file->file.name.data;
    uscf->line = cf->conf_file->line;
    uscf->port = u->port;
    uscf->no_port = u->no_port;

    // 第一次解析的upstream不走这里
    if (u->naddrs == 1 && (u->port || u->family == AF_UNIX))
    {
        uscf->servers = ngx_array_create(cf->pool, 1,
                                         sizeof(ngx_http_upstream_server_t));
        if (uscf->servers == NULL)
        {
            return NULL;
        }

        us = ngx_array_push(uscf->servers);
        if (us == NULL)
        {
            return NULL;
        }

        ngx_memzero(us, sizeof(ngx_http_upstream_server_t));

        us->addrs = u->addrs;
        us->naddrs = 1;
    }

	//3 将新的upstream配置添加到umcf的upstream数组中。
    uscfp = ngx_array_push(&umcf->upstreams);
    if (uscfp == NULL)
    {
        return NULL;
    }

    *uscfp = uscf;

    return uscf;
}



版权声明:本文为qq_26312651原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。