Nginx的了解服务器和定位块选择算法

Nginx的是世界上最流行的Web服务器之一。在本指南中,我们将讨论如何Nginx的选择服务器和位置块将处理特定客户端的请求。我们将仔细到位的算法,以及你可以用它来修改选择过程中的指令和选项。

介绍

Nginx是世界上最流行的Web服务器之一。 它可以成功地处理高负载与许多并发客户端连接,并可以轻松地充当Web服务器,邮件服务器或反向代理服务器。

在本指南中,我们将讨论一些确定Nginx如何处理客户端请求的幕后细节。 了解这些想法可以帮助您在设计服务器和位置块时进行猜测,并且可以使请求处理看起来更不可预测。

Nginx模块配置

Nginx将用于服务不同内容的配置逻辑地划分为块,这些块存在于分层结构中。 每次做出客户端请求时,Nginx开始确定应该使用哪些配置块来处理请求的过程。 这个决策过程是我们将在本指南中讨论的。

我们将要讨论的主要模块是服务器块和块的位置

服务器块是Nginx配置的一个子集,定义用于处理定义类型的请求的虚拟服务器。 管理员通常配置多个服务器块,并根据请求的域名,端口和IP地址决定哪个块应处理哪个连接。

位置块驻留在服务器块中,用于定义Nginx应如何处理针对父服务器的不同资源和URI的请求。 URI空间可以被细分为管理员喜欢使用这些块的任何方式。 它是一个非常灵活的模型。

Nginx如何决定哪个服务器块将处理请求

由于Nginx允许管理员定义作为单独的虚拟web服务器实例的多个服务器块,它需要一个过程来确定这些服务器块中的哪些将被用于满足请求。

它通过一个用于找到最佳匹配的定义的检查系统来实现。 Nginx的是在此过程中涉及的主要服务器模块的指令是listen指令,和server_name指令。

解析“listen”指令以查找可能的匹配

首先,Nginx查看请求的IP地址和端口。 它匹配这对listen每个服务器的指令,为建立能尽可能解决请求的服务器块的列表。

listen指令通常定义了IP地址和端口,服务器块将响应。 默认情况下,不包括任何服务器块listen指令给出的参数听0.0.0.0:80 (或0.0.0.0:8080如果Nginx的是由一个正常的,非root用户运行)。 这允许这些块响应端口80上任何接口上的请求,但是这个默认值在服务器选择过程中不具有很大的重要性。

listen指令可以设置为:

  • IP地址/端口组合。
  • 一个单独的IP地址,然后将在默认端口80上侦听。
  • 一个单独的端口,将监听该端口上的每个接口。
  • Unix套接字的路径。

最后一个选项通常只在不同服务器之间传递请求时有影响。

当试图确定哪个服务器块发送一个请求到,Nginx的将首先尝试基于所述的特异性决定listen使用以下规则指令:

  • nginx的翻译所有“不完全” listen通过使每个块可通过其IP地址和端口来评估他们的默认值代缺失值的指令。 这些翻译的一些示例是:
    • 没有一个块listen指令,使用值0.0.0.0:80
    • 设置一个IP地址块111.111.111.111 ,没有端口将成为111.111.111.111:80
    • 设置为端口块8888没有的IP地址成为0.0.0.0:8888
  • Nginx然后尝试基于IP地址和端口最具体地收集与请求匹配的服务器块的列表。 这意味着,在功能上使用任何块0.0.0.0作为其IP地址(以匹配任何接口),如果有匹配的,列出的特定的IP地址块将不被选择。 在任何情况下,端口必须完全匹配。
  • 如果只有一个最具体的匹配,那么该服务器块将用于服务请求。 如果有与特异性匹配的同一级别上的多个服务器块,Nginx的再开始,以评估server_name的每个服务器块的指令。

要明白的Nginx只会评价是很重要的server_name时,它需要一个匹配服务器块之间进行区分,以在相同的水平的特异性的指令listen指令。 例如,如果example.com在端口托管80192.168.1.10 ,对于一个请求example.com将总是通过在本实施例的第一块,送达尽管server_name中第二块指令。

server {
    listen 192.168.1.10;

    . . .

}

server {
    listen 80;
    server_name example.com;

    . . .

}

在一个以上的服务器块具有相等的特异性相匹配的情况下,下一个步骤是检查server_name指令。

解析“server_name”指令以选择匹配

接下来,以进一步评估有同样的具体要求listen指令,Nginx的检查请求的“主机”标题。 此值保存客户端实际尝试访问的域或IP地址。

nginx的尝试找到它发现通过查看该值最佳匹配server_name中的每个是仍然候选者的服务器块指令。 Nginx使用以下公式评估这些:

  • Nginx的将首先尝试找到一个服务器块server_name ,该值在完全相同的要求的“主机”头相匹配。 如果找到,相关的块将用于服务请求。 如果找到多个完全匹配, 一个被使用。
  • 如果没有找到精确匹配,Nginx的将尝试找到一个服务器块server_name的匹配使用通配符领先(由指示*在名称在config开头)。 如果找到一个,该块将用于服务请求。 如果找到多个匹配, 最长匹配将被用来服务请求。
  • 如果使用的是主导通配符发现不匹配,Nginx的然后查找与一个服务器块server_name进行匹配使用尾随通配符(通过与结尾的服务器名指示*在config)。 如果找到一个,则该块用于服务请求。 如果找到多个匹配, 最长匹配将被用来服务请求。
  • 如果使用通配符尾随发现不匹配,Nginx的再求定义服务器块server_name使用正则表达式(由指示~名称之前)。 第一个 server_name正则表达式的“主机”头匹配将被用来服务请求。
  • 如果没有找到正则表达式匹配,Nginx然后为该IP地址和端口选择默认服务器块。

每个IP地址/端口组合都有一个默认服务器块,当使用上述方法无法确定操作过程时,将使用该块。 一个IP地址/端口组合,这要么是在配置第一块或包含该块default_server选项作为一部分listen指令(这将重写第一发现算法)。 可以有只有一个default_server按每个IP地址/端口组合声明。

例子

如果有一个server_name定义完全“主机”头值相匹配时,该服务器块被选择来处理请求。

在此示例中,如果请求的“主机”标头设置为“host1.example.com”,则将选择第二个服务器:

server {
    listen 80;
    server_name *.example.com;

    . . .

}

server {
    listen 80;
    server_name host1.example.com;

    . . .

}

如果没有找到精确匹配,Nginx的再检查,看看是否有一个server_name与适合起始通配符。 将选择以通配符开头的最长匹配项以满足请求。

在本实施例中,如果请求了的“一”主机“头www.example.org ”,第二个服务器块将被选择:

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

    . . .

}

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

    . . .

}

server {
    listen 80;
    server_name *.org;

    . . .

}

如果找不到与起始通配符匹配,Nginx将使用表达式末尾的通配符查看是否存在匹配。 此时,将选择以通配符结尾的最长匹配项来提供请求。

例如,如果请求具有“主机”报头设置为“ www.example.com ”,第三服务器块将被选择:

server {
    listen 80;
    server_name host1.example.com;

    . . .

}

server {
    listen 80;
    server_name example.com;

    . . .

}

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

    . . .

}

如果无法找到匹配的通配符,Nginx的将然后转移到试图匹配server_name使用正则表达式指令。 第一个匹配的正则表达式将被选中的请求作出回应。

例如,如果“主机”的请求的首部设置为“ www.example.com ”,则第二服务器块将被选择,以满足该请求:

server {
    listen 80;
    server_name example.com;

    . . .

}

server {
    listen 80;
    server_name ~^(www|host1).*\.example\.com$;

    . . .

}

server {
    listen 80;
    server_name ~^(subdomain|set|www|host1).*\.example\.com$;

    . . .

}

如果没有任何上述步骤都能够满足该请求,则该请求将被传递给用于匹配的IP地址和端口的默认服务器。

匹配位置块

与Nginx用于选择将处理请求的服务器块的过程类似,Nginx还具有确定的算法,用于决定服务器中的哪个位置块用于处理请求。

位置块语法

在我们介绍Nginx如何决定使用哪个位置块来处理请求之前,让我们来看一下你在位置块定义中可能看到的一些语法。 位置块位于服务器块(或其他位置块)内,用于决定如何处理请求URI(域名或IP地址/端口之后的请求部分)。

位置块通常采取以下形式:

location optional_modifier location_match {

    . . .

}

location_match在上面定义了Nginx的应检查请求URI反对。 上面示例中修饰符的存在或不存在影响Nginx尝试匹配位置块的方式。 下面的修改将导致相关的位置块被解释如下:

  • (无):如果没有调节剂存在时,位置被解释为一个前缀匹配。 这意味着给定的位置将匹配请求URI的开始以确定匹配。
  • = :如果使用一个等号,该模块将被视为匹配,如果请求的URI给定的位置完全匹配。
  • ~ :如果一个波浪线修饰符,这个位置将被解释为区分大小写的正则表达式匹配。
  • ~* :如果使用了波浪线和星号修改,定位板将被解释为不区分大小写的正则表达式匹配。
  • ^~如果克拉和波浪修饰符,如果此块被选为最佳非正则表达式匹配,正则表达式匹配将不会发生。

演示位置块语法的示例

作为前缀匹配的一个例子,下面的位置的块可被选择为对于像URI请求响应/site/site/page1/index.html/site/index.html

location /site {

    . . .

}

对于确切的请求的URI匹配的演示,此块将始终用来为请求URI看起来像响应/page1 不会被用来向一个响应/page1/index.html请求URI。 请记住,如果选择此块,并且使用索引页完成请求,则内部重定向将发生到另一个位置,该位置将是请求的实际处理程序:

location = /page1 {

    . . .

}

作为应该被解释为,区分大小写的正则表达式的位置的一个例子,该块可用于处理对请求/tortoise.jpg ,但不适用于/FLOWER.PNG

location ~ \.(jpe?g|png|gif|ico)$ {

    . . .

}

将允许类似于上述的不区分大小写匹配的块如下所示。 在这里,无论/tortoise.jpg /FLOWER.PNG可以通过此块来处理:

location ~* \.(jpe?g|png|gif|ico)$ {

    . . .

}

最后,如果确定该块是最佳的非正则表达式匹配,则该块将阻止正则表达式匹配发生。 它可以处理的请求/costumes/ninja.html

location ^~ /costumes {

    . . .

}

如您所见,修饰符指示应如何解释位置块。 然而,这并没有告诉我们,Nginx的使用来决定哪些位置块发送请求的算法。 接下来我们将讨论。

Nginx如何选择使用哪个位置来处理请求

Nginx以类似于如何选择服务器块的方式选择将用于服务请求的位置。 它运行通过一个过程,确定任何给定请求的最佳位置块。 理解这个过程是能够可靠和准确地配置Nginx的关键要求。

记住我们上面描述的位置声明的类型,Nginx通过比较请求URI到每个位置来评估可能的位置上下文。 它使用以下算法执行此操作:

  • Nginx首先检查所有基于前缀的位置匹配(所有位置类型不涉及正则表达式)。 它根据完整的请求URI检查每个位置。
  • 首先,Nginx寻找一个完全匹配。 如果使用的是一个位置块=发现修改完全匹配的请求的URI,这个位置块被立即选择服务请求。
  • 如果没有确切的(与=修饰符)的位置块匹配被发现,Nginx的然后移动到评估非精确的前缀。 它发现给定请求URI的最长匹配前缀位置,然后它评估如下:
    • 如果最长匹配前缀的位置有^~修饰符,那么Nginx的将立即结束其搜索并选择该位置服务请求。
    • 如果最长匹配前缀位置使用^~改性剂,所述匹配的时刻,以便搜索的焦点可移动存储由Nginx的。
  • 在确定和存储最长匹配前缀位置之后,Nginx继续评估正则表达式位置(区分大小写和不区分大小写)。 如果有最长匹配前缀位置的任何正则表达式的位置,Nginx的将移动那些到其正则表达式的位置的列表的顶部检查。 Nginx然后尝试顺序地匹配正则表达式位置。 该请求URI匹配的第一个正则表达式的位置立即选择服务请求。
  • 如果没有找到与请求URI匹配的正则表达式位置,则选择先前存储的前缀位置来服务该请求。

重要的是要理解,默认情况下,Nginx将服务正则表达式匹配优先于前缀匹配。 然而,它首先计算前缀位置,从而允许辖通过指定使用位置以覆盖此倾向=^~改性剂。

还重要的是注意,尽管前缀位置通常基于最长的最特定匹配来选择,但是当找到第一匹配位置时,停止正则表达式求值。 这意味着在配置中的定位对正则表达式位置有很大的影响。

最后,要明白,正则表达式的最长前缀匹配内的匹配将“跳行”时,Nginx的正则表达式进行评估它的位置是很重要的。 在考虑任何其他正则表达式匹配之前,将按顺序对它们进行求值。 马克西姆Dounin,一个令人难以置信的有益Nginx的开发者,在解释这一职位的选择算法的这一部分。

什么时候进行位置块评估跳到其他位置?

一般来说,当选择一个位置块来服务一个请求时,该请求完全在该上下文中从该点向前处理。 只有选定的位置和继承的指令确定请求的处理方式,而不受兄弟位置块的干扰。

虽然这是一个通用规则,将允许您以可预测的方式设计位置块,但重要的是要意识到有时,新的位置搜索由所选位置内的某些指令触发。 “只有一个位置块”规则的例外可能会影响请求的实际提供方式,并可能与您在设计位置块时的期望不一致。

可能导致这种类型的内部重定向的一些指令是:

  • 指数
  • try_files
  • 改写
  • error_page

让我们略过这些。

index ,如果它是用来处理所述请求指令总是导致内部重定向。 精确位置匹配通常用于通过立即结束算法的执行来加速选择过程。 但是,如果你做一个精确的位置匹配的是一个目录 ,有一个很好的机会,请求将被重定向到一个不同的位置进行实际的处理。

在本实施例中,第一位置是由一个请求的URI相匹配/exact ,但为了处理请求,所述index由块继承指令启动一个内部重定向到第二块:

index index.html;

location = /exact {

    . . .

}

location / {

    . . .

}

在上面的情况下,如果你真的需要执行留在第一个块,你将不得不提出一种不同的方法来满足目录的请求。 例如,你可以设置一个无效的index为块,并打开autoindex

location = /exact {
    index nothing_will_match;
    autoindex on;
}

location  / {

    . . .

}

这是防止的一种途径index从上下文切换,但它可能不适合大多数配置非常有用。 大多数目录上的精确匹配对于重写请求(这也导致新的位置搜索)等事情有帮助。

其中,所述处理位置可以重新评估的另一个实例是与try_files指令。 这个指令告诉Nginx检查一个命名的文件或目录集的存在。 最后一个参数可以是Nginx将进行内部重定向的URI。

考虑以下配置:

root /var/www/main;
location / {
    try_files $uri $uri.html $uri/ /fallback/index.html;
}

location /fallback {
    root /var/www/another;
}

在上面的例子中,如果请求是对于由/blahblah ,所述第一位置将首先获得请求。 它会尝试找到一个名为blahblah/var/www/main目录。 如果不能找到一个,它会通过搜索一个名为跟进blahblah.html 然后,它会尝试看看是否有一个名为blahblah/的范围内/var/www/main目录。 失败的所有这些尝试,它会重定向到/fallback/index.html 这将触发另一个位置搜索,将被第二个位置块捕获。 这将有助于该文件/var/www/another/fallback/index.html

这可能会导致一个位置块冒充另一个指令是rewrite指令。 当使用last参数与rewrite指令,或使用任何参数时在所有,Nginx的将搜索基于所述重写的结果的新的匹配位置。

例如,如果我们修改最后的例子为包括重写,我们可以看到,该请求被有时直接传递到所述第二位置,而不依赖于try_files指令:

root /var/www/main;
location / {
    rewrite ^/rewriteme/(.*)$ /$1 last;
    try_files $uri $uri.html $uri/ /fallback/index.html;
}

location /fallback {
    root /var/www/another;
}

在上面的例子中,对于一个请求/rewriteme/hello将最初由第一位置块处理。 它将改写为/hello和位置将被搜索。 在这种情况下,将再次匹配第一位置,并通过该处理try_files照常,也许踢回/fallback/index.html如果没有找到(利用try_files我们上面讨论的内部重定向)。

但是,如果请求的是制作/rewriteme/fallback/hello ,首块将再次匹配。 重写再次应用,导致这段时间/fallback/hello 然后,将从第二位置块中提供该请求。

一个相关的情况将发生在return发送指令时, 301302状态码。 在这种情况下的区别是,它导致一个全新的请求以外部可见的重定向的形式。 这种相同的情况下可以用发生rewrite使用时,指令redirectpermanent标志。 但是,这些位置搜索不应该是意外的,因为外部可见的重定向总是产生一个新的请求。

error_page指令可导致类似于由创建内部重定向try_files 此伪指令用于定义在遇到某些状态代码时应该发生什么。 如果这将有可能永远不会被执行try_files设置,因为该指令处理请求的整个生命周期。

考虑这个例子:

root /var/www/main;

location / {
    error_page 404 /another/whoops.html;
}

location /another {
    root /var/www;
}

每一个请求(除了那些开始与其他/another )将第一个块,这将成为文件出来进行处理/var/www/main 但是,如果没有找到一个文件(404状态),内部重定向/another/whoops.html会发生,导致一个新的位置搜索将在第二块最终登陆。 这个文件将被提供出来/var/www/another/whoops.html

如你所见,理解Nginx触发新位置搜索的情况有助于预测在发出请求时您将看到的行为。

结论

理解Nginx处理客户端请求的方式可以使您作为管理员的工作更容易。 您将能够知道Nginx将根据每个客户端请求选择哪个服务器块。 您还可以根据请求URI了解如何选择位置块。 总的来说,知道Nginx选择不同块的方式将使您能够跟踪Nginx将应用的上下文以便为每个请求提供服务。