Caddy 源码全解析
Preface
Caddy 是 Go 语言构建的轻量配置化服务器。同时代码结构由于 Go 语言的轻便简洁,比较易读,推荐学弟学妹学习 Go 的时候也去查看追一下它的源码。不用怕相信这篇文章能给你很大的信心。
可能会有点多,建议多看几遍。
Overview-CaddyMain
当然,建议看这篇文章的时候,查看上手一下 Caddy 的实际配置操作应用,对理解源码会有好处,如果没有操作过也没有关系。
Package
这是 caddy 包的结构
首先我们从一切的开始讲起,即平时我们程序运行的 main.go 函数。
这是 上图 caddy 文件夹下的目录结构。
在 caddy 文件夹中的 main 函数启动 caddy 服务器。实际运行的是 run.go 中的文件,这是方便测试使用
看 main.go 的代码
通过改变 run 变量的值来方便测试,可以学习一下。
启动流程
启动 caddy 的流程画了张图
见到不认识的不用担心,查看上文的目录结构可以找到他们大概的位置,下文会详细讲解。
可以在此图中看到几个重要的点 caddyfileLoader
这是加载 caddyfile 配置来启动服务器的。
如果配置使用过 caddy ,配置的 caddyfile 就是在这里被 Loader
读取后实例化服务器的。如果没有使用过,大致说一下流程,使用 caddy 非常简单,只需配置上文所说的 caddyfile 文件,按行配置选项,然后使用 caddy 运行读取该配置文件即可。简单示例就是以下的文本。
Instance
是运行操作的实例,可以看到几个主要的操作都是在他身上
Server
可以看到拥有 TCP
UDP
两个 Server 的接口。
我们首先关心的是 Start()
启动服务器。
启动服务器
发送 StartupEvent, 参照下文中 Event 理解
// Executes Startup events
caddy.EmitEvent(caddy.StartupEvent, nil)
读取配置文件:
caddyfileinput, err := caddy.LoadCaddyfile(serverType)
启动:
instance, err := caddy.Start(caddyfileinput)
发送 InstanceStartupEvent
caddy.EmitEvent(caddy.InstanceStartupEvent, instance
caddy.Start()
阅读完代码,画一张图帮助理解
是不是很简单,来一点更详细的交互
这里除了 Instance
之外还有两个新名词
Controller
:它是用来帮助 Directives
设置它自身的,通过读取 Token
,这里的 Directives
实际上对应的就是上文所说的 caddyfile 中的配置文件选项。这一点请参照下文中 Loader 下的 excuteDirective
理解。
Token
:是 caddy 自己的 词法分析器 解析 caddyfile 配置文件出的选项的标记。这一点请参照下文中 Loader 中的 Parser 理解
如果不理解,首先记住 caddy 是配置化的服务器,
通过 caddyfile 配置 ->
那么肯定要读取它啦 ->
然后要解析它配置的到底是那些东西 ->
之后呢,就要让配置的目标做到 caddyfile 中声明的更改。
记住这个流程继续看几遍就能理解了。
Server
在 caddy.go 中定义着 Server
的接口,同时实现了优雅的退出。我们首先看图了解组织结构
简单看一下 Stopper
的接口
// Stopper is a type that can stop serving. The stop
// does not necessarily have to be graceful.
type Stopper interface {
// Stop stops the server. It blocks until the
// server is completely stopped.
Stop() error
}
GracefulServer
包含 Stopper
的接口实现了优雅退出,这是拦截了 系统 signal 的信号之后执行的结果,意在意外中断的时候保存好需要保存的东西。
它同时包含着 WrapListener 函数。可以看出,他用来做中间件。
// WrapListener wraps a listener with the
// listener middlewares configured for this
// server, if any.
WrapListener(net.Listener) net.Listener
ServerType
最后看到不同 serverType 生成不同的 server
另外可以看到 这里最重要的 Instance
下面我们进一步查看 Instance
的代码
Instance
instance 是 Server 用来执行操作的实体。首先来看他的结构。它的代码在 主文件夹中的 caddy.go 中
首先我们看一下 它的结构了解下它可能有的功能
struct
type Instance struct {
serverType string
caddyfileInput Input
wg *sync.WaitGroup
context Context
servers []ServerListener
OnFirstStartup []func() error // starting, not as part of a restart
OnStartup []func() error // starting, even as part of a restart
OnRestart []func() error // before restart commences
OnRestartFailed []func() error // if restart failed
OnShutdown []func() error // stopping, even as part of a restart
OnFinalShutdown []func() error // stopping, not as part of a restart
Storage map[interface{
}]interface{
}
StorageMu sync.RWMutex
}