后端服务器

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() 将解析为 server1Aserver1B 如果两者都是健康的,或者如果只有一个是健康的,那么就是其中之一。只有当两个人都生病时,健康的服务器才能从 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 过期。