写一部导演

Varnish已经提供了一组通用的控制器,从Varnish 4开始,它被捆绑在内置的 VMOD导向器-Varnish导向器模块 。编写导向器归根结底是使用适当的数据结构和API编写VMOD。如果没有一个内置组件满足您的需求,您不仅可以编写自己的控制器,而且从Varnish 4.1开始,您甚至可以编写自己的后端。

后端可以分为以下几类:

  • 静态:在VCL中声明的本机后端

  • 动态:由VMOD创建的本机后端

  • 定制:由VMOD创建和完全管理的后端

后端与控制器

后端和定向器的直观分类是前者的端点和后者的负载均衡器,但实际实现要稍微微妙一些。VMOD可以接受后端参数并在VCL中返回后端(请参见 VCL和C数据类型 ),但底层的C类型是 struct director 也就是 VCL_BACKEND 泰德福。在引擎盖下,导演是一个通用的概念,后端是一种导演。

在这一点上,两者之间的界限有点模糊,让我们来看看一些代码::

// VRT interface from vrt.h

struct vdi_methods {
    unsigned                        magic;
#define VDI_METHODS_MAGIC           0x4ec0c4bb
    const char                      *type;
    vdi_http1pipe_f                 *http1pipe;
    vdi_healthy_f                   *healthy;
    vdi_resolve_f                   *resolve;
    vdi_gethdrs_f                   *gethdrs;
    vdi_getip_f                     *getip;
    vdi_finish_f                    *finish;
    vdi_event_f                     *event;
    vdi_release_f                   *release;
    vdi_destroy_f                   *destroy;
    vdi_panic_f                     *panic;
    vdi_list_f                      *list;
};

struct director {
    unsigned                        magic;
#define DIRECTOR_MAGIC              0x3336351d
    void                            *priv;
    char                            *vcl_name;
    struct vcldir                   *vdir;
    struct lock                     *mtx;
};

一名董事可以概括为:

  • 作为一种特殊的 type 具有一组对该特定类型的所有实例都相同的操作

  • 某些特定于实例的属性,如 vcl_nametype -特定的私有数据

两者之间的区别是 load balancing 导演和一名 backend 导演主要是他们要执行的职能。

实施导向器的基本步骤包括:

  • 实施所需的功能

  • 填写一份 struct vdi_methods 使用您的控制器类型和函数指针的名称

    存在一种 healthy 回调表示控制器具有某种动态确定其健康状态的方法。

  • 在构造函数或其他初始化例程中,分配和初始化特定于控制器的配置状态(也称为私有数据)并调用 VRT_AddDirector() 你的 struct vdi_methods 、指向您所在州的指针和您的Director实例名称的打印格式

  • 实现返回的方法或函数 VCL_BACKEND

  • 在析构函数或其他终结器中,调用 VRT_DelDirector()

  • 实施 destroy 回调以摧毁实际的董事私有状态。它将在对导演的所有引用都消失时被调用,在此之前,私有状态必须保持不变 vdi_methods 可调用的函数(但它们可能返回错误)。

虽然VMOD可以实现返回导向器的功能, 对象和方法 通常是更自然的表示,其中vmod对象实例是或引用导向器私有数据。

负载平衡控制器

如中所示 VMOD导向器-Varnish导向器模块 ,您可以编写将共享相同角色的后端分组的董事,并根据策略选择他们。如果您需要的不只是内置的策略(循环、散列等),即使它们可以堆叠在一起,您也可以编写自己的策略。

在这种情况下,您只需实现 resolve 为导演发挥作用。导演们一直在走访,直到找到一位叶导演。一位叶导演没有 resolve 函数,用于实际发出后端请求,就像您在VCL中声明的后端一样。

load balancing 董事们使用 VRT_Assign_Backend() 引用其他董事的意见。他们 must 实施 release 回调,它必须释放对其他控制器的所有引用,并确保在返回后不获取任何引用。

静态导向器

与下面介绍的动态后端不同,保证具有VCL生存期(即,它们在VCL变冷之前不会被销毁)的控制器可以调用 VRT_StaticDirector() 以避免引用计数开销。

动态后端

如果您想在TCP或UDS上使用HTTP/1,但由于某种原因VCL不适合,那么您可以重新使用整个后端工具。例如,它允许您按需添加和删除后端,而无需重新加载您的VCL。然后,您可以利用您的供应系统。

请考虑以下代码片段:

backend default {
    .host = "localhost";
}

VCL编译器将此声明转换为 struct vrt_backend 。加载VCL时,Varnish调用 VRT_new_backend (或者更确切地说 VRT_new_backend_clustered 为了提高VSM效率),以便创建导向器。Varnish没有为实际的后端公开它的数据结构,只有控制器抽象和动态后端是像静态后端一样构建的,一个 struct 一次来一次。你可以去掉那些 struct vrt_backend 一旦你有了 struct director

(动态)后端不能超过其VCL的寿命,因为原生后端 owned 由VCLS提供。尽管动态后端的寿命不能超过其VCL,但可以使用以下命令随时删除它 VRT_delete_backend 。一旦丢弃,VCL会删除剩余的后端,您不需要打理它。

引用计数用于确保不再引用的后端被销毁。

最后,Varnish将负责以下方面的事件传播 all 原生后端,但动态后端只能在VCL处于热状态时创建。如果您的后端是由独立线程创建的(基本上在VCL作用域之外),则必须订阅VCL事件并监视VCL状态(请参见 事件函数 )。如果你试图在冷的VCL上创建一个后端,Varnish将会死机 VRT_new_backend 会回来的 NULL 如果VCL正在冷却。此外,我们亦鼓励您遵守 VCL温度 总体而言。

健康探测仪

在VCL程序中,可以查询控制器的运行状况(请参见 Bool Healthy(后端BE) )。如果一个董事实现了 healthy 功能,否则它总是被认为是健康的。

除非您正在制作动态后端,否则您需要自己照顾健康探头。为 load balancing 董事,健康通常意味着至少有一个健康的底层后端或董事。

对于动态后端,只需将 probe 字段中的 struct vrt_backend 。一旦创建了控制器,也不再需要探测定义。然后,Varnish将负责运行状况探测器并禁用冷VCL上的功能(请参见 事件函数 )。

无需初始化您自己的探测器定义,您可以获得一个 VCL_PROBE 直接从VCL构建(请参见 VCL和C数据类型 )。

自定义后端

如果您想实现自定义后端,请看一看Varnish如何实现本机后端。它是规范的实现,尽管它提供其他服务,如连接池或统计,但它本质上是一个控制器,它的状态是 struct backend 。Varnish本地后端目前通过TCP或UDS使用HTTP/1,因此,如果您希望Varnish执行其他操作,例如通过UDP连接或使用不同的协议,则需要创建自己的自定义后端。

如果您想利用VCL中的探测器声明,因为它们只是规范,所以具有可重用的优势,您可以。但是,您需要从头开始实现整个探测基础设施。

您还可以考虑使您的自定义后端与VCL状态兼容(请参见 事件函数 )。

如果您正在实现 gethdrs 方法(即,您的后端能够生成后端响应以在 vcl_backend_response ),您将希望记录响应代码、协议和它将创建的各种标头,以便于调试。对于这一点,您可以查看 VSL* 中列出的函数族 cache/cache.h

数据结构注意事项

在创建自定义后端时,您可能希望提供本机后端的语义。在这种情况下,您可以使用宏,而不是在数据结构之间重复冗余字段 VRT_BACKEND_FIELDSVRT_BACKEND_PROBE_FIELDS 一下子把它们都宣布出来。这是Varnish用来在 struct vrt_backend 以及它的内部数据结构。

可以使用宏自动执行复制 VRT_BACKEND_HANDLEVRT_BACKEND_PROBE_HANDLE 。您可以了解如何在Varnish代码库中使用它们。