Preview

我们已经了解了 Caddy 的整个建立流程,这一次我们来了解关于 Caddy 服务的执行者, Instance 和 Server。

Caddy 中 Server 的定义是接口,只要 满足 Server 的接口的行为,你可以任意扩展你自己的 Caddy 程序。

而 Instance 是执行服务的实例。

Server

在 caddy.go 中定义着 Server 的接口,同时实现了优雅的退出。我们首先看图了解组织结构

caddy-server-interface.svg

简单看一下 Stopper 的接口

  1. // Stopper is a type that can stop serving. The stop
  2. // does not necessarily have to be graceful.
  3. type Stopper interface {
  4. // Stop stops the server. It blocks until the
  5. // server is completely stopped.
  6. Stop() error
  7. }

GracefulServer 包含 Stopper 的接口实现了优雅退出,这是拦截了 系统 signal 的信号之后执行的结果,意在意外中断的时候保存好需要保存的东西。

它同时包含着 WrapListener 函数。可以看出,他用来做中间件。

  1. // WrapListener wraps a listener with the
  2. // listener middlewares configured for this
  3. // server, if any.
  4. WrapListener(net.Listener) net.Listener

ServerType

上文看到,caddyfile 的 Loader 是针对 不同的 serverType 的,例如 HTTP。

当没有 caddyfile 可以 Load 的时候,对于一个 ServerType 也有一个默认的方法 DefaultInput进行调用,让你的程序更健壮。

  1. // ServerType contains information about a server type.
  2. type ServerType struct {
  3. // Function that returns the list of directives, in
  4. // execution order, that are valid for this server
  5. // type. Directives should be one word if possible
  6. // and lower-cased.
  7. Directives func() []string
  8. // DefaultInput returns a default config input if none
  9. // is otherwise loaded. This is optional, but highly
  10. // recommended, otherwise a blank Caddyfile will be
  11. // used.
  12. DefaultInput func() Input
  13. // The function that produces a new server type context.
  14. // This will be called when a new Caddyfile is being
  15. // loaded, parsed, and executed independently of any
  16. // startup phases before this one. It's a way to keep
  17. // each set of server instances separate and to reduce
  18. // the amount of global state you need.
  19. NewContext func(inst *Instance) Context
  20. }

保存

有一个全局变量维护有的 ServerType

  1. // serverTypes is a map of registered server types.
  2. serverTypes = make(map[string]ServerType)

usage

使用 RegisterServerType 注册

  1. // RegisterServerType registers a server type srv by its
  2. // name, typeName.
  3. func RegisterServerType(typeName string, srv ServerType) {
  4. if _, ok := serverTypes[typeName]; ok {
  5. panic("server type already registered")
  6. }
  7. serverTypes[typeName] = srv
  8. }

Directives

前文提过该概念,这是在 caddy 中的概念,和 token 对应,
Loader 和 Parser 会通过 caddyfile 生产 token ,
通过读取 token 对应相应的 plugin 的 directive ,用来配置 plugin 。

值得注意的是,他是交给 controller 来配置的,可在上一篇文章详细了解

controller 会调用 plugin 结构中的 Action 字段,
Action 是一个 SetupFunc 类型

  1. // SetupFunc is used to set up a plugin, or in other words,
  2. // execute a directive. It will be called once per key for
  3. // each server block it appears in.
  4. type SetupFunc func(c *Controller) error

在 Plugin.go 中有 DirectiveAction 来通过 directive 调用 action

  1. // DirectiveAction gets the action for directive dir of
  2. // server type serverType.
  3. func DirectiveAction(serverType, dir string) (SetupFunc, error) {
  4. if stypePlugins, ok := plugins[serverType]; ok {
  5. if plugin, ok := stypePlugins[dir]; ok {
  6. return plugin.Action, nil
  7. }
  8. }
  9. if genericPlugins, ok := plugins[""]; ok {
  10. if plugin, ok := genericPlugins[dir]; ok {
  11. return plugin.Action, nil
  12. }
  13. }
  14. return nil, fmt.Errorf("no action found for directive '%s' with server type '%s' (missing a plugin?)",
  15. dir, serverType)
  16. }

最后看到不同 serverType 生成不同的 servercaddy-serverType.svg

另外可以看到 这里最重要的 Instance 下面我们进一步查看 Instance 的代码

Instance

instance 是 Server 用来执行操作的实体。首先来看他的结构。它的代码在 主文件夹中的 caddy.go

首先我们看一下 它的结构了解下它可能有的功能

struct

  1. type Instance struct {
  2. serverType string
  3. caddyfileInput Input
  4. wg *sync.WaitGroup
  5. context Context
  6. servers []ServerListener
  7. OnFirstStartup []func() error // starting, not as part of a restart
  8. OnStartup []func() error // starting, even as part of a restart
  9. OnRestart []func() error // before restart commences
  10. OnRestartFailed []func() error // if restart failed
  11. OnShutdown []func() error // stopping, even as part of a restart
  12. OnFinalShutdown []func() error // stopping, not as part of a restart
  13. Storage map[interface{}]interface{}
  14. StorageMu sync.RWMutex
  15. }

serverType 代表这个实例的服务器类型,通常是 HTTP

caddyfileInputInput 类型,通常我们配置 caddy 服务器的时候,就是通过编辑 caddyfileInput 的文本实现的修改配置行动。值得注意的是,生成 Instance 的参数同样是 caddyfile,这里的 caddyfile 在程序中是一个接口,一会儿继续讲解

wg 是用来等待所有 servers 执行他们操作的信号量。

context 是实例 Instance的上下文,其中包含 serverType 信息和服务器配置管理状态的信息。

servers 是一组 server 和 他们的 listeners,两种 Server TCP/UDP,即 serverType ,两种不同的 serverType 会对应不同的 caddyfile中的选项。

OnXXX 等 6 个函数是一系列回调函数,通过名字能够看出在什么时候回调触发。

Storage 是存储数据的地方,本来可以设计在 全局状态中,但是设计在这里更好,考虑到垃圾回收机制,进程中重新加载时,旧的 Instance be destroyed 之后,会变成垃圾,收集。这和 12-factor 中的 第九条 Disposability 相符合。意思是每一次重载实例 Instance 即使是在进程中重载,也不会出现数据相互影响到情况,保持幂等

屏幕快照 2019-08-04 下午6.34.33.png
虽然 Instance 操作着众多操作,但是我们却不是从它讲起,我们已经了解了 Instance 的整个建立,渐渐了解 Instance 能调用的函数,自然 Instance 的功能就清晰了。

上面的 Instance 结构非常简单,各个字段的功能也很明晰,下面我们看涉及到 建立服务器的 Context 字段和结构

Context

Context 看源码可以知道,他是用来创建 Server 的。

  1. // Context is a type which carries a server type through
  2. // the load and setup phase; it maintains the state
  3. // between loading the Caddyfile, then executing its
  4. // directives, then making the servers for Caddy to
  5. // manage. Typically, such state involves configuration
  6. // structs, etc.
  7. type Context interface {
  8. // Called after the Caddyfile is parsed into server
  9. // blocks but before the directives are executed,
  10. // this method gives you an opportunity to inspect
  11. // the server blocks and prepare for the execution
  12. // of directives. Return the server blocks (which
  13. // you may modify, if desired) and an error, if any.
  14. // The first argument is the name or path to the
  15. // configuration file (Caddyfile).
  16. //
  17. // This function can be a no-op and simply return its
  18. // input if there is nothing to do here.
  19. InspectServerBlocks(string, []caddyfile.ServerBlock) ([]caddyfile.ServerBlock, error)
  20. // This is what Caddy calls to make server instances.
  21. // By this time, all directives have been executed and,
  22. // presumably, the context has enough state to produce
  23. // server instances for Caddy to start.
  24. MakeServers() ([]Server, error)
  25. }

他在 Caddy.go 中 被 startWithListenerFds(cdyfile Input, inst *Instance, restartFds map[string]restartTriple) error 调用

用来创建服务器们

  1. slist, err := inst.context.MakeServers()

  1. ![image.png](https://cdn.nlark.com/yuque/0/2019/png/176280/1569484034159-97eb9da7-82b5-4e73-9049-2669aa9fb830.png#align=left&display=inline&height=772&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1120&originWidth=720&size=121316&status=done&style=none&width=496)