简介

mod_proxy 用于为 apache 实现代理/网关功能,它支持一定数量的流行协议,以及几个不同的负载均衡算法。可以通过添加第三方模块来支持更多的协议和负载均衡算法。

必须要加载一组模块才能提供必要的功能。这些模块可以在构建时静态加入,也可以用 LoadModule 指令动态加载。以下模块必须加载:

  • mod_proxy :提供基本的代理功能
  • mod_proxy_balancer :用于负载均衡
  • 至少一个代理方案,或协议、模块:
协议 模块
AJP13 (Apache JServe Protocol version 1.3) mod_proxy_ajp
CONNECT (for SSL) mod_proxy_connect
FastCGI mod_proxy_fcgi
ftp mod_proxy_ftp
HTTP/0.9, HTTP/1.0, HTTP/1.1 mod_proxy_http
SCGI mod_proxy_scgi
WS, WSS (Web-sockets) mod_proxy_wstunnel

另外,更多的模块可以带来更多的扩展功能:

  • mod_cache :缓存
  • mod_ssl :使用 SSL/TLS 与远程服务器通信,使用 SSLProxy* 指令

正向代理与反向代理

  • 正向代理:forward proxy
  • 反向代理:reverse proxy/gateway

apache 支持正向、反向两种代理。

正向代理

image-center

普通的正向代理是位于源服务器(如网站)和客户端中间的一个服务器,要想从源服务器读取数据,客户端需要把请求发给代理服务器,但会注明源服务器才是最终目标。由代理服务器向源服务器请求内容,得到之后将其返给客户端。客户端必须 显式配置 为使用正向代理来访问网站。

正向代理典型的使用场景是 为内网客户端提供外网访问,否则他们会被防火墙挡住。正向代理也可以使用缓存来减少网络的使用,通常由 mod_cache 实现。

正向代理由 ProxyRequests 指令 激活。因为正向代理可以让客户端通过它访问任意网站,并会隐藏它们的真正起源,因此有必要强化代理服务器的安全,在激活正向代理之前,必须保证只有授权的客户端才能访问代理服务器。

反向代理

image-center

与之相反,反向代理在客户端看来就是一个普通的网页服务器,客户端无需特殊配置。

客户端向反向代理发出正常的内容请求,反向代理会根据配置来判断要把请求发送给谁,它取得内容后再返给客户端。客户端不了解这些后端操作,以为内容就是反向代理提供的。

反向代理的典型应用场景是让 外网用户可以访问位于防火墙后面的服务器。反向代理也可用于在多个后端服务器之间做 负载均衡,或为较慢的后端提供 缓存 功能。另外,也可以仅仅用来让多个服务器同时为同一个域名服务。

反向代理由 ProxyPass 指令 激活,或者也可以由 RewriteRule[P] 标签激活。不需要启用 ProxyRequests

基本范例

以下为非常基本的范例:

正向代理:

ProxyRequests On
ProxyVia On

<Proxy "*">
  Require host internal.example.com
</Proxy>

反向代理:

ProxyPass "/foo" "http://foo.example.com/bar"
ProxyPassReverse "/foo" "http://foo.example.com/bar"

用专门的程序处理特定请求

handler - 处理程序

可以 强制 把某个请求作为一个反向代理请求来处理,通过创建一个合适的 处理程序 来实现。

以下范例,会把所有 PHP 脚本的请求都用反向代理传递给指定的 FastCGI 服务器:

<FilesMatch "\.php$">
    # Unix sockets require 2.4.7 or later
    SetHandler  "proxy:unix:/path/to/app.sock|fcgi://localhost/"
</FilesMatch>

工人 | Workers

后端服务器的配置,以及它们的通信参数是放在 worker 对象中进行管理的。有两个内建的 worker:

默认的正向代理 worker 和默认的反向代理 worker,可以显式配置来添加额外的 worker。

这两个默认的工人兄弟有着固定的配置,如果没有其它的工人能匹配,他们就上。他们不使用 HTTP Keep-Alive 或连接的复用,为每个请求都要打开、关闭到后端服务器的连接。

工人的创建

反向代理

显式配置的式人由他们的 URL 标识,在反向代理中,通常使用 ProxyPassProxyPassMatch 创建并配置。

ProxyPass "/example" "http://backend.example.com" connectiontimeout=5 timeout=30

该配置会创建一个与后端服务器地址 http://backend.example.com 相关的工人。

正向代理

在正向代理中,通常用 ProxySet 指定来定义工人:

ProxySet "http://backend.example.com" connectiontimeout=5 timeout=30

也可以放在 容器中配置:

<Proxy "http://backend.example.com">
  ProxySet connectiontimeout=5 timeout=30
</Proxy>

在正向代理中使用显式配置的工人并不常见,因为正向代理通常要与多个不同的后端服务器通信,如果某些服务器使用的特别频繁,则显式给它们创建工人也有些用处。

工人是如何干活的

被显式配置的工人,它们自己对正向还是反向代理是没有概念的。它们仅仅是把与后端服务器的正常通信封装了起来。反向代理中,由 ProxyPass 创建的工人也可以用于正向代理,只要到 URL 的请求与它们的地址匹配,反之亦然。

用来标识工人的 URL 是后端服务器的 URL,其中可以包括任何路径元素:

ProxyPass "/examples" "http://backend.example.com/examples"
ProxyPass "/docs" "http://backend.example.com/docs"

该示例定义了两个不同的工人,每个都使用一个 单独的连接池和配置

共享工人

如果工人的 URL 发生 重叠,就意味着发生了工人的共享。

ProxyPass "/apps" "http://backend.example.com/" timeout=60
ProxyPass "/examples" "http://backend.example.com/examples" timeout=10

示例中,第二个工人根本就不会被创建,而会使用第一个工人。这样做的好处就是,只有 一个连接池,因此连接可以更多的被 复用。第二个工人的所有配置属性都会被忽略,这在日志中会体现为警告消息。因此,为 /examples 指定的超时将为 60 秒,而不是 10 秒。

要想避免工人的共享,工人的定义就应该按 URL 的长度排序,最长的在前面。如果要最大化工人的共享,则相反。

直接工人与均衡器工人

显式配置的工人主要分两类:直接工人和(负载)均衡器工人。他们支持许多重要的配置属性,通常由 ProxyPassProxySet 来设定。

直接工人适用的选项取决于后端服务器 URL 中所指定的协议,可用的协议包括 ajpfcgiftphttpscgi

均衡器工人是 虚拟 的工人,实际上,他们需要把直接工人做为其成员来处理请求。每个均衡器可以拥有多个成员,均衡器在处理请求时,会基于所配置的均衡算法来选择一个成员。

均衡器工人的创建方法是在其 URL 中使用 balancer 做为协议。均衡器的 URL 是其工人的唯一标识,在均衡器中,使用 BalancerMember 来添加成员。

后端服务器的 DNS 解析

DNS 的解析是发生在 “到后端服务器的 socket ” 首次建立时。如果启用了连接的复用,后端域名只会在每个子进程中解析一次,然后会进入缓存,直到子进程被回收。在规化后端域名的 DNS 维护时一定要考虑到这一点。

对代理服务器的访问加以控制

可以控制谁能访问代理服务器,通过 <Proxy> 容器中的 Require 指令来实现:

<Proxy "*">
  Require ip 192.168.0
</Proxy>

严格限制对代理服务器的访问至关重要,尤其是正向代理,否则随便什么人都可以随便使用,还可以掩盖自己的痕迹。无论是对你自己的网络还是外网都有很大的危险。

使用反向代理时,访问控制就相对没有那么重要了,因为客户端只能访问特定的主机。

会影响启动速度的指令

ProxyBlock

ProxyBlock 相当于敏感词的过滤,该指令用于指定哪些词语、主机、域名被禁止访问。

ProxyBlock "news.example.com" "auctions.example.com" "friends.example.com"

这些网站都会限制访问。

在 HTTP、HTTPS、FTP 的文档请求中,如果其文件名中包含这些关键词,就会被代理服务器限制访问。代理模块还会尝试解析关键字中的域名的 IP 地址,把 IP 地址也放入缓存。这些都会导致服务器的启动时间变长。

内网代理

ProxyRemote

局域网往往需要通过公司的防火墙来转发外部的请求,apache 代理服务器同样满足了这一需要。为此可以使用 ProxyRemote 指令来把对应的协议转发给防火墙代理。

客户端 > apache > 远端代理 > 其它服务器

防火墙代理可以就是上面的远端代理。

语法: ProxyRemote match remote-server

ProxyRemote 用来配置 对于 apache 来说的 远端代理服务器,apache 会把匹配的 协议 转发给这些远端代理服务器。

match 可以是远端代理所支持的协议名称,如 http、ftp、https 等,也可以是远端代理会使用的部分 URL,或者用通配符 * 来匹配所有请求。

remote-server 是远端代理的局部 URL,其语法为:scheme://hostname[:port]

scheme 实际上是指与远端服务器通信所使用的 协议。本模块只支持 http 和 https。如果使用 https,会使用 HTTP CONNECT 方法通过远端代理来转发请求。

ProxyRemote "http://goodguys.example.com/" "http://mirrorguys.example.com:8000"
ProxyRemote "*" "http://cleverproxy.localdomain"
ProxyRemote "ftp" "http://ftpproxy.mydomain:8080"

可见,scheme 可以是完整的网址、通配符、协议名。

根据第三行的配置,apache 会转发 FTP 请求,将其封装成另一个 HTTP 代理请求,发给远端的代理来处理。

该选项也支持反向代理的配置,可以把后端服务器嵌入一个虚拟主机的 URL,即使该后端服务器隐藏在另一个转发代理的后面。

NoProxy

该指令与 ProxyRemote 配合使用,其指定的访问目标不会经 ProxyRemote 转发,而会被 直接访问

该指令只适用 内网 中的 apache 代理服务器,它用来指定一系列的子网、IP 地址、主机、域名等,用空格分隔,匹配一项或多项的请求会直接处理,不会转发给别的代理。

ProxyRemote  "*"  "http://firewall.example.com:81"
NoProxy         ".example.com" "192.168.112.0/21"

示例中,客户端如果访问 *.example.com192.168.112.0/21 ,可以直接访问。而要是访问其他网站就会被转发给 http://firewall.example.com:81 来处理。

可以想象一下某网站自家员工在公司上网的情景。

内网的用户如果想在访问网页时省略本地的域名,可以用 http://somehost 来代替 http://somehost.example.com

协议的调整

mod_proxy 模块向某个源服务器发送请求时,如果该服务器没有正确的部署 keepalive 或 HTTP/1.1 的话,有两个环境变量可以把请求强制为 使用 HTTP/1.0不用 keepalive,这些都由 SetEnv 指令来实现:

<Location "/buggyappserver/">
  ProxyPass "http://buggyappserver:7001/foo/"
  SetEnv force-proxy-request-1.0 1
  SetEnv proxy-nokeepalive 1
</Location>

在 2.4.26 之后的版本中,可以通过设置 no-proxy 环境变量来禁止 mod_proxy 处理当前的请求,该变量应该用 SetEnvIf 来配置,因为 SetEnv 解析的时机不够靠前。

请求的正文

request body

有一些请求方法会包含一个请求正文,如 POST。HTTP 协议要求,这样的请求要么使用分块传输编码(Chunked Transfer Encoding),要么发送 Content-Length 请求标头。

把这些请求传递给源服务器时,mod_proxy_http 会始终尝试发送 Content-Length ,但是如果正文很大,而且原始请求使用的是分块编码,则把请求传递给后端时也使用分块编码。

可以用环境变量来控制这种选择。配置 proxy-sendcl 可以确保与后端服务器最大的兼容,始终会发送 Content-Length。而配置 proxy-sendchunked 则会减少资源的使用。

在某些情况下,服务器必须临时把请求正文放到磁盘中,以满足对请求正文处理的需要。例如,如果原始正文是用分块编码处理的,而且很大,但管理员要求到后端的请求必须有 Content-Length,或必须是 HTTP/1.0 的,这时就需要把正文临时放到磁盘中。还有一种情况是,请求正文已经有了一个 Content-Length 标头,但服务器被配置为过滤传入的请求正文。

LimitRequestBody 指令用于限制客户端发送的 HTTP 请求中的正文的总大小。

反向代理请求标头

在反向代理模式中,mod_proxy_http 会增加几个请求标头,为的是向源服务器传递信息。

X-Forwarded-For :客户端的 IP 地址

X-Forwarded-Host :客户端在 HTTP 请求的 Host 标头中最初所请求的主机,

X-Forwarded-Server :代理服务器的主机名

在源服务器上使用这些标头时一定要小心,因为如果原始请求中就包含了一部分这些标头,最终这些标头就有可能包含不止一个值。

例如,你可以在源服务器的日志格式字符串中使用 %{X-Forwarded-For}i,来记录源客户端的 IP 地址。但是,如果请求经过了多个代理的话,你可能会得到不止一个地址。

如果需要把自定义请求标头加到被转发的请求中,可以使用 RequestHeader 指令。

部分 mod_proxy 指令介绍

BalancerGrowth

在配置文件所设定的基础之上,该指令用于指定每个虚拟主机额外还可以再增加多少个均衡器,必须要有至少一个预配置好的均衡器该指令才能生效。

BalancerInherit

该指令会使当前的虚拟主机继承主服务器中定义的 反向代理均衡器 以及 工人

如果同时使用 Balancer Manager,会带来一些问题和不一致的行为,应当禁用。

BalancerMember

向均衡器组中添加成员。

语法:

BalancerMember [balancerurl] url [key=value [key=value ...]]

该指令可用于 <Proxy balancer://...> 容器中,可以接受所有适用于 ProxyPass 的键值对。

还有一个单独的参数只适用于 BalancerMember,即 loadfactor。它是成员的负载因子,取值为 1~100 之间的整数,用于定义应用到成员的 权重

语法中的 balancerurl 仅在 <Proxy balancer://...> 容器之外才有用,它对应的是由 ProxyPass 定义的均衡器的 URL。

<Proxy balancer://...> 容器中的所有均衡器 URL 的路径组件都会被忽略。

均衡器成员的 URL 中的结尾的斜线通常应该删除。

BalancerPersist

该指令会使与均衡器及其成员有关的 共享内存空间 在进程重启期间能够被保留下来。这也使得这些本地的修改在 restart/graceful 状态切换期间不至于丢失。

.