v2中文文档
项目

架构

Caddy 是一个单一的、自包含的、静态的二进制文件,外部依赖项为零,因为它是用 Go 编写的。这些价值观构成了项目愿景的重要组成部分,因为它们简化了部署并减少了生产环境中繁琐的故障排除。

如果没有动态链接,那么如何扩展呢?Caddy 采用了一种新颖的插件架构,其功能远远超出任何其他 Web 服务器,即使是那些具有外部(动态链接)依赖项的服务器。

我们“更少的活动部件”的理念最终会导致更可靠、更易于管理、更便宜的站点——尤其是在规模上。这份半技术文档描述了我们如何通过软件工程实现这一目标。

概述

CCaddy 由命令、核心库和模块组成。

命令提供了你更熟悉的命令行界面。这是你从操作系统启动进程的方式。这里的代码和逻辑数量相当少,并且仅具有以用户所需方式引导核心所需的内容。我们有意避免使用标记和环境变量进行配置,除非它们与引导配置有关。

核心库,或者叫 Caddy的“核心”,主要作用是管理配置。它可以Run() 一个新配置或Stop() 正在运行的配置。它还为要使用的模块提供各种实用程序、类型和值。

模块做其他所有事情。许多模块内置在 Caddy 中,称为 标准模块。这是是对大多数用户而言最有用的。

Caddy核心

Caddy的核心只是加载一个初始配置("config"),或者,在没有配置的情况下,随后打开一个套接字以接受新配置。

Caddy配置 就是一个JSON文档,在其顶层包含一些字段:

{
	"admin": {},
	"logging": {},
	"apps": {•••},
	...
}

Caddy 的核心知道如何在本地使用其中一些字段:

但是其他顶级字段(apps)对于 Caddy 的核心是不透明的。事实上,Caddy 所知道的如何处理apps中的字节,是将它们反序列化为一个接口类型,它可以调用两个方法:

  1. Start()
  2. Stop()

……就是这样,它在每个应用的配置载入时调用Start(),且在它们的配置被卸载时调用Stop()

启动应用的模块时,它会初始化应用模块的生命周期。

模块生命周期

有两种模块:主机模块访客模块

主机模块(或“父”模块)是加载其他模块的模块。

访客模块(或“子”模块)是那些被加载的模块。所有模块都是访客模块——甚至是应用程序模块。

模块被加载,被配置和验证,被使用,然后被清理,按以下顺序:

  1. 被载入
  2. 被配置和验证
  3. 被使用
  4. 被清理

当首先加载配置时,Caddy 通过初始化所有已配置的应用模块来启动模块生命周期。之后,它被每个应用程序模块带着它走完剩下的路。

加载阶段

加载模块涉及将其 JSON 字节反序列化为内存中的类型值。就是……基本上就是这,它只是将 JSON 解码为一个值。

供应阶段

这个阶段是大部分设置工作的地方。所有模块在加载后都有机会自行配置。

由于 JSON 编码中的任何属性都已经被解码,因此这里只需要进行额外的设置。配置期间最常见的任务是设置访客模块。换句话说,供应主机模块也会导致供应其客户模块,一直向下。

您可以通过在我们的文档遍历 Caddy 的 JSON 结构来了解这一点。你看到{•••} 的任何地方都是可以使用访客模块的地方;当你单击其中一个时,你可以一直向下探索,直到没有更多的访客模块。

其他常见的配置任务是设置将在模块生命周期内使用的内部值,或标准化输入。例如,http.matchers.remote_ip 模块使用配置阶段从它从 JSON 接收的字符串输入中解析 CIDR 值。这样,它就不必在每个 HTTP 请求期间都这样做,因此效率更高。

验证也可以在供应阶段进行。如果模块的结果配置无效,则会在此处返回错误,从而中止整个配置加载过程。

使用阶段

一旦客户模块被配置和验证,它就可以被它的主机模块使用。这究竟意味着什么取决于每个主机模块。

每个模块都有一个 ID,它由一个命名空间和该命名空间中的名称组成。例如,http.handlers.reverse_proxy 是一个 HTTP 处理程序,因为它在http.handlers 命名空间中,并且它的名称是reverse_proxy。命名空间中的所有模块都http.handlers 满足主机模块已知的相同接口。因此,http 应用程序知道如何加载和使用这些类型的模块。

清理阶段

当需要停止配置时,所有模块都会被卸载。如果一个模块分配了任何应该被释放的资源,它就有机会在清理阶段这样做。

插入

一个模块——或任何 Caddy 插件——通过import为模块的包添加一个“插入”到 Caddy。通过导入包,模块将自己注册 到 Caddy 核心,因此当 Caddy 进程启动时,它通过名称知道每个模块。它甚至可以在模块值和名称之间关联,反之亦然。

管理配置

由于服务器需要的高并发性和数千个参数,更改正在运行的服务器的活动配置(通常称为“重新加载”)可能会很棘手。Caddy 使用具有许多优点的设计优雅地解决了这个问题:

  • 不中断运行服务
  • 可以进行粒度配置更改
  • 只需要一把锁(在后台)
  • 所有的重载都是原子的、一致的、隔离的,并且大多是持久的(“ACID”)
  • 最小的全局状态

您可以在此处观看有关 Caddy 2 设计的视频

配置重新加载通过配置新模块来工作,如果全部成功,则清理旧模块。在短时间内,两个配置同时运行。

每个配置都与一个包含所有模块状态的上下文相关联,因此大多数状态永远不会逃脱配置的范围。这对于正确性、性能和简单性来说是个好消息!

然而,有时真正的全局状态是必要的。例如,反向代理可能会跟踪其上游的健康状况;由于全局每个上游只有一个,因此如果每次进行小的配置更改时都忘记它们,那将是很糟糕的。幸运的是,Caddy提供了类似于语言运行时的垃圾收集器的工具来保持全局状态的整洁。

一种明显的在线配置更新方法是同步对每个配置参数的访问,即使在热路径中也是如此。这在性能和复杂性方面非常糟糕———尤其—是在规模上——因此 Caddy 不使用这种方法。

相反,配置被视为不可变的原子单元:要么整个东西被替换,要么什么都没有改变。管理API端点——允许通过遍历结构进行精细更改——仅改变配置的内存表示,从中生成并加载一个全新的配置文档。这种方法在简单性、性能和一致性方面具有巨大的优势。由于只有一把锁,Caddy 很容易处理快速重新加载。