阅读 52

nginx对listen端口的优化 -- 第一篇

微信公众号:郑尔多斯
关注可了解更多的Nginx知识。任何问题或建议,请公众号留言;
关注公众号,有趣有内涵的文章第一时间送达!

listen端口优化

本篇文章主要是分析配置文件解析完毕之后对listen的进一步优化。这一部分主要完成了端口的初始化以及打开操作。

代码分析

ngx_http_block()中有下面的一部分代码,如下:

1 /* optimize the lists of ports, addresses and server names */
2
3if (ngx_http_optimize_servers(cf, cmcf, cmcf->ports) != NGX_OK) {
4    return NGX_CONF_ERROR;
5}
复制代码

从上面的代码注释中可以看出来,ngx_http_optimize_servers()函数的作用就是对我们监听的端口,address以及server name进行优化处理的,下面我们结合自己的例子分析一下这个方法。
先把我们解析完http指令之后的内存结构图放出来:

解析http之后的内存图
解析http之后的内存图

ngx_http_optimize_servers

先看一下源码,如下:

 1static ngx_int_t
2ngx_http_optimize_servers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf,  ngx_array_t *ports)
3
{
4    ngx_uint_t             p, a;
5    ngx_http_conf_port_t  *port;
6    ngx_http_conf_addr_t  *addr;
7
8    if (ports == NULL) {
9        return NGX_OK;
10    }
11
12    port = ports->elts;
13//遍历所有的ports数组
14    for (p = 0; p < ports->nelts; p++) {
15// 对每个port下的所有addr进行排序
16        ngx_sort(port[p].addrs.elts, (size_t) port[p].addrs.nelts,
17            sizeof(ngx_http_conf_addr_t), ngx_http_cmp_conf_addrs);
18
19        /*
20         * check whether all name-based servers have the same
21         * configuration as a default server for given address:port
22         */

23
24        addr = port[p].addrs.elts;
25        for (a = 0; a < port[p].addrs.nelts; a++) {
26
27            if (addr[a].servers.nelts > 1
28#if (NGX_PCRE)
29                || addr[a].default_server->captures
30#endif
31               )
32            {
33                if (ngx_http_server_names(cf, cmcf, &addr[a]) != NGX_OK) {
34                    return NGX_ERROR;
35                }
36            }
37        }
38
39        if (ngx_http_init_listening(cf, &port[p]) != NGX_OK) {
40            return NGX_ERROR;
41        }
42    }
43
44    return NGX_OK;
45}
复制代码

对addr进行排序

从上面的代码中可以看到,nginx会遍历所有的port,然后对每个遍历到的port调用ngx_sort()进行排序,使用的排序函数为ngx_http_cmp_conf_addrs(),该函数如下:

 1ngx_http_cmp_conf_addrs(const void *one, const void *two)
2{
3    ngx_http_conf_addr_t  *first, *second;
4
5    first = (ngx_http_conf_addr_t *) one;
6    second = (ngx_http_conf_addr_t *) two;
7
8    if (first->opt.wildcard) {
9        /* a wildcard address must be the last resort, shift it to the end */
10        return 1;
11    }
12
13    if (second->opt.wildcard) {
14        /* a wildcard address must be the last resort, shift it to the end */
15        return -1;
16    }
17
18    if (first->opt.bind && !second->opt.bind) {
19        /* shift explicit bind()ed addresses to the start */
20        return -1;
21    }
22
23    if (!first->opt.bind && second->opt.bind) {
24        /* shift explicit bind()ed addresses to the start */
25        return 1;
26    }
27
28    /* do not sort by default */
29
30    return 0;
31}
复制代码

从代码中可以看到排序规则:

  • 如果first addr是wildcard,那么这个addr排在后面second的后面,这一句就说明wildcard 属性要排在所有的地址的后面。
  • 如果first addr不是wildcard,但是second 是wildcard,那么first排在second的前面,也就是说wildcard类型的要排在 非wildcard后面
  • 如果 first和second 都不是wildcard,但是first有bind属性,而second没有bind属性,那么first排在second的前面,这个规则说明bind属性要排在非bind的前面。
  • 如果first和second都不是wildcard,但是first没有bind属性,而second有bind属性,那么frist排在second的后面,也是为了说明bind要排在前面。
  • 其他情况的排序规则不变
排序之后的样式
排序之后的样式

上图是排序之后的样式,不过由于我们只有一个addr,所以这里并没有这么麻烦。

ngx_http_init_listening

我们真正要关心的是这个函数,它对我么监听的端口进行了处理:

 1static ngx_int_t
2ngx_http_init_listening(ngx_conf_t *cf, ngx_http_conf_port_t *port)
3
{
4    ngx_uint_t                 i, last, bind_wildcard;
5    ngx_listening_t           *ls;
6    ngx_http_port_t           *hport;
7    ngx_http_conf_addr_t      *addr;
8
9    addr = port->addrs.elts;
10// 在本例子中last = 1
11    last = port->addrs.nelts;
12
13    /*
14     * If there is a binding to an "*:port" then we need to bind() to
15     * the "*:port" only and ignore other implicit bindings.  The bindings
16     * have been already sorted: explicit bindings are on the start, then
17     * implicit bindings go, and wildcard binding is in the end.
18     */

19
20    if (addr[last - 1].opt.wildcard) {
21        addr[last - 1].opt.bind = 1;
22        bind_wildcard = 1;
23
24    } else {
25        bind_wildcard = 0;
26    }
27
28    i = 0;
29
30    while (i < last) {
31
32        if (bind_wildcard && !addr[i].opt.bind) {
33            i++;
34            continue;
35        }
36
37        ls = ngx_http_add_listening(cf, &addr[i]);
38        if (ls == NULL) {
39            return NGX_ERROR;
40        }
41
42        hport = ngx_pcalloc(cf->pool, sizeof(ngx_http_port_t));
43        if (hport == NULL) {
44            return NGX_ERROR;
45        }
46
47        ls->servers = hport;
48
49        hport->naddrs = i + 1;
50
51        switch (ls->sockaddr->sa_family) {
52
53#if (NGX_HAVE_INET6)
54        case AF_INET6:
55            if (ngx_http_add_addrs6(cf, hport, addr) != NGX_OK) {
56                return NGX_ERROR;
57            }
58            break;
59#endif
60        default/* AF_INET */
61            if (ngx_http_add_addrs(cf, hport, addr) != NGX_OK) {
62                return NGX_ERROR;
63            }
64            break;
65        }
66
67        if (ngx_clone_listening(cf, ls) != NGX_OK) {
68            return NGX_ERROR;
69        }
70
71        addr++;
72        last--;
73    }
74
75    return NGX_OK;
76}
复制代码

预知后事如何,且听下文分解


喜欢本文的朋友们,欢迎长按下图关注订阅号郑尔多斯,更多精彩内容第一时间送达

郑尔多斯
郑尔多斯