最新消息:

nginx的http模块的upstream机制

nginx admin 1浏览
### 概述
upstream是nginx向上游发起tcp请求的一种机制。在nginx中有很多有用的模块都用到了该机制,例如proxy模块,memcache模块等。
upstream模块提供了两个配置指令:upstream和server来指定上游服务器地址。
### 代码
upstream机制的实现是在ngx_http_upstream.h|c, ngx_http_upstream_round_robin.h|c 这几个文件中。
先从程序启动解析配置指令开始。调用逻辑在ngx_http_block函数中。
首先会调用ctx的函数指针指向的ngx_http_upstream_create_main_conf函数创建一个结构体保存配置:
<br>typedef struct {<br>ngx_hash_t headers_in_hash;<br>ngx_array_t upstreams; // 一组上游服务器数组<br>/* ngx_http_upstream_srv_conf_t */<br>} ngx_http_upstream_main_conf_t;<br>
接着会调用ctx的另一个函数指针指向的函数ngx_http_upstream_add_variables添加变量。
<br>static ngx_int_t<br>ngx_http_upstream_add_variables(ngx_conf_t *cf)<br>{<br>ngx_http_variable_t *var, *v;<br>for (v = ngx_http_upstream_vars; v->name.len; v++) {<br>var = ngx_http_add_variable(cf, &v->name, v->flags);<br>if (var == NULL) {<br>return NGX_ERROR;<br>}<br>var->get_handler = v->get_handler;<br>var->data = v->data;<br>}<br>return NGX_OK;<br>}<br>
接着会解析NGX_HTTP_MAIN_CONF级别的配置,upstream模块只有upstream指令是该级别的。读取配置内容,直到处理到upstream name { … } ,会调用ngx_http_upstream函数处理。
<br>// 对应一组上游服务器,也就是一个upstream指令<br>struct ngx_http_upstream_srv_conf_s {<br>ngx_http_upstream_peer_t peer;<br>void **srv_conf;<br>ngx_array_t *servers; /* ngx_http_upstream_server_t */ // server 指令对应的配置,实际的上游服务器的地址<br>ngx_uint_t flags;<br>ngx_str_t host; // 一组上游服务器的名称<br>u_char *file_name;<br>ngx_uint_t line;<br>in_port_t port;<br>ngx_uint_t no_port; /* unsigned no_port:1 */<br>#if (NGX_HTTP_UPSTREAM_ZONE)<br>ngx_shm_zone_t *shm_zone;<br>#endif<br>};<br>typedef struct {<br>ngx_str_t name;<br>ngx_addr_t *addrs;<br>ngx_uint_t naddrs;<br>ngx_uint_t weight;<br>ngx_uint_t max_conns;<br>ngx_uint_t max_fails;<br>time_t fail_timeout;<br>ngx_msec_t slow_start;<br>unsigned down:1;<br>unsigned backup:1;<br>NGX_COMPAT_BEGIN(6)<br>NGX_COMPAT_END<br>} ngx_http_upstream_server_t;<br>static char *<br>ngx_http_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)<br>{<br>char *rv;<br>void *mconf;<br>ngx_str_t *value;<br>ngx_url_t u;<br>ngx_uint_t m;<br>ngx_conf_t pcf;<br>ngx_http_module_t *module;<br>ngx_http_conf_ctx_t *ctx, *http_ctx;<br>ngx_http_upstream_srv_conf_t *uscf;<br>ngx_memzero(&u, sizeof(ngx_url_t));<br>value = cf->args->elts;<br>u.host = value[1]; // name 一组上游服务器的名称。<br>u.no_resolve = 1;<br>u.no_port = 1;<br>// 分配ngx_http_upstream_srv_conf_t结构体添加到umcf->upstreams数组<br>uscf = ngx_http_upstream_add(cf, &u, NGX_HTTP_UPSTREAM_CREATE<br>\|NGX_HTTP_UPSTREAM_WEIGHT<br>\|NGX_HTTP_UPSTREAM_MAX_CONNS<br>\|NGX_HTTP_UPSTREAM_MAX_FAILS<br>\|NGX_HTTP_UPSTREAM_FAIL_TIMEOUT<br>\|NGX_HTTP_UPSTREAM_DOWN<br>\|NGX_HTTP_UPSTREAM_BACKUP);<br>if (uscf == NULL) {<br>return NGX_CONF_ERROR;<br>}<br>// http模块下每个block配置都会对应一个这样的上下文<br>// 指明所属block的模块配置 和 本block的模块配置<br>ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));<br>if (ctx == NULL) {<br>return NGX_CONF_ERROR;<br>}<br>http_ctx = cf->ctx;<br>// main_conf 指向所属的http{}<br>ctx->main_conf = http_ctx->main_conf;<br>/* the upstream{}'s srv_conf */<br>ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);<br>if (ctx->srv_conf == NULL) {<br>return NGX_CONF_ERROR;<br>}<br>// upstream 模块srv级别创建配置文件结构体的函数指针为空,所以再下边数组赋值不会被覆盖掉<br>ctx->srv_conf[ngx_http_upstream_module.ctx_index] = uscf;<br>uscf->srv_conf = ctx->srv_conf;<br>/* the upstream{}'s loc_conf */<br>ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);<br>if (ctx->loc_conf == NULL) {<br>return NGX_CONF_ERROR;<br>}<br>for (m = 0; cf->cycle->modules[m]; m++) {<br>if (cf->cycle->modules[m]->type != NGX_HTTP_MODULE) {<br>continue;<br>}<br>module = cf->cycle->modules[m]->ctx;<br>if (module->create_srv_conf) {<br>mconf = module->create_srv_conf(cf);<br>if (mconf == NULL) {<br>return NGX_CONF_ERROR;<br>}<br>// srv_conf 数组包含了所有模块的srv级别的配置<br>ctx->srv_conf[cf->cycle->modules[m]->ctx_index] = mconf;<br>}<br>if (module->create_loc_conf) {<br>mconf = module->create_loc_conf(cf);<br>if (mconf == NULL) {<br>return NGX_CONF_ERROR;<br>}<br>// loc_conf 数组包含了所有模块的loc级别的配置<br>ctx->loc_conf[cf->cycle->modules[m]->ctx_index] = mconf;<br>}<br>}<br>uscf->servers = ngx_array_create(cf->pool, 4,<br>sizeof(ngx_http_upstream_server_t));<br>if (uscf->servers == NULL) {<br>return NGX_CONF_ERROR;<br>}<br>/* parse inside upstream{} */<br>pcf = *cf;<br>cf->ctx = ctx;<br>cf->cmd_type = NGX_HTTP_UPS_CONF;<br>// 解析NGX_HTTP_UPS_CONF级别的配置<br>// 该级别的配置指令有server,配置上游服务器地址的。有ip_hash 配置负载均衡策略的。<br>rv = ngx_conf_parse(cf, NULL);<br>*cf = pcf;<br>if (rv != NGX_CONF_OK) {<br>return rv;<br>}<br>if (uscf->servers->nelts == 0) {<br>ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,<br>"no servers are inside upstream");<br>return NGX_CONF_ERROR;<br>}<br>return rv;<br>}<br>
开始解析NGX_HTTP_UPS_CONF级别的server指令,对应的函数ngx_http_upstream_server。
<br>static char *<br>ngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)<br>{<br>ngx_http_upstream_srv_conf_t *uscf = conf;<br>time_t fail_timeout;<br>ngx_str_t *value, s;<br>ngx_url_t u;<br>ngx_int_t weight, max_conns, max_fails;<br>ngx_uint_t i;<br>ngx_http_upstream_server_t *us;<br>us = ngx_array_push(uscf->servers);<br>if (us == NULL) {<br>return NGX_CONF_ERROR;<br>}<br>ngx_memzero(us, sizeof(ngx_http_upstream_server_t));<br>value = cf->args->elts;<br>weight = 1;<br>max_conns = 0;<br>max_fails = 1;<br>fail_timeout = 10;<br>for (i = 2; i < cf->args->nelts; i++) {<br>if (ngx_strncmp(value[i].data, "weight=", 7) == 0) {<br>if (!(uscf->flags & NGX_HTTP_UPSTREAM_WEIGHT)) {<br>goto not_supported;<br>}<br>weight = ngx_atoi(&value[i].data[7], value[i].len - 7);<br>if (weight == NGX_ERROR \|\| weight == 0) {<br>goto invalid;<br>}<br>continue;<br>}<br>if (ngx_strncmp(value[i].data, "max_conns=", 10) == 0) {<br>if (!(uscf->flags & NGX_HTTP_UPSTREAM_MAX_CONNS)) {<br>goto not_supported;<br>}<br>max_conns = ngx_atoi(&value[i].data[10], value[i].len - 10);<br>if (max_conns == NGX_ERROR) {<br>goto invalid;<br>}<br>continue;<br>}<br>if (ngx_strncmp(value[i].data, "max_fails=", 10) == 0) {<br>if (!(uscf->flags & NGX_HTTP_UPSTREAM_MAX_FAILS)) {<br>goto not_supported;<br>}<br>max_fails = ngx_atoi(&value[i].data[10], value[i].len - 10);<br>if (max_fails == NGX_ERROR) {<br>goto invalid;<br>}<br>continue;<br>}<br>if (ngx_strncmp(value[i].data, "fail_timeout=", 13) == 0) {<br>if (!(uscf->flags & NGX_HTTP_UPSTREAM_FAIL_TIMEOUT)) {<br>goto not_supported;<br>}<br>s.len = value[i].len - 13;<br>s.data = &value[i].data[13];<br>fail_timeout = ngx_parse_time(&s, 1);<br>if (fail_timeout == (time_t) NGX_ERROR) {<br>goto invalid;<br>}<br>continue;<br>}<br>if (ngx_strcmp(value[i].data, "backup") == 0) {<br>if (!(uscf->flags & NGX_HTTP_UPSTREAM_BACKUP)) {<br>goto not_supported;<br>}<br>us->backup = 1;<br>continue;<br>}<br>if (ngx_strcmp(value[i].data, "down") == 0) {<br>if (!(uscf->flags & NGX_HTTP_UPSTREAM_DOWN)) {<br>goto not_supported;<br>}<br>us->down = 1;<br>continue;<br>}<br>goto invalid;<br>}<br>ngx_memzero(&u, sizeof(ngx_url_t));<br>u.url = value[1];<br>u.default_port = 80;<br>if (ngx_parse_url(cf->pool, &u) != NGX_OK) {<br>if (u.err) {<br>ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,<br>"%s in upstream \"%V\"", u.err, &u.url);<br>}<br>return NGX_CONF_ERROR;<br>}<br>us->name = u.url;<br>us->addrs = u.addrs;<br>us->naddrs = u.naddrs;<br>us->weight = weight;<br>us->max_conns = max_conns;<br>us->max_fails = max_fails;<br>us->fail_timeout = fail_timeout;<br>return NGX_CONF_OK;<br>invalid:<br>ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,<br>"invalid parameter \"%V\"", &value[i]);<br>return NGX_CONF_ERROR;<br>not_supported:<br>ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,<br>"balancing method does not support parameter \"%V\"",<br>&value[i]);<br>return NGX_CONF_ERROR;<br>}<br>

转载请注明:爱开源 » nginx的http模块的upstream机制

转载请注明:爱开源 » nginx的http模块的upstream机制