迹忆客 专注技术分享

当前位置:主页 > 学无止境 > 网络 >

深入理解 Nginx 的 Server 块选择算法

作者:迹忆客 最近更新:2022/01/13 浏览次数:

Nginx 是世界上最受欢迎的 Web 服务器之一。 它可以成功处理具有许多并发客户端连接的高负载,并且可以用作 Web 服务器、邮件服务器或反向代理服务器。

在本篇文章中,我们将讨论一些决定 Nginx 处理客户端请求的细节。 了解这些可以帮助我们在设计 Server 和 Location 时更加得心应手,对于一些请求的现象不至于迷惑。

Nginx 块配置

Nginx 在逻辑上将用于提供不同内容的配置划分为块,这些块存在于层次结构中。每次发出客户端请求时,Nginx 都会开始确定应该使用哪些配置块来处理请求。这个决策过程就是我们接下来要讨论的内容。

我们将讨论的主要块是 Server 块和 Location 块。

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

Location 块位于 Server 块中,用于定义 Nginx 应如何处理不同资源和 URI 的请求。可以使用这些块以管理员喜欢的任何方式细分 URI 空间。这是一个非常灵活的模型。

Nginx 如何决定使用哪个 Server 块处理请求

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

它通过一个定义好的检查系统来做到这一点,该系统主要用来找到一个最佳匹配。 Nginx 在此过程中关注的主要 Server 块指令是 listen 指令和 server_name 指令。

解析 listen 指令,查找可能的匹配项

首先,Nginx 查看请求的 IP 地址和端口。 然后将其与每个 Server 的监听指令相匹配,来构建一个可能解析请求的 Server 块列表。

listen 指令通常定义 Server 块将响应的 IP 地址和端口。 如果没有定义 listen 指令,则使用默认值 0.0.0.0:80 (如果Nginx由普通的非root用户运行,则为0.0.0.0:8080)。 这允许这些块响应对端口 80 的任何请求,但此默认值在Server块的选择过程中没有太大的权重。

listen 指令可以设置为:

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

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

当尝试确定将请求发送到哪个 Server 块时,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 地址和端口收集与请求最匹配的 Server 块列表。 这意味着如果存在列出特定 IP 地址的匹配块,则不会选择在功能上使用 0.0.0.0 作为其 IP 地址(以匹配任何接口)的任何块。 无论如何,端口必须完全匹配。
  • 如果只有一个最具体的匹配项,则该 Server 块将用于为请求提供服务。 如果有多个 Server 块具有相同级别的匹配,Nginx 然后开始检测每个 Server 块的 server_name 指令。

重要的是要了解 Nginx 仅在使用 listen 指令不能区分 Server 块时才会去检测 server_name 指令。 例如,如果 example.com 托管在 192.168.1.10 的端口 80 上,那么在本示例中,对 example.com 的请求将始终由第一个块提供服务,尽管第二个块中有 server_name 指令。

server {
    listen 192.168.1.10;

    . . .

}

server {
    listen 80;
    server_name example.com;

    . . .

}

解析 server_name 指令,选择匹配项

接下来,为了进一步检测具有相同特定 listen 指令的请求,Nginx 会检查请求的 Host 标头。 此值包含客户端实际尝试访问的域或 IP 地址。

nginx 接下来在通过 listen 指令选择出来的 Server 块中匹配 server_name 指令,来匹配最终的块。Nginx 使用以下公式进行匹配:

  • Nginx 将首先尝试找到一个 server_name 与请求的 Host 标头中的值完全匹配的服务器块。如果找到,关联的块将用于服务请求。如果找到多个完全匹配,则使用第一个。
  • 如果没有完全匹配,Nginx 将尝试使用前导通配符(由配置中名称开头的 * 表示)匹配 server_name 的 Server 块。如果找到,该块将用于服务请求。如果找到多个匹配项,将使用最长的匹配项来处理请求。
  • 如果使用前导通配符未找到匹配项,Nginx 然后查找具有使用尾随通配符匹配的 server_name 的服务器块(由配置中以 *结尾的服务器名称表示)。如果找到,则使用该块来服务请求。如果找到多个匹配项,将使用最长的匹配项来处理请求。
  • 如果使用尾随通配符没有找到匹配项,Nginx 然后匹配使用正则表达式定义 server_name 的服务器块(由名称前的 ~ 表示)。第一个带有与“Host”标头匹配的正则表达式的 server_name 将用于为请求提供服务。
  • 如果没有找到正则表达式匹配,Nginx 然后为该 IP 地址和端口选择默认 Server 块。

每个 IP 地址/端口 组合都有一个默认 Server 块,当无法使用上述方法确定最终匹配时,将使用该块。 对于 IP地址/端口组合,这将是配置中的第一个块,或者是包含 default_server 选项作为 listen 指令一部分的块(这将覆盖 first-found 算法)。 每个 IP 地址/端口组合只能有一个 default_server 声明。

如果定义了与 Host 标头值完全匹配的 server_name,则选择该 Server 块来处理请求。

在下面示例中,如果请求的 Host 标头设置为 host1.example.com,则将选择第二个 Server 块:

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

    . . .

}

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

    . . .

}

如果没有找到完全匹配,Nginx 然后检查是否有一个 server_name 带有一个适合的起始通配符。 将选择以通配符开头的最长匹配来满足请求。

在此示例中,如果请求的 Host 标头为 www.example.org,则将选择第二个 Server 块:

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

    . . .

}

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

    . . .

}

server {
    listen 80;
    server_name *.org;

    . . .

}

如果没有找到使用通配符的匹配项,Nginx 将在表达式末尾使用通配符查看是否存在匹配项。 此时,将选择以通配符结尾的最长匹配项来处理请求。

例如,如果请求的 Host 标头设置为 www.example.com,则将选择第三个 Server 块:

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 指令。 将选择第一个匹配的正则表达式来响应请求。

例如,如果请求的 Host 标头设置为 www.example.com,则将选择第二个 Server 块来满足请求:

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 地址和端口的默认 Server。

除非注明转载,本站文章均为原创或翻译,欢迎转载,转载请以链接形式注明出处

本文地址:

迹忆客

专注技术分享,项目实战分享!

技术宅 乐于分享 7年编程经验
社交账号
  • https://www.github.com/onmpw
  • qq:1244347461

热门文章

教程更新

热门标签