博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Nginx域名访问处理过程
阅读量:5796 次
发布时间:2019-06-18

本文共 6124 字,大约阅读时间需要 20 分钟。

  hot3.png

基于域名的虚拟服务器(server)

在实际应用中,我们可以将多个域名指向一个IP 地址,或者使用范IP解析功能。当多个域名执行一个 IP 地址时,Nginx 可以根据域名来分配不同的虚拟服务器,如下面的例子。定义了三个虚拟服务器同时监听80端口:

http {    #同时监听80端口的三个虚拟服务器    server {        listen      80;        server_name example.org www.example.org;    }    server {        listen      80;        server_name example.net www.example.net;    }    server {        listen      80;        server_name example.com www.example.com;    }}

这个时候,Nginx 会根据访问头(request head)中Host 的数据来确定使用哪个server来处理当前请求。如果请求没有匹配任何 server,或者访问头(request head)中没有包含Host的数据,那么 Nginx 会将该请求路由给默认的 server,默认情况下就是配置文件中的第一个 server

可以通过在 listen 指令中增加 default_server 参数来指定默认的 server

server {    #指定当前server为默认的server    listen      80 default_server;    server_name example.net www.example.net;}

我们还可以在一个 listen 指令下配置多个端口的监听:

#server {    listen       80;    listen       8080  default_server;    server_name  example.net;}#server {    listen       80  default_server;    listen       8080;    server_name  example.org;}

避免请求中没有定义服务器名称的情况

如果服务器不允许没有 request head 中没有 host 数据的请求,那么可以增加一个如下配置的虚拟服务器:

server {    listen      80;    #空字符串匹配无HOST参数的情况    server_name "";     #返回444状态码    return      444;}

这时,在接收到一个无 host 数据的请求时会返回一个444的异常状态码表示拒绝该次请求的链接。

基于IP和域名的混合路由服务

Nginx 同样支持根据访问 IP 来选择 server 的情况,下面是一个混合处理 IP 以及域名的例子:

server {    #指定监听的域名以及端口    listen      192.168.1.1:80;    server_name example.org www.example.org;}server {    listen      192.168.1.1:80;    server_name example.net www.example.net;}server {    listen      192.168.1.2:80;    server_name example.com www.example.com;}

在这个配置下,Nginx 首先测试与 listen 指令 能够匹配的 IP 地址和端口,然后在能够匹配上 IP 和端口的条目下,再检查server_name是否匹配访问头(request head)的 host 参数。如果 server_name 无法匹配上,那么会使用“默认”的server来响应当前的请求——即第一个匹配上 IP 地址的 server。例如当前请求的 HOST 是 www.example.com 并发送给 192.168.1.1:80 地址,那么用来处理这个请求的是第一个 server,原因是域名和端口匹配上,但是 server_name 无法匹配,选用第一个能匹配的 server

在混合的规则下,可以在 listen 指令上为不同的地址端口定义多个默认的 server

server {    listen      192.168.1.1:80;    server_name example.org www.example.org;}server {    #定义默认虚拟服务    listen      192.168.1.1:80 default_server;    server_name example.net www.example.net;}server {    listen      192.168.1.2:80 default_server;    server_name example.com www.example.com;}

切记,default_server 参数只能用在 listen 指令上,也就是根据 IP 以及端口来指定默认的虚拟服务器。相同的IP以及端口可以设置一个默认虚拟服务器。

 范域名解析

前面介绍了根据域名( host 属性)路由 server 的情况,他们都使用 www.example.com 这样明确的字符串来定义域名。除此之外,还可以使用通配符以及正则表达式来设定 server_name 的匹配规则:

server {    listen       80;    #固定字符串    server_name  example.org  www.example.org;}server {    listen       80;    #子域名的范域名解析    server_name  *.example.org;}server {    listen       80;    #主域名的范域名解析    server_name  mail.*;}server {    listen       80;    #正则表达式解析    server_name  ~^(?
.+)\.example\.net$;}

通过各种规则来解析域名我们称之为“范域名解析”

我们可以在域名服务商那里设定范域名解析的规则。通常情况下是在主域名的之前使用通配符*来指定所有的二级域名指向同一个地址,例如 *.example.com。范域名解析有很强的应用场景,例如动态生成二级域名或多级域名等等。

在上面的这个配置设定下,一个请求如果能够同时匹配多个 server_name 的规则(例如同时匹配上一个通配符和一个正则表达式),Nginx 会使用顺序靠前的匹配 server 来处理该请求。下面是匹配的优先级:

  1. 固定的字符串(无通配符、非正则表达式)。
  2. 通配符的位置出现在字符串的起始位置,例如 *.example.org。多个匹配使用长度优先原则。
  3. 通配符的位置出现在字符串的末尾位置,例如 mail.*。多个匹配使用长度优先原则。
  4. 最先匹配的正则表达式(次序按照server在文档中出现先后位置确定)。

通配符规则

一个星号(*)表示一个通配符,他表示匹配一个或多个URL允许使用的字符的组合。通配符只能出现在字符串的开头和末尾,并且只能用点号(.)与其他字符串分割。

例如下面这样就是正确的通配符书写方式:

  1. *.example.org
  2.  mail.*

而下面这样的书写方式是错误的:

  1. www.*.example.org 。
  2. w*.example.org

一个星号可以匹配多个URL字符,所以 *.example.org 即可匹配 www.example.org 也可以匹配 www.sub.example.org。一个特殊的情况是 .example.org 这样的域名,即可匹配 example.org 这样固定的字符串,也可匹配 *.example.org 这样的通配符。

正则表达式规则

正则表达式必须以(~)符号开头:

#正则表达式server_name  ~^www\d+\.example\.net$;

否则 Nginx 会认为这是一个固定的字符串或通配符字符串。

在使用正则表达式时,通常会以 开头以 $ 结尾,虽然正则语法上并不要求一定要使用这2个符号,但是会大大提升解析效率。另外还要注意的是,由于点符号(.)是正则表达式的一个关键字,所以域名中的点需要使用反斜线来转意(\.)。

如果在正则表达式中需要使用大括号( "{" 和 "}" ),因为大括号是 Nginx 块符号,所以使用时需要用双引号将正则表达式引用起来:

server_name  "~^(?
\w\d{1,3}+)\.example\.net$";

否则启动时会输出异常。

使用正则表达式还支持变量传递,例如:

server {    #
表示一个变量 server_name ~^(www\.)?(?
.+)$; location / { #使用$domain获取变量值映射到指定的磁盘路径 root /sites/$domain; }}

Nginx 使用的正则表达式通过 Perl 来解析(PCRE)。不同版本的 perlPCRE)对正则表达式获取变量的语法有略微的差异。通常情况下现在安装的操作系统都支持最新的语法规则。如果在启动Nginx时日志出现:

pcre_compile() failed: unrecognized character after (?< in ...

这样的内容,表示当前的PCRE版本较低,需要用旧的表达式。

Server_name更多的规则说明

可以在一个 server 模块中一次指定多个匹配字符串、通配符以及正则表达式:

server {    listen       80;    server_name  example.org  www.example.org  "";}

在上面的例子中,"" 用来处理请求头中(request head)中不包含 Host 参数的情况。如果 server 块没有指定 server_name 参数。那么当前的 server 默认使用空字符串作为虚拟注意的 server_name

如果将 server_name 的参数指定为 $hostname,那么会使用当前主机的 hostname

使用 server_name 也可以处理 IP 请求:

server {    listen       80;    server_name  example.org                 www.example.org                 ""                 192.168.1.1                 ;}

当客户端直接用 IP 访问时,对应的 server_name 会处理。

基于server_name的性能优化

无论是固定的字符串,还是星号通配符以及正则表达式,所有的匹配规则都会根据 server 的监听端口创建一个哈希表(hash table)。这个哈希表在Nginx加载阶段进行了优化,以便在CPU运算时以最少的读写次数命中哈希值。

Nginx 在匹配一个请求时,固定字符串的哈希表是最先进行匹配的。如果没有固定的字符串匹配,那么会开始匹配以星号通配符开始的哈希表。未匹配上的话就继续匹配以通配符星号结尾的哈希表。

匹配通配符的过程肯定比匹配一个固定的哈希值的过程慢许多。需要特别注意的是:“.example.org”这样的字符串是被存储在通配符的哈希表中的,而不是固定字符串的hash表,所以不要出现这样的书写

如果固定哈希表和通配符哈希表都无法匹配得上,最后就会去匹配正则表达式,也也是最慢的。

因此,建议将一些经常会出现的域名以固定字符串的方式记录。例如外部的访问请求大量来源于域名 example.org 或 www.example.org,而有部分请求来源与其他二级域名,明确的将常用域名定义出来这可以得到不错的优化:

server {    listen       80;    server_name  example.org  www.example.org  *.example.org;}

这样配置指令比简写为:

server {    #    listen       80;    server_name  .example.org;}

性能会提升不少。

如果我们在某个主块内的上下文中(例如http)配置了大量的服务的名称(域名),或者单个服务器的名称非常长,建议调整上下文中 和   参数。默认情况下  的值是32、64或者其他值,这取决于服务器的 CPU 缓存大小。如果当前值为32,那么当出现”too.long.server.name.example.org“这样的域名匹配时,在启动的过程中会输出:

could not build the server_names_hash,you should increase server_names_hash_bucket_size: 32

这样的异常内容。这个时候需要增加对应的size。

http {    #增加名称哈希表大小    server_names_hash_bucket_size  64;}

如果 server 的名称太多,会输出:

could not build the server_names_hash,you should increase either server_names_hash_max_size: 512or server_names_hash_bucket_size: 32

这个时候可以调整对应的参数数值。不过还是建议不要出现这样的情况,因为将相关的数值调整后会影响单次读写缓存的大小,造成命中数值所需的读写次数增加。

最后,如果在一个 server 中只有一个 server_name 的指令配置,Nginx 仅仅会考虑 listen 命中而不会去判断域名是否命中。此时可以使用任意一个字符串来表达 server_name 只有标记意义,没有实际意义。

转载于:https://my.oschina.net/chkui/blog/1793581

你可能感兴趣的文章
JAVA中循环删除list中元素的方法总结
查看>>
ChPlayer播放器的使用
查看>>
js 经过修改改良的全浏览器支持的软键盘,随机排列
查看>>
第十六章:脚本化HTTP
查看>>
L104
查看>>
被遗忘的CSS
查看>>
做完小程序项目、老板给我加了6k薪资~
查看>>
脱离“体验”和“安全”谈盈利的游戏运营 都是耍流氓
查看>>
ELK实战之logstash部署及基本语法
查看>>
TortoiseSVN中图标的含义
查看>>
Python version 2.7 required, which was not foun...
查看>>
[BZOJ] 1012 [JSOI2008]最大数maxnumber
查看>>
根据毫秒数计算出当前的“年/月/日/时/分/秒/星期”并不是件容易的事
查看>>
华为硬件工程师笔试题
查看>>
jquery居中窗口-页面加载直接居中
查看>>
Unity Shaders and Effects Cookbook (3-5) 金属软高光
查看>>
31-hadoop-hbase-mapreduce操作hbase
查看>>
NYOJ283对称排序
查看>>
C#反射实例应用--------获取程序集信息和通过类名创建类实例
查看>>
VC中实现文字竖排的简单方法
查看>>