nginx对listen端口的处理 -- 第二篇

1,449 阅读8分钟

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

上集回顾

上集介绍到了ngx_http_init_listening(),本篇文章继续介绍该函数,该函数的主要功能就是创建listening的端口信息。

ngx_create_listening

先介绍这个方法,这个方法主要是创建ngx_listening_t结构体,这个结构体代表了一个监听端口的信息。

调用ngx_create_listening生成listening信息
调用ngx_create_listening生成listening信息

我们看一下ngx_create_listening源码,如下,我们删除了我们例子用不到的代码,比如错误判断,以及不会被执行的if-else分支等。

 1ngx_listening_t *
2ngx_create_listening(ngx_conf_t *cf, void *sockaddr, socklen_t socklen)
3{
4    size_t            len;
5    ngx_listening_t  *ls;
6    struct sockaddr  *sa;
7    u_char            text[NGX_SOCKADDR_STRLEN];
8    //向全局cycle的listening字段中增加元素,
9    // 该字段保存了所有的监听端口的信息
10    ls = ngx_array_push(&cf->cycle->listening);
11
12    ngx_memzero(ls, sizeof(ngx_listening_t));
13
14    sa = ngx_palloc(cf->pool, socklen);
15  // 结合上篇文章中的图片来看这部分内容
16    ngx_memcpy(sa, sockaddr, socklen);
17
18    ls->sockaddr = sa;
19    ls->socklen = socklen;
20
21    len = ngx_sock_ntop(sa, socklen, text, NGX_SOCKADDR_STRLEN, 1);
22    ls->addr_text.len = len;
23
24    switch (ls->sockaddr->sa_family) {
25
26    case AF_INET:
27        ls->addr_text_max_len = NGX_INET_ADDRSTRLEN;
28        break;
29    }
30
31    ls->addr_text.data = ngx_pnalloc(cf->pool, len);
32    if (ls->addr_text.data == NULL) {
33        return NULL;
34    }
35
36    ngx_memcpy(ls->addr_text.data, text, len);
37
38    ls->fd = (ngx_socket_t-1;
39    ls->type = SOCK_STREAM;
40
41    ls->backlog = NGX_LISTEN_BACKLOG;
42    ls->rcvbuf = -1;
43    ls->sndbuf = -1;
44
45#if (NGX_HAVE_SETFIB)
46    ls->setfib = -1;
47#endif
48
49#if (NGX_HAVE_TCP_FASTOPEN)
50    ls->fastopen = -1;
51#endif
52
53    return ls;
54}

经过ngx_create_listening()处理之后,返回一个ngx_listening_t结构体,然后会经过ngx_http_add_listening()处理,ngx_http_add_listening()非常简单,就是简单的赋值,经过ngx_http_add_listening()处理之后的内存布局如下:

ngx_listening_t结构体
ngx_listening_t结构体

ngx_http_init_listening

下面介绍一下ngx_http_init_listening()函数,顾名思义,这个函数就是初始化listeng结构体,源码如下,同样的,我们删除了对于我们现在的例子不会执行的代码:

 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 = port->addrs.nelts;
11
12    /*
13     * If there is a binding to an "*:port" then we need to bind() to
14     * the "*:port" only and ignore other implicit bindings.  The bindings
15     * have been already sorted: explicit bindings are on the start, then
16     * implicit bindings go, and wildcard binding is in the end.
17     */

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

从代码中可以看出来,这个函数的执行步骤如下:

  • 调用ngx_http_add_listening()生成一个监听listening结构体
  • 调用ngx_http_add_addrs()来初始化ngx_http_port结构体
  • 调用ngx_clone_listening()来完成其他工作

ngx_http_add_addrs()

源码如下:

 1static ngx_int_t
2ngx_http_add_addrs(ngx_conf_t *cf, ngx_http_port_t *hport,
3    ngx_http_conf_addr_t *addr)

4
{
5    ngx_uint_t                 i;
6    ngx_http_in_addr_t        *addrs;
7    struct sockaddr_in        *sin;
8    ngx_http_virtual_names_t  *vn;
9
10    hport->addrs = ngx_pcalloc(cf->pool,
11                               hport->naddrs * sizeof(ngx_http_in_addr_t));
12    if (hport->addrs == NULL) {
13        return NGX_ERROR;
14    }
15
16    addrs = hport->addrs;
17
18    for (i = 0; i < hport->naddrs; i++) {
19
20        sin = &addr[i].opt.sockaddr.sockaddr_in;
21        addrs[i].addr = sin->sin_addr.s_addr;
22        addrs[i].conf.default_server = addr[i].default_server;
23#if (NGX_HTTP_SSL)
24        addrs[i].conf.ssl = addr[i].opt.ssl;
25#endif
26#if (NGX_HTTP_V2)
27        addrs[i].conf.http2 = addr[i].opt.http2;
28#endif
29        addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
30
31        if (addr[i].hash.buckets == NULL
32            && (addr[i].wc_head == NULL
33                || addr[i].wc_head->hash.buckets == NULL)
34            && (addr[i].wc_tail == NULL
35                || addr[i].wc_tail->hash.buckets == NULL)
36#if (NGX_PCRE)
37            && addr[i].nregex == 0
38#endif
39            )
40        {
41            continue;
42        }
43
44        vn = ngx_palloc(cf->pool, sizeof(ngx_http_virtual_names_t));
45        if (vn == NULL) {
46            return NGX_ERROR;
47        }
48
49        addrs[i].conf.virtual_names = vn;
50
51        vn->names.hash = addr[i].hash;
52        vn->names.wc_head = addr[i].wc_head;
53        vn->names.wc_tail = addr[i].wc_tail;
54#if (NGX_PCRE)
55        vn->nregex = addr[i].nregex;
56        vn->regex = addr[i].regex;
57#endif
58    }
59
60    return NGX_OK;
61}


经过上面的代码之后,内存布局如下:

创建的ngx_listening_t结构
创建的ngx_listening_t结构

下面的 ngx_clone_listening()函数并没有执行,所以最终形成的listening结构体就是这样的。到这里ngx_http_optimize_servers()就执行完毕了。


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

郑尔多斯
郑尔多斯