在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()方法:
下面主要看下的ngx_parse_url()与ngx_http_add_listen()两个方法
1、ngx_parse_url()
这里重点关注ipv4的处理:ngx_parse_inet_url();
ngx_inet_resolve_host() 将配置的hostname通过操作系统hosts文件解析出对应的一个或多个ip,再分别每个ip调用ngx_inet_add_addr()
经过上面的URL解析,大概有两种结构,一种是单个ip的,一种是经过host解析出多个ip的:
单个ip配置的,如listen 8081:
多个ip配置的,如listen localhost:8081:
2、ngx_http_add_listen()
ngx_http_add_listen方法完成的结构如下:
最终,在整个listen配置解析阶段,生成的结构如下:
在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命令解析
server_name解析完成每个server_name配置关联对应的srv_conf,后续将对server_name优化构建hash表,可通过hash表查找每个server_name对应的srv_conf配置。
三、ngx_http_optimize_servers()虚拟主机优化
1、ngx_http_server_names()
方法将单个addr的多个server块配置中server_names配置建立server_name到server块配置srv_conf的映射,然后将多个server块所有的server_name的映射构建hash表。具体构建hash表的方法,参考我的另一篇博客ngx_hash,addr的hash指针指向构建的hash表。
仅以本例,构造的结构如下:
2、ngx_http_init_listening()
2.1、ngx_http_add_listening()
2.2、ngx_http_add_addrs()
创建listening后的结构如图:
在整个过程中,会有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复制,需注意区分。