nginx對listen埠的處理 -- 第二篇

鄭爾多斯發表於2018-12-06

微信公眾號:鄭爾多斯
關注可瞭解更多的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()就執行完畢了。


喜歡本文的朋友們,歡迎長按下圖關注訂閱號鄭爾多斯,更多精彩內容第一時間送達

鄭爾多斯
鄭爾多斯

相關文章