NGINX源码之:listen和server_name命令与listening监听创建

在http块的server块解析中,通过解析listen和server_name命令配置,完成端口监听的初始化,虚拟主机配置关联,实现从host+port到虚拟主机的映射关系。在进入解析源码之前,先来看看server块集中配置:

server {
        listen       8081 default;//default作为默认虚拟主机配置,当不匹配其他servername时,默认使用该配映射;*:8081与8081等效
        server_name  localhost;
            root   html;
            index  index.html index.htm;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }


    server{
        listen localhost:8081;//localhost会被解析出两个ip,一个是ipv4:127.0.0.1;一个是ipv6 [::1]
        server_name www.a.com www.b.com *.c.com;
        location / {
            root   html;
            index  index.html index.htm;
        }
    }
    server{
        listen localhost:8081;//有两个相同的listen配置时,才会构建servername的hash表
        server_name www.test.com  *.t.com;
        location /test {
            root   html;
            index  index.html index.htm;
        }
    }

    server{
        listen 127.0.0.1:8082;
        server_name www.a.com .c.com ~^www\.d\..*$;//设置正则格式的servername
        location / {
            root   html;
            index  index.html index.htm;
        }
    }
    server{
        listen 127.0.0.1:8083 default_server;//default_server等同于default
        server_name www.a.com .c.com *.test.com;
        location / {
            root   html;
            index  index.html index.htm;
        }
    }

一、listen命令解析
在解析server块时,首先解析listen命令,这时进入ngx_http_core_listen()方法:
NGINX源码之:listen和server_name命令与listening监听创建_第1张图片
下面主要看下的ngx_parse_url()与ngx_http_add_listen()两个方法
1、ngx_parse_url()
NGINX源码之:listen和server_name命令与listening监听创建_第2张图片
这里重点关注ipv4的处理:ngx_parse_inet_url();

NGINX源码之:listen和server_name命令与listening监听创建_第3张图片
ngx_inet_resolve_host() 将配置的hostname通过操作系统hosts文件解析出对应的一个或多个ip,再分别每个ip调用ngx_inet_add_addr()
NGINX源码之:listen和server_name命令与listening监听创建_第4张图片
经过上面的URL解析,大概有两种结构,一种是单个ip的,一种是经过host解析出多个ip的:
单个ip配置的,如listen 8081

NGINX源码之:listen和server_name命令与listening监听创建_第5张图片
多个ip配置的,如listen localhost:8081:
NGINX源码之:listen和server_name命令与listening监听创建_第6张图片

2、ngx_http_add_listen()
NGINX源码之:listen和server_name命令与listening监听创建_第7张图片
ngx_http_add_listen方法完成的结构如下:
NGINX源码之:listen和server_name命令与listening监听创建_第8张图片
最终,在整个listen配置解析阶段,生成的结构如下:

NGINX源码之:listen和server_name命令与listening监听创建_第9张图片

在ports数组中,每个端口可能有两种port元素(ipv4的port元素,ipv6的port元素),每个port元素可能有多个addr,如8081,加入同时配置多个server块,每个server的listen配置分别为 8081、localhost:8081,ports会有两个8081的port元素,那么ipv4port元素中的addrs元素就有两个,分别为0.0.0.0:8081、127.0.0.1:8081;ipv6port元素的addrs数组中元素只有一个[::]:8081;
当有多个server块,同时配了相同的listen,如多个server块配置listen localhost:8081,那么127.0.0.1:8081、[::]:8081对应的ngx_http_conf_addr元素中的servers数组就会有多个元素。

在给sockaddr赋值sin_port时,需要将端口号转成网络字节序,网络字节序用的都是大端字节序。内存读取都是按字节读取,每次读取8位,将8081转换字节序:
8081二进制为:0001 1111 1001 0001;为小端字节序,即每8位从左往右高位到地位,而大端字节序,每8位从左往右是低位到高位;那么转换后为1001 0001 0001 1111,十进制为37141

二、server_name命令解析

NGINX源码之:listen和server_name命令与listening监听创建_第10张图片

server_name解析完成每个server_name配置关联对应的srv_conf,后续将对server_name优化构建hash表,可通过hash表查找每个server_name对应的srv_conf配置。

三、ngx_http_optimize_servers()虚拟主机优化
NGINX源码之:listen和server_name命令与listening监听创建_第11张图片
1、ngx_http_server_names()
方法将单个addr的多个server块配置中server_names配置建立server_name到server块配置srv_conf的映射,然后将多个server块所有的server_name的映射构建hash表。具体构建hash表的方法,参考我的另一篇博客ngx_hash,addr的hash指针指向构建的hash表。
仅以本例,构造的结构如下:
NGINX源码之:listen和server_name命令与listening监听创建_第12张图片

2、ngx_http_init_listening()
NGINX源码之:listen和server_name命令与listening监听创建_第13张图片
2.1、ngx_http_add_listening()
NGINX源码之:listen和server_name命令与listening监听创建_第14张图片
2.2、ngx_http_add_addrs()
NGINX源码之:listen和server_name命令与listening监听创建_第15张图片NGINX源码之:listen和server_name命令与listening监听创建_第16张图片
创建listening后的结构如图:
NGINX源码之:listen和server_name命令与listening监听创建_第17张图片在整个过程中,会有ngx_addr_t(addr)、ngx_http_conf_addr_t(addr)、ngx_http_in_addr_t(addr)三种类型的addr、分别在listen命令解析(ngx_parse_url)、添加port到ports数组、创建listening三个流程节点中使用。后一个addr通常需要前一个addr复制,需注意区分。

四、绑定地址与开启监听
在ngx_init_cycle()方法中,完成ngx_conf_parse()之后,会先将旧的cycle中的listening数组中的元素中可继承的部分拷贝到新的cycle的listening中,这部分操作主要是为了热重启平滑过渡,防止用户连接丢失用的,直接启动的Nginx旧的cycle中是没有listening数组元素的。这里还涉及到main()方法中调用ngx_add_inherited_sockets()获取对应已经存在sockets连接的文件描述符。平滑热重启后面有时间再研究,这里暂时不解读这方面的内容。
NGINX源码之:listen和server_name命令与listening监听创建_第18张图片
NGINX源码之:listen和server_name命令与listening监听创建_第19张图片
最后,通过ngx_open_listening_sockets()遍历cycle中listening数组的每一个元素调用ngx_socket()生成一个socket并占用一个文件描述符,调用系统函数bind()绑定ip:port,并通过调用系统函数listen()开启监听。此时通过netstat -anp|grep nginx可以查看到开启的监听:NGINX源码之:listen和server_name命令与listening监听创建_第20张图片
另外可查看对应的文件描述符:NGINX源码之:listen和server_name命令与listening监听创建_第21张图片

8081端口有ipv4和ipv6两种监听,同时,8081ipv4的addr中,原本还有127.0.0.1的,但是有通配配置的存在,只剩通配形式的监听,这些都符合第三章中的原理解读。

除此之外,通过ngx_configure_listening_sockets()完成socket 接收缓存、发送缓存、Keepalive等socketopt的设置。sockopt的设置可参考setsockopt()函数使用详解

你可能感兴趣的:(nginx源码学习分析,nginx,运维,linux)