连接、会话、请求、日志#

连接处理机制#

Angie 支持多种连接处理方法。特定方法的可用性取决于所使用的平台。在支持多种方法的平台上,Angie 通常会自动选择最高效的方法。但是,如有必要,可以使用 use 指令显式选择连接处理方法。

以下是可用的连接处理方法:

方法

描述

select

标准方法。在没有更高效方法的平台上会自动构建支持模块。可以使用 --with-select_module--without-select_module 构建选项来强制启用或禁用此模块的构建。

poll

标准方法。在没有更高效方法的平台上会自动构建支持模块。可以使用 --with-poll_module--without-poll_module 构建选项来强制启用或禁用此模块的构建。

kqueue

高效方法,可用于 FreeBSD 4.1+、OpenBSD 2.9+、NetBSD 2.0 和 macOS。

epoll

高效方法,可用于 Linux 2.6+。

/dev/poll

高效方法,可用于 Solaris 7 11/99+、HP/UX 11.22+ (eventport)、IRIX 6.5.15+ 和 Tru64 UNIX 5.1A+。

eventport

event ports 方法可用于 Solaris 10+。(由于已知问题,建议改用 /dev/poll 方法。)

HTTP 请求处理#

HTTP 请求会经历一系列阶段,在每个阶段执行特定类型的处理。

Post-read

初始阶段。在此阶段调用 RealIP 模块。

Server-rewrite

处理 Rewrite 模块中定义在 server 块(但在 location 块之外)的指令的阶段。

Find-config

特殊阶段,根据请求 URI 选择 location

Rewrite

类似于 Server-rewrite 阶段,但它应用于在上一阶段选择的 location 块中定义的 rewrite 规则。

Post-rewrite

特殊阶段,如果在 Rewrite 阶段修改了请求的 URI,则将请求重定向到新的 location,如同 Find-config 阶段。

Preaccess

在此阶段,标准 Angie 模块(如 Limit Req)注册它们的处理程序。

Access

验证客户端是否有权发出请求的阶段,通常通过调用标准 Angie 模块(如 Auth Basic)来实现。

Post-access

特殊阶段,处理 satisfy any 指令。

Precontent

标准模块指令(如 try_filesmirror)在此阶段注册它们的处理程序。

Content

通常生成响应的阶段。多个标准 Angie 模块在此阶段注册它们的处理程序,包括 Indexproxy_passfastcgi_passuwsgi_passscgi_passgrpc_pass 指令也在此处理。

处理程序按顺序调用,直到其中一个产生输出。

Log

最后阶段,执行请求日志记录。目前只有 Log 模块在此阶段注册其处理程序以进行访问日志记录。

TCP/UDP 会话处理#

来自客户端的 TCP/UDP 会话会经历一系列阶段,在每个阶段执行特定类型的处理:

Post-accept

接受客户端连接后的初始阶段。在此阶段调用 RealIP 模块。

Pre-access

检查访问权限的预备阶段。在此阶段调用 Set 模块。

Access

在实际数据处理之前限制客户端访问的阶段。在此阶段调用 Access 模块。

SSL

发生 TLS/SSL 终止的阶段。在此阶段调用 SSL 模块。

Preread

将初始数据字节读入 预读缓冲区 的阶段,以允许模块(如 SSL Preread)在处理之前分析数据。

Content

实际处理数据的强制阶段,通常涉及 Return 模块向客户端发送响应。proxy_pass 指令也在此处理。

Log

记录客户端会话处理结果的最后阶段。在此阶段调用 Log 模块。

处理请求#

虚拟服务器选择#

最初,连接是在默认服务器的上下文中创建的。然后可以在请求处理的以下阶段确定服务器名称,每个阶段都参与服务器配置的选择:

  • 在 SSL 握手期间,根据 SNI 提前确定。

  • 处理请求行之后。

  • 处理 Host 头字段之后。

如果在处理请求行或 Host 头字段之后未确定服务器名称,Angie 将使用空名称作为服务器名称。

在这些阶段的每一个阶段,都可能应用不同的服务器配置。因此,某些指令应谨慎指定:

基于名称的虚拟服务器#

Angie 首先确定哪个服务器应该处理请求。考虑一个简单的配置,其中所有三个虚拟服务器都监听端口 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;
    #  ...
}

在此配置中,Angie 仅根据 Host 头字段确定哪个服务器应该处理请求。如果此头的值与任何服务器名称都不匹配,或者请求不包含此头字段,Angie 将把请求路由到此端口的默认服务器。在上面的配置中,默认服务器是第一个——这是 Angie 的标准默认行为。也可以使用 listen 指令中的 default_server 参数显式指定哪个服务器应该是默认服务器:

server {

    listen 80 default_server;
    server_name example.net www.example.net;
    #  ...
}

备注

请注意,默认服务器是监听套接字的属性,而不是服务器名称的属性。

国际化名称#

国际化域名(IDN)应在 server_name 指令中使用 ASCII(Punycode)表示形式指定:

server {

    listen 80;
    server_name xn--e1afmkfd.xn--80akhbyknj4f; # пример.испытание
    #    ...
}

阻止未定义服务器名称的请求#

如果不应允许没有 Host 头字段的请求,可以定义一个简单丢弃此类请求的服务器:

server {

    listen 80;
    server_name "";
    return 444;
}

在此配置中,服务器名称设置为空字符串,它匹配没有 Host 头字段的请求。然后返回一个特殊的非标准代码 444,该代码会关闭连接。

组合基于名称和基于 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;
    #  ...
}

在此配置中,Angie 首先根据 server 块的 listen 指令测试请求的 IP 地址和端口。然后,它根据匹配 IP 地址和端口的 server 块的 server_name 条目测试请求的 Host 头字段。如果未找到服务器名称,该请求将由默认服务器处理。例如,在端口 192.168.1.1:80 上接收到的对 www.example.com 的请求将由该端口的默认服务器处理——即第一个服务器——因为 www.example.com 未为此端口定义。

如前所述,默认服务器是监听端口的属性,可以为不同的端口定义不同的默认服务器:

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;
    #  ...
}

选择位置#

考虑一个简单的PHP网站配置:

server {

    listen 80;
    server_name example.org www.example.org;
    root /data/www;

    location / {

        index index.html index.php;
    }

    location ~* \.(gif|jpg|png)$ {

        expires 30d;
    }

    location ~ \.php$ {

        fastcgi_pass localhost:9000;
        fastcgi_param SCRIPT_FILENAME
        $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

Angie 首先搜索由字面字符串给出的最具体的前缀 location,而不考虑它们的列出顺序。在上面的配置中,唯一的前缀 location 是 location /,它匹配任何请求,并将作为最后的选择。然后 Angie 按照配置文件中出现的顺序检查由正则表达式定义的 location。第一个匹配的表达式会停止搜索,Angie 将使用该 location。如果没有正则表达式匹配请求,Angie 将使用之前找到的最具体的前缀 location

备注

所有类型的 location 仅测试请求行的 URI 部分,不包括参数。这是因为查询字符串中的参数可以以各种方式指定,例如:

  • /index.php?user=john&page=1

  • /index.php?page=1&user=john

此外,查询字符串可能包含任意数量的参数:

  • /index.php?page=1&something+else&user=john

现在让我们看看在上述配置中如何处理请求:

  • 请求 /logo.gif 首先由前缀 location / 匹配,然后由正则表达式 .(gif|jpg|png)$ 匹配。因此,它由后者 location 处理。使用指令 root /data/www,请求被映射到文件 /data/www/logo.gif,并将文件发送给客户端。

  • 请求 /index.php 也首先由前缀 location / 匹配,然后由正则表达式 .(php)$ 匹配。因此,它由后者 location 处理,请求被传递到监听 localhost:9000 的 FastCGI 服务器。fastcgi_param 指令将 FastCGI 参数 SCRIPT_FILENAME 设置为 /data/www/index.php,FastCGI 服务器执行该文件。变量 $document_root 设置为 root 指令的值,变量 $fastcgi_script_name 设置为请求 URI,即 /index.php

  • 请求 /about.html 仅由前缀 location / 匹配,因此在此 location 中处理。使用指令 root /data/www,请求被映射到文件 /data/www/about.html,并将文件发送给客户端。

处理请求 / 更为复杂。它仅由前缀 location / 匹配,因此由此 location 处理。然后 index 指令根据其参数和 root /data/www 指令测试索引文件是否存在。如果文件 /data/www/index.html 不存在但文件 /data/www/index.php 存在,该指令执行到 /index.php 的内部重定向,Angie 再次搜索 location,就像请求是由客户端发送的一样。如前所述,重定向的请求最终将由 FastCGI 服务器处理。

代理和负载均衡#

Angie 的一个常见用途是将其设置为代理服务器。在此角色中,Angie 接收请求,将它们转发到被代理的服务器,从这些服务器检索响应,并将响应发送回客户端。

一个简单的代理服务器:

server {

    location / {

        proxy_pass http://backend:8080;
    }

proxy_pass 指令指示 Angie 将客户端请求传递给后端 backend:8080 (被代理的服务器)。还有许多其他 指令 可用于进一步配置代理连接。

FastCGI 代理#

Angie 可用于将请求路由到运行使用各种框架和编程语言(如 PHP)构建的应用程序的 FastCGI 服务器。

与 FastCGI 服务器配合使用的最基本的 Angie 配置涉及使用 fastcgi_pass 指令代替 proxy_pass 指令,以及使用 fastcgi_param 指令设置传递给 FastCGI 服务器的参数。假设 FastCGI 服务器可在 localhost:9000 访问。在 PHP 中,SCRIPT_FILENAME 参数用于确定脚本名称,QUERY_STRING 参数用于传递请求参数。生成的配置如下:

server {

    location / {

        fastcgi_pass localhost:9000;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param QUERY_STRING $query_string;
    }

    location ~ \.(gif|jpg|png)$ {

        root /data/images;
    }
}

此配置设置了一个服务器,该服务器将所有请求(静态图像请求除外)通过 FastCGI 协议路由到在 localhost:9000 上运行的被代理服务器。

WebSocket 代理#

要将连接从 HTTP/1.1 升级到 WebSocket,需要使用 HTTP/1.1 中提供的`协议切换 <https://datatracker.ietf.org/doc/html/rfc2616#section-14.42>`_机制。

然而,这里有一个微妙之处:由于 Upgrade 头是一个`逐跳头 <https://datatracker.ietf.org/doc/html/rfc2616#section-13.5.1>`_,它不会从客户端传递到被代理的服务器。在正向代理中,客户端可以使用 CONNECT 方法来规避这个问题。但这种方法不适用于反向代理,因为客户端不知道任何代理服务器的存在,需要在代理服务器上进行特殊处理。

Angie 实现了一种特殊的操作模式,如果被代理的服务器返回代码为 101(切换协议)的响应,并且客户端通过请求中的 Upgrade 头请求协议切换,则允许在客户端和被代理服务器之间建立隧道。

如前所述,逐跳头(包括 UpgradeConnection)不会从客户端传递到被代理的服务器。因此,为了让被代理的服务器知道客户端切换到 WebSocket 协议的意图,必须显式传递这些头:

location /chat/ {

    proxy_pass http://backend;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

一个更复杂的示例演示了如何根据客户端请求头中是否存在 Upgrade 字段,来决定发送到被代理服务器的请求中 Connection 头字段的值:

http {

    map $http_upgrade $connection_upgrade {

        default upgrade;
        '' close;
    }

    server {

        ...

        location /chat/ {

            proxy_pass http://backend;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;
        }
    }
}

默认情况下,如果被代理的服务器在 60 秒内没有传输任何数据,连接将被关闭。可以使用 proxy_read_timeout 指令增加此超时时间。或者,可以将被代理的服务器配置为定期发送 WebSocket ping 帧以重置超时并检查连接是否仍然活动。

负载均衡#

在多个应用程序实例之间进行负载均衡是一种广泛使用的技术,用于优化资源利用率、最大化吞吐量、减少延迟并确保容错配置。

Angie 可以用作高效的 HTTP 负载均衡器,将流量分配到多个应用程序服务器,从而提高 Web 应用程序的性能、可扩展性和可靠性。

使用 Angie 进行负载均衡的最简单配置可能如下所示:

http {

    upstream myapp1 {

        server srv1.example.com;
        server srv2.example.com;
        server srv3.example.com;
    }

    server {

        listen 80;

        location / {

            proxy_pass http://myapp1;
        }
    }
}

在上面的示例中,同一应用程序的三个实例分别运行在 srv1srv3 上。当没有显式配置负载均衡方法时,默认使用轮询。其他支持的负载均衡机制包括:weightleast_connip_hash。Angie 中的反向代理实现还支持带内(或被动)服务器健康探测。这些探测使用 upstream 上下文中 server 块内的 max_failsfail_timeout 指令进行配置。

日志记录#

备注

除了此处列出的选项外, 您还可以启用 调试日志

Syslog#

error_logaccess_log 指令支持记录到 syslog。以下参数用于配置到 syslog 的日志记录:

server=address

指定 syslog 服务器的地址。地址可以是域名或 IP 地址(带可选端口),或在 "unix:" 前缀后指定的 UNIX 域套接字路径。如果未指定端口,则使用 UDP 端口 514。如果域名解析为多个 IP 地址,则使用第一个解析的地址。

facility=string

设置 syslog 消息的设施,如 RFC 3164 中定义。可能的设施包括:"kern""user""mail""daemon""auth""intern""lpr""news""uucp""clock""authpriv""ftp""ntp""audit""alert""cron""local0".."local7"。默认值为 "local7"

severity=string

定义 access_logsyslog 消息的严重性级别,如 RFC 3164 中指定。可能的值与 error_log 指令的第二个参数(级别)相同。默认值为 "info"。错误消息的严重性由 Angie 确定,因此在 error_log 指令中忽略此参数。

tag=string

设置 syslog 消息的标签。默认标签为 "angie"

nohostname

禁止在 syslog 消息头中添加 hostname 字段。

syslog 配置示例:

error_log syslog:server=192.168.1.1 debug;

access_log syslog:server=unix:/var/log/angie.sock,nohostname;
access_log syslog:server=[2001:db8::1]:12345,facility=local7,tag=angie,severity=info combined;

备注

为防止泛滥,Syslog 条目每秒最多报告一次。