nignx解析配置文件时,当遇到一下配置时,会做哪些事情?
upstream test.balancer.com {
1.1.1.1:80;
2.2.2.2:80;
}
首先需要解决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;
没解析到一个指令,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;
}