Caddy 源码阅读

Caddy

Caddy 是一个go编写的轻量配置化web server。
类似于nginx。有丰富的插件,配置也很简单,自定义插件也很容易。更人性化。
官网上是这么介绍的:Caddy is the HTTP/2 web server with automatic HTTPS.
(说实话官网v1版本的介绍并不怎么清楚,反而是v2版本的介绍更明确)

Caddy官方文档: https://caddyserver.com/v1/tutorial
GitHub地址: https://github.com/caddyserver/caddy

Caddy的功能

官方首页盗来的图
TLS证书的续订 : TLS certificate renewal。
Caddy可以通过ACME协议(Let’s Encrypt 为了实现自动化证书管理,制订了 ACME 协议)和Let’s Encrypt(一个免费、开放、自动化的数字证书认证机构)进行证书的签发、续订等。这也是官方介绍的automatic HTTPS.
(这个功能尝试了一次因为网络timeout,就没有继续研究。等有需求的时候,再尝试吧。)

OCSP装订: OCSP Staplin。
在线证书状态协议(Online Certificate Status )Protocol),简称 OCSP,是一个用于获取 X.509 数字证书撤销状态的网际协议。 Web 服务端将主动获取 OCSP 查询结果,并随证书一起发送给客户端,以此让客户端跳过自己去寻求验证的过程,提高 TLS 握手效率。

静态文件服务器: static file serving。
这个可以用来进行文件管理、文件上传、基于 MarkDown 的博客系统等等。只需简单的配置。附链接:Caddy服务器搭建和实现文件共享
(这个值得尝试,之后打算用caddy搭建一个MarkDown的博客玩玩)

反向代理
这个和nginx的功能一样。什么叫反向代理,什么叫正向代理,请看我的这篇博客:反向代理和正向代理

Kubernetes入口 : Kubernetes Ingress.
k8s集群的网络入口。这个是要和traefik抢工作啊。(还是习惯使用treafik,不打算尝试caddy)。

自定义中间件
这个可nginx可以写lua插件一样,caddy也支持写插件。是用golang写,得益于caddy代码结构的组织,在caddy源码基础上扩展很容易。(TODO: 下一篇文章写怎么给caddy添加插件)

自定义服务器(ServerType)
Caddy本身是一个http服务器(const serverType = "http") ,但是通过扩展ServerType可以变成 SSH、SFTP、TCP等等,教科书一样的典范是DNS服务器CoreDNS

Caddy代码目录

下面是caddy项目源码的目录,去除了相关文档文件、test文件等。

directives 指令: 比如log、limits、proxy都是指令。

.
├── access.log          // 访问日志
├── assets.go           // 工具方法,用来获取环境变量CADDYPATH和用户目录的路径
├── caddy
│   ├── caddymain
│   │   ├── run.go          // caddy服务启动入口,解析参数、日志输出、读取Caddyfile\设置cpu等等。
│   ├── main.go           // 程序入口, main函数
├── caddy.go            // 定义了caddy服务器相关概念和接口     
├── caddyfile           // caddy的配置文件caddyfile的解析和使用
│   ├── dispenser.go      // 定义了一系列方便使用配置里Token的方法,如Next、NextBlock、NextLine等
│   ├── json.go           // caddyfile同样支持json,两种形式可以用caddy相互转换
│   ├── lexer.go          // 词法分析器
│   ├── parse.go          // 读入配置文件并使用lexer进行解析
├── caddyhttp           // caddy的http服务器
│   ├── caddyhttp.go      // 用来加载插件,import了所有caddy http服务器相关的指令(中间件)
│   ├── httpserver
│   │   ├── error.go        // 定义了常见的错误,都实现了error接口
│   │   ├── https.go        // 处理https相关逻辑
│   │   ├── logger.go       // 日志相关逻辑
│   │   ├── plugin.go       // httpserver插件逻辑,定义了一个directives的字符串slice,自定义插件时,这里要改!!
│   │   ├── server.go       // HTTP server的实现,包裹了一层标准库的http.Server
│   │   ├── ...
│   ├── bind
│   ├── browse
│   ├── errors
│   ├── basicauth
│   ├── ...
├── caddytls             // caddy tls 相关逻辑,不影响主要流程先不看。
├── commands.go          // 命令行终端命令的处理逻辑,处理终端执行的时候加入的参数。
├── controller.go        // 用于从配置caddyfile中的配置来设置directive
├── onevent              // 插件,on在触发指定事件时执行命令。举个栗子:在服务器启动时,启动php-fpm。
├── plugins.go           // 维护了caddy的所有插件,event hook等
├── rlimit_nonposix.go
├── rlimit_posix.go      // 启动服务器的时候,如果文件句柄数限制过低就提醒你设置ulimits
├── sigtrap.go           // 信号机关,用来处理信号,如中断、挂起等。
├── sigtrap_nonposix.go
├── sigtrap_posix.go
├── telemetry            // 遥测,就是监控。个人觉得使用prometheus的exporter做更好。
└── upgrade.go           // 热更新

Caddy 启动过程

main 入口

caddy/main.go

package main

import "github.com/caddyserver/caddy/caddy/caddymain"

var run = caddymain.Run // replaced for tests

func main() {
   
	run()
}

main函数很简单,引用了caddmain包,调用了caddy/caddymain/run.go的run函数。下面主要看run函数怎么去启动服务器的:

run函数

run函数代码比较长,首先大致看下run.go文件里有哪些东西。包内函数(首字母小写的)先忽略,因为最能提现一个包的主要职责的是它的import、包内变量、init函数、导出函数(首字母大写的函数)、导出结构体(首字母大写的结构体)。

package caddymain

import (
	...

	_ "github.com/caddyserver/caddy/caddyhttp" // plug in the HTTP server type
	// This is where other plugins get plugged in (imported)
)

const appName = "Caddy"    // 这里定义了app的名字。

// Flags that control program flow or startup
// 定义了程序启动的一些参数,这些参数从运行时指定(从os.Args中解析)。
// 这里定义的是程序启动后,就不能更改的。 配置文件中的参数是程序启动后,还可以热更新的。
var (
	serverType      string
	conf            string
	cpu             string
	envFile         string
	...
)

// EnableTelemetry defines whether telemetry is enabled in Run.
var EnableTelemetry = true    // 遥测启动开关,不是重点,忽略。

func init() {
   ...} 

// Run is Caddy's main() function.
func Run() {
   ...}

根据执行顺序来看,main包import了caddymain这个包,且调用了caddymain.Run。那么执行步骤如下:

  1. import caddymain包的相关依赖,这里需要看看import却没有使用的caddyhttp包做了什么操作。
    在这里插入图片描述
    可以看到caddyhttp包都是在import其他的包, 这些被caddyhttp import的包,就是caddy官方自带的相关插件,和httpserver这个server plugin。
    稍后再看plugin的具体实现。
  2. 初始化caddymain包中的包内变量。
  3. 执行caddymain包的init函数。
    不管什么包,init函数的作用都很明确:初始化相关包内变量,读取相关配置、参数等等。caddymain的init函数也不例外。
    func init() {
         
        caddy.TrapSignals()     // 捕捉信号量,处理
    	// 解析启动参数,flag包是从os.Args中解析的。
        flag.BoolVar(&certmagic.Default.Agreed, "agree", false, "Agree to the CA's Subscriber Agreement")
    	flag.StringVar(&certmagic.Default.CA, "ca", certmagic.Default
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值