后端服务器¶
Varnish有一个“后端”或“源”服务器的概念。后端服务器是提供Varnish将加速的内容的服务器。
我们的第一个任务是告诉Varnish在哪里可以找到它的后端。启动您最喜欢的文本编辑器并打开相关的VCL文件。
在顶部的某个地方将会有一个类似于以下内容的部分。
# backend default {
# .host = "127.0.0.1";
# .port = "8080";
# }
我们去掉了此文本节中的注释标记,使其看起来像.::
backend default {
.host = "127.0.0.1";
.port = "8080";
}
现在,这段配置用Varnish定义了一个后端,称为 default 。当Varnish需要从此后端获取内容时,它将连接到本地主机(127.0.0.1)上的端口8080。
Varnish可以定义多个后端,您甚至可以出于负载平衡的目的将几个后端连接到后端集群中。
“无”后端¶
后端也可以声明为 none
使用以下语法:
backend default none;
none
后端是特殊的:
所有后端均已声明
none
比较相等::backend a none; backend b none; sub vcl_recv { set req.backend_hint = a; if (req.backend_hint == b) { return (synth(200, "this is true")); } }
这个
none
后端评估结果为false
在布尔上下文中使用时::backend nil none; sub vcl_recv { set req.backend_hint = nil; if (! req.backend_hint) { return (synth(200, "We get here")); } }
当董事发现没有健康的后端时,他们通常会返回
none
后端
多个后端¶
在某些情况下,您可能需要Varnish来缓存来自多个服务器的内容。您可能希望Varnish将所有URL映射到一个主机或不映射到一个主机。有很多选择。
假设我们需要将一个Java应用程序引入到我们的PHP网站中。假设我们的Java应用程序应该处理以 /java/ 。
我们设法让它在8000号端口上启动并运行。现在,让我们来看看 default.vcl .::
backend default {
.host = "127.0.0.1";
.port = "8080";
}
我们添加一个新的后端。::
backend java {
.host = "127.0.0.1";
.port = "8000";
}
现在我们需要告诉Varnish将差异URL发送到哪里。让我们来看看 vcl_recv .::
sub vcl_recv {
if (req.url ~ "^/java/") {
set req.backend_hint = java;
} else {
set req.backend_hint = default;
}
}
这很简单,真的。让我们停下来思考一下这一点。正如您所看到的,您可以定义如何基于真正任意的数据选择后端。您想将移动设备发送到不同的后端吗?没问题。 if (req.http.User-agent ~ /mobile/) ..
应该能行得通。
如果没有显式的后端选择,Varnish将继续使用 default 后端。如果没有名为 default ,VCL中找到的第一个后端将用作默认后端。
Varnish中的后端和虚拟主机¶
Varnish完全支持虚拟主机。然而,它们可能以某种与直觉相反的方式工作,因为它们从未被明确声明。您可以在中设置传入HTTP请求的路由 vcl_recv 。如果您希望在虚拟主机的基础上完成此路由,则只需检查 req.http.host 。
你可以有这样的东西::
sub vcl_recv {
if (req.http.host ~ "foo.com") {
set req.backend_hint = foo;
} elsif (req.http.host ~ "bar.com") {
set req.backend_hint = bar;
}
}
请注意,第一个正则表达式将匹配“foo.com”、“www.foo.com”、“zoop.foo.com”和以“foo.com”结尾的任何其他主机。在本例中,这是有意的,但您可能希望它更紧一些,可能依赖于 ==
操作符,如下所示::
sub vcl_recv {
if (req.http.host == "foo.com" || req.http.host == "www.foo.com") {
set req.backend_hint = foo;
}
}
通过代理连接¶
从这个版本开始,Varnish可以连接到一个实际的 destination 通过一个 proxy 使用 PROXY2 协议。可以添加其他协议。
目前,此功能的典型用例是通过TLS建立TLS加密连接 onloader 。这个 onloader 中获取的目标地址信息支持动态连接。 PROXY2 前言。例如,使用 haproxy 版本2.2或更高版本,此代码段可用作配置 onloader **
# to review and adjust:
# - maxconn
# - bind ... mode ...
# - ca-file ...
#
listen sslon
mode tcp
maxconn 1000
bind /path/to/sslon accept-proxy mode 777
stick-table type ip size 100
stick on dst
server s00 0.0.0.0:0 ssl ca-file /etc/ssl/certs/ca-bundle.crt alpn http/1.1 sni fc_pp_authority
server s01 0.0.0.0:0 ssl ca-file /etc/ssl/certs/ca-bundle.crt alpn http/1.1 sni fc_pp_authority
server s02 0.0.0.0:0 ssl ca-file /etc/ssl/certs/ca-bundle.crt alpn http/1.1 sni fc_pp_authority
# ...
# A higher number of servers improves TLS session caching
然后,在同一服务器/命名空间上运行的Varnish可以使用 onloader 与 .via
功能(请参见 属性 .via ):
backend sslon {
.path = "/path/to/sslon";
}
backend destination {
.host = "my.https.service";
.port = "443";
.via = sslon;
}
这个 .authority
属性可用于指定 SNI 如果该连接不同于 .host
。
董事¶
您还可以将多个后端分组为一组后端。这些团体被称为董事。这将给您带来更高的性能和弹性。
您可以定义几个后端,并将它们组合在一个控制器中。这需要加载一个VMOD、一个Varnish模块,然后在 vcl_init .::
import directors; # load the directors
backend server1 {
.host = "192.168.0.10";
}
backend server2 {
.host = "192.168.0.11";
}
sub vcl_init {
new bar = directors.round_robin();
bar.add_backend(server1);
bar.add_backend(server2);
}
sub vcl_recv {
# send all traffic to the bar director:
set req.backend_hint = bar.backend();
}
这位导演是一位循环制的导演。这意味着指挥交换机将以循环方式分发传入请求。也有一个 random 以随机方式分发请求的主管,你猜对了。如果这还不够,您还可以编写您自己的导演(请参见 写一部导演 )。
但是,如果您的一台服务器出现故障怎么办?Varnish是否可以将所有请求定向到运行状况良好的服务器?当然可以。这就是健康检查发挥作用的地方。
健康检查¶
让我们设置一个具有两个后端和运行状况检查的控制器。首先,让我们定义后端::
backend server1 {
.host = "server1.example.com";
.probe = {
.url = "/";
.timeout = 1s;
.interval = 5s;
.window = 5;
.threshold = 3;
}
}
backend server2 {
.host = "server2.example.com";
.probe = {
.url = "/";
.timeout = 1s;
.interval = 5s;
.window = 5;
.threshold = 3;
}
}
这里的新特性是 probe
。在本例中,Varnish将每5秒检查一次每个后端的运行状况,并在1秒后超时。每个轮询都会向/发送一个GET请求。如果最近5次轮询中有3次成功,则后端被认为是健康的,否则将被标记为有病。
请参阅 后端运行状况探测器 部分中的 VCL 文档以获取更多信息。
现在我们来定义“导演”::
import directors;
sub vcl_init {
new vdir = directors.round_robin();
vdir.add_backend(server1);
vdir.add_backend(server2);
}
你用这个 vdir Director作为请求的后端提示,就像您使用简单的后端一样。Varnish不会向标记为不健康的主机发送流量。
如果所有后端都停机,Varnish还可能提供过时的内容。看见 Grace模式和Keep 有关如何启用此功能的详细信息,请参阅。
请注意,Varnish将为所有加载的VCL保持健康探测器运行。Varnish将合并看似相同的探测器--因此,如果您执行大量VCL加载,请注意不要更改探测器配置。卸载VCL将丢弃探测器。具体操作方法请参考ref:ference-vcl-director。
分层¶
默认情况下,大多数董事的 .backend()
方法返回对控制器本身的引用。这允许分层,如本例所示::
import directors;
sub vcl_init {
new dc1 = directors.round_robin();
dc1.add_backend(server1A);
dc1.add_backend(server1B);
new dc2 = directors.round_robin();
dc2.add_backend(server2A);
dc2.add_backend(server2B);
new dcprio = directors.fallback();
dcprio.add_backend(dc1);
dcprio.add_backend(dc2);
}
利用该初始化, dcprio.backend()
将解析为 server1A
或 server1B
如果两者都是健康的,或者如果只有一个是健康的,那么就是其中之一。只有当两个人都生病时,健康的服务器才能从 dc2
如果有的话,将被退还。
董事决议¶
返回后,当准备好后端连接时,就会发生实际的解析 vcl_backend_fetch {}
或 vcl_pipe {}
。
在某些情况下,比如服务器分片,VCL中已经需要解析结果。在这类情况下, .resolve()
方法,如本例所示::
set req.backend_hint = dcprio.backend().resolve();
将此语句与前面的示例代码一起使用时, req.backend_hint
将设置为 server*
后端或 none
后端,如果他们都病了。
.resolve()
在任何对象上工作 BACKEND
打字。
连接池¶
打开与后端的连接总是要付出代价的:根据连接类型和后端基础设施的不同,打开新连接的开销对于本地Unix域套接字来说从非常低的开销不等(请参见 后端定义 .path
属性)以用于在可能的多跳和长网络路径上建立可能的多个TCP和/或TLS连接。无论开销多么相关,它肯定总是存在的。
因此,因为重用现有连接通常可以被认为是为了减少开销和延迟,所以Varnish默认情况下会将后端连接池化:每当后端任务完成时,使用的连接不会关闭,而是会添加到池中以供以后重用。若要避免重复使用连接, Connection: close
可以在中添加HTTP标头 vcl_backend_fetch 。
虽然后端是按VCL定义的,但连接池可以跨VCL甚至跨后端工作:默认情况下,池连接的标识符是从 .host
/ .port
或 .path
的属性 后端定义 (VMOD可以使用自定义标识符)。因此,只要两个后端共享相同的地址信息,无论它们是在哪个VCL中定义的,它们的连接都是从公共池中获取的。
如果不是由后端主动关闭,则池连接将由Varnish保持打开,直到 backend_idle_timeout 过期。