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

连接处理机制#

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

事件端口 方法在Solaris 10+上可用。(由于已知问题,建议使用 /dev/poll 方法。)

HTTP请求处理#

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

Post-read

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

Server-rewrite

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

Find-config

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

Rewrite

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

Post-rewrite

一个特殊阶段,如果在 Rewrite 阶段中修改了URI,则请求被重定向到新位置,如在 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

将初始数据字节读入 preread buffer 的阶段,以允许模块如 SSL Preread 在处理前分析数据。

Content

一个必需阶段,数据在此实际处理,通常涉及 Return 模块以向客户端发送响应。在此也处理 proxy_pass 指令。

Log

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

处理请求#

虚拟服务器选择#

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

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

  • 在处理请求行之后。

  • 在处理 Host 头字段之后。

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

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

  • ssl_protocols 指令的情况下,协议列表由OpenSSL库在服务器配置根据SNI请求的名称应用之前设置。因此,协议应仅为默认服务器指定。

  • client_header_buffer_sizemerge_slashes 指令在读取请求行之前应用。因此,这些指令要么使用默认服务器配置,要么使用SNI选择的服务器配置。

  • 在处理请求头字段时,ignore_invalid_headerslarge_client_header_buffersunderscores_in_headers 指令的情况下,服务器配置还取决于它是否根据请求行或 Host 头字段进行了更新。

  • 错误响应使用当前处理请求的服务器中的 error_page 指令处理。

基于名称的虚拟服务器#

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

备注

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

国际化名称#

国际化域名(IDNs) 应在 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首先测试请求的IP地址和端口与 server 块的 listen 指令进行匹配。然后测试请求的 Host 头字段与匹配IP地址和端口的 server 块的 server_name 条目进行匹配。如果未找到服务器名称,请求将由默认服务器处理。例如,收到的请求 www.example.com 在端口192.168.1.1:80上将由该端口的默认服务器处理——即第一个服务器——因为 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 /,它匹配任何请求,并将作为最后的手段使用。然后,Angie按配置文件中出现的顺序检查由正则表达式定义的位置。第一个匹配的表达式停止搜索,Angie将使用该 location。如果没有正则表达式匹配请求,Angie将使用之前找到的最具体的前缀 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)$。因此,它由后者位置处理。使用指令 root /data/www,请求映射到文件 /data/www/logo.gif,并将文件发送给客户端。

  • 请求 /index.php 也首先匹配前缀 location /,然后匹配正则表达式 .(php)$。因此,它由后者位置处理,请求被传递到监听 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 /,因此在此位置处理。使用指令 root /data/www,请求映射到文件 /data/www/about.html,并将文件发送给客户端。

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

代理和负载均衡#

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

一个简单的代理服务器:

server {

    location / {

        proxy_pass http://backend:8080;
    }

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

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中可用的 协议切换 机制。

不过,这里有一个细微之处:由于 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";
}

更复杂的示例演示了在向代理服务器的请求中 Connection 头字段的值如何依赖于客户端请求头中的 Upgrade 字段的存在:

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 指令进行配置。

日志记录#

备注

除了这里列出的选项外,还可以启用 debugging log

Syslog#

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

server=address

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

facility=string

设置 syslog 消息的facility,如 RFC 3164 中定义的。可能的facilities包括:"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条目每秒报告不超过一次。