Caddy源码阅读(一)Run详解

前言

本次系列会讲解 caddy 整个生命周期涉及到的源码。

平时我们使用 caddy 都是使用 它的 二进制 分发文件,现在来分析 caddy 的 Run 函数。
从最外层逻辑看它都做了些什么。

Caddy Run

我们来看看 Caddy Run 中引入了哪些包和操作,对 Caddy 的总体行为做一个概览
caddy/caddymain/run.go
首先看 init 函数

func init() {caddy.TrapSignals()flag.BoolVar(&certmagic.Default.Agreed, "agree", false, "Agree to the CA's Subscriber Agreement")flag.StringVar(&certmagic.Default.CA, "ca", certmagic.Default.CA, "URL to certificate authority's ACME server directory")flag.StringVar(&certmagic.Default.DefaultServerName, "default-sni", certmagic.Default.DefaultServerName, "If a ClientHello ServerName is empty, use this ServerName to choose a TLS certificate")flag.BoolVar(&certmagic.Default.DisableHTTPChallenge, "disable-http-challenge", certmagic.Default.DisableHTTPChallenge, "Disable the ACME HTTP challenge")flag.BoolVar(&certmagic.Default.DisableTLSALPNChallenge, "disable-tls-alpn-challenge", certmagic.Default.DisableTLSALPNChallenge, "Disable the ACME TLS-ALPN challenge")flag.StringVar(&certmagic.Default.Email, "email", "", "Default ACME CA account email address")flag.DurationVar(&certmagic.HTTPTimeout, "catimeout", certmagic.HTTPTimeout, "Default ACME CA HTTP timeout")flag.StringVar(&revoke, "revoke", "", "Hostname for which to revoke the certificate")flag.StringVar(&disabledMetrics, "disabled-metrics", "", "Comma-separated list of telemetry metrics to disable")flag.StringVar(&conf, "conf", "", "Caddyfile to load (default \""+caddy.DefaultConfigFile+"\")")flag.StringVar(&cpu, "cpu", "100%", "CPU cap")flag.BoolVar(&printEnv, "env", false, "Enable to print environment variables")flag.StringVar(&envFile, "envfile", "", "Path to file with environment variables to load in KEY=VALUE format")flag.BoolVar(&fromJSON, "json-to-caddyfile", false, "From JSON stdin to Caddyfile stdout")flag.BoolVar(&toJSON, "caddyfile-to-json", false, "From Caddyfile stdin to JSON stdout")flag.BoolVar(&plugins, "plugins", false, "List installed plugins")flag.StringVar(&logfile, "log", "", "Process log file")flag.IntVar(&logRollMB, "log-roll-mb", 100, "Roll process log when it reaches this many megabytes (0 to disable rolling)")flag.BoolVar(&logRollCompress, "log-roll-compress", true, "Gzip-compress rolled process log files")flag.StringVar(&caddy.PidFile, "pidfile", "", "Path to write pid file")flag.BoolVar(&caddy.Quiet, "quiet", false, "Quiet mode (no initialization output)")flag.StringVar(&serverType, "type", "http", "Type of server to run")flag.BoolVar(&version, "version", false, "Show version")flag.BoolVar(&validate, "validate", false, "Parse the Caddyfile but do not start the server")caddy.RegisterCaddyfileLoader("flag", caddy.LoaderFunc(confLoader))caddy.SetDefaultCaddyfileLoader("default", caddy.LoaderFunc(defaultLoader))
}

然后我们来看 Run() 函数 Run() 函数全文可在最下看到

conf 的灵活设置

conf 用来设置 Caddyfile 的文件路径,是可以从 Stdin 即终端输入配置的。
会在以下两个在 init() 中的函数调用更改

    caddy.RegisterCaddyfileLoader("flag", caddy.LoaderFunc(confLoader))caddy.SetDefaultCaddyfileLoader("default", caddy.LoaderFunc(defaultLoader))

而注意到这里使用的 Loader ,可以用来自定义 caddyfile 的装载方式。

// confLoader loads the Caddyfile using the -conf flag.
func confLoader(serverType string) (caddy.Input, error) {if conf == "" {return nil, nil}if conf == "stdin" {return caddy.CaddyfileFromPipe(os.Stdin, serverType)}var contents []byteif strings.Contains(conf, "*") {// Let caddyfile.doImport logic handle the globbed pathcontents = []byte("import " + conf)} else {var err errorcontents, err = ioutil.ReadFile(conf)if err != nil {return nil, err}}return caddy.CaddyfileInput{Contents:       contents,Filepath:       conf,ServerTypeName: serverType,}, nil
}

注意到这里返回的 caddy.Input 类型,它代表 caddyfile 在程序中的 structure

Log

log 设置 日志 输出在什么地方,log-roll-mb log-roll-compress 是设置 log 文件大小限制,达到限制的时候会放弃旧的日志。还有 文件的压缩选项

    // Set up process log before anything bad happensswitch logfile {case "stdout":log.SetOutput(os.Stdout)case "stderr":log.SetOutput(os.Stderr)case "":log.SetOutput(ioutil.Discard)default:if logRollMB > 0 {log.SetOutput(&lumberjack.Logger{Filename:   logfile,MaxSize:    logRollMB,MaxAge:     14,MaxBackups: 10,Compress:   logRollCompress,})} else {err := os.MkdirAll(filepath.Dir(logfile), 0755)if err != nil {mustLogFatalf("%v", err)}f, err := os.OpenFile(logfile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)if err != nil {mustLogFatalf("%v", err)}// don't close file; log should be writeable for duration of processlog.SetOutput(f)}}

证书处理

我们看前 8 个 flag 选项,都是设定 TLS 配置的。使用的是 CertMagic ,同时作者也把它放出来可以被其他的 Go 语言程序集成,如果想要为自己的 Go 程序添加 TLS 加密传输,就使用它吧。他还支持自动续期,本身是使用的 ACME 客户端集成。
本身大部分不需要设置,会使用默认设置帮助你启用 HTTPS 。
介绍一下 revoke 选项,这里会调用 CertMagic 的 Certificate revocation (please, only if private key is compromised)来撤销证书

// Check for one-time actionsif revoke != "" {err := caddytls.Revoke(revoke)if err != nil {mustLogFatalf("%v", err)}fmt.Printf("Revoked certificate for %s\n", revoke)os.Exit(0)}

telemetry

然后是 disabled-metrics 选项,用来关闭一些不需要的 遥测指标
注意到这里的 initTelemetry() 函数,其中会使用 这里输入的选项进行遥测的关闭。

// initialize telemetry clientif EnableTelemetry {err := initTelemetry()if err != nil {mustLogFatalf("[ERROR] Initializing telemetry: %v", err)}} else if disabledMetrics != "" {mustLogFatalf("[ERROR] Cannot disable specific metrics because telemetry is disabled")}

然后会在 Run() 的最后部分进行相应遥测模块的 设置

// Begin telemetry (these are no-ops if telemetry disabled)telemetry.Set("caddy_version", module.Version)telemetry.Set("num_listeners", len(instance.Servers()))telemetry.Set("server_type", serverType)telemetry.Set("os", runtime.GOOS)telemetry.Set("arch", runtime.GOARCH)telemetry.Set("cpu", struct {BrandName  string `json:"brand_name,omitempty"`NumLogical int    `json:"num_logical,omitempty"`AESNI      bool   `json:"aes_ni,omitempty"`}{BrandName:  cpuid.CPU.BrandName,NumLogical: runtime.NumCPU(),AESNI:      cpuid.CPU.AesNi(),})if containerized := detectContainer(); containerized {telemetry.Set("container", containerized)}telemetry.StartEmitting()

环境设置

然后是环境变量的设置 envenvfile,分别是打印出环境信息和可以设置环境变量文件的位置

// load all additional envs as soon as possibleif err := LoadEnvFromFile(envFile); err != nil {mustLogFatalf("%v", err)}if printEnv {for _, v := range os.Environ() {fmt.Println(v)}}

cpu

cpu 设定 cpu 使用限制

    // Set CPU caperr := setCPU(cpu)if err != nil {mustLogFatalf("%v", err)}

分发事件

这是用来启用插件的。

    // Executes Startup eventscaddy.EmitEvent(caddy.StartupEvent, nil)

服务器类型

type 设定 caddy 启动的服务器类型,这是因为 caddy 能启动的服务器很多,只要满足了 Server 的接口就可以使用 caddy。
这里调用的 LoadCaddyfile 可以认为是 Caddy 配置最重要的部分。

    // Get Caddyfile inputcaddyfileinput, err := caddy.LoadCaddyfile(serverType)if err != nil {mustLogFatalf("%v", err)}

pidfile 设置写入 pid 文件的路径, quiet 设置 caddy 不输出初始化信息的启动 。

version 和 plugin 信息

version 用来显示 caddy 版本,plugins 用来列出已经安装的 插件

    if version {if module.Sum != "" {// a build with a known version will also have a checksumfmt.Printf("Caddy %s (%s)\n", module.Version, module.Sum)} else {fmt.Println(module.Version)}os.Exit(0)}if plugins {fmt.Println(caddy.DescribePlugins())os.Exit(0)}

validate 验证 caddyfile 文件

validate 是验证 caddyfile 的可行性,他会检测 caddyfile 但是不会启动一个新的 Server。注意到在这里调用了 os.Exit(0) 退出。

    if validate {err := caddy.ValidateAndExecuteDirectives(caddyfileinput, nil, true)if err != nil {mustLogFatalf("%v", err)}msg := "Caddyfile is valid"fmt.Println(msg)log.Printf("[INFO] %s", msg)os.Exit(0)}

caddyfile 与 JSON

json-to-caddyfilecaddyfile-to-json 从 JSON 文件中读取 caddyfile 的配置文件,或者将 caddyfile 文件输出为 JSON 格式。

    // Check if we just need to do a Caddyfile Convert and exitcheckJSONCaddyfile()

根据 flag 选项进行 Caddyfile  和 JSON 直接的转换。

func checkJSONCaddyfile() {if fromJSON {jsonBytes, err := ioutil.ReadAll(os.Stdin)if err != nil {fmt.Fprintf(os.Stderr, "Read stdin failed: %v", err)os.Exit(1)}caddyfileBytes, err := caddyfile.FromJSON(jsonBytes)if err != nil {fmt.Fprintf(os.Stderr, "Converting from JSON failed: %v", err)os.Exit(2)}fmt.Println(string(caddyfileBytes))os.Exit(0)}if toJSON {caddyfileBytes, err := ioutil.ReadAll(os.Stdin)if err != nil {fmt.Fprintf(os.Stderr, "Read stdin failed: %v", err)os.Exit(1)}jsonBytes, err := caddyfile.ToJSON(caddyfileBytes)if err != nil {fmt.Fprintf(os.Stderr, "Converting to JSON failed: %v", err)os.Exit(2)}fmt.Println(string(jsonBytes))os.Exit(0)}
}

Start

启动服务器

    // Start your enginesinstance, err := caddy.Start(caddyfileinput)if err != nil {mustLogFatalf("%v", err)}

总结

看完 caddy 的 run 函数,了解了程序的启动配置了哪些东西,相应的会有一些问题。带着这些问题继续看源码,能够深入了解 caddy 内的逻辑。

  • caddy.Input 的内在读取 caddyfile 的操作
  • Loader 可以看出也是可以自定义的,他实际涉及到 Caddy 较为重要的 接收配置,安装配置的操作部分。它会读取 token 给 plugin 配置时 给 controller 消费,这里多的名词是来自于 caddy 的配置部分
  • caddy.EmitEvent 是怎样启动各 caddy 插件的
  • caddy.ServerType 除了 HTTP 还有什么(插件会对应相应的服务器的)
  • instance 可以看出是 caddy 服务器的实例,它具体的设置逻辑是什么来获得 插件装配的能力的。他是怎样作为服务器服务的
  • 遥测模块的接入(caddy 2已经不用了)
  • 其他的就是一些方便的选项了。

附件

Run() 函数源码

// Run is Caddy's main() function.
func Run() {flag.Parse()module := getBuildModule()cleanModVersion := strings.TrimPrefix(module.Version, "v")caddy.AppName = appNamecaddy.AppVersion = module.Versioncertmagic.UserAgent = appName + "/" + cleanModVersion// Set up process log before anything bad happensswitch logfile {case "stdout":log.SetOutput(os.Stdout)case "stderr":log.SetOutput(os.Stderr)case "":log.SetOutput(ioutil.Discard)default:if logRollMB > 0 {log.SetOutput(&lumberjack.Logger{Filename:   logfile,MaxSize:    logRollMB,MaxAge:     14,MaxBackups: 10,Compress:   logRollCompress,})} else {err := os.MkdirAll(filepath.Dir(logfile), 0755)if err != nil {mustLogFatalf("%v", err)}f, err := os.OpenFile(logfile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)if err != nil {mustLogFatalf("%v", err)}// don't close file; log should be writeable for duration of processlog.SetOutput(f)}}// load all additional envs as soon as possibleif err := LoadEnvFromFile(envFile); err != nil {mustLogFatalf("%v", err)}if printEnv {for _, v := range os.Environ() {fmt.Println(v)}}// initialize telemetry clientif EnableTelemetry {err := initTelemetry()if err != nil {mustLogFatalf("[ERROR] Initializing telemetry: %v", err)}} else if disabledMetrics != "" {mustLogFatalf("[ERROR] Cannot disable specific metrics because telemetry is disabled")}// Check for one-time actionsif revoke != "" {err := caddytls.Revoke(revoke)if err != nil {mustLogFatalf("%v", err)}fmt.Printf("Revoked certificate for %s\n", revoke)os.Exit(0)}if version {if module.Sum != "" {// a build with a known version will also have a checksumfmt.Printf("Caddy %s (%s)\n", module.Version, module.Sum)} else {fmt.Println(module.Version)}os.Exit(0)}if plugins {fmt.Println(caddy.DescribePlugins())os.Exit(0)}// Check if we just need to do a Caddyfile Convert and exitcheckJSONCaddyfile()// Set CPU caperr := setCPU(cpu)if err != nil {mustLogFatalf("%v", err)}// Executes Startup eventscaddy.EmitEvent(caddy.StartupEvent, nil)// Get Caddyfile inputcaddyfileinput, err := caddy.LoadCaddyfile(serverType)if err != nil {mustLogFatalf("%v", err)}if validate {err := caddy.ValidateAndExecuteDirectives(caddyfileinput, nil, true)if err != nil {mustLogFatalf("%v", err)}msg := "Caddyfile is valid"fmt.Println(msg)log.Printf("[INFO] %s", msg)os.Exit(0)}// Log Caddy version before startlog.Printf("[INFO] Caddy version: %s", module.Version)// Start your enginesinstance, err := caddy.Start(caddyfileinput)if err != nil {mustLogFatalf("%v", err)}// Begin telemetry (these are no-ops if telemetry disabled)telemetry.Set("caddy_version", module.Version)telemetry.Set("num_listeners", len(instance.Servers()))telemetry.Set("server_type", serverType)telemetry.Set("os", runtime.GOOS)telemetry.Set("arch", runtime.GOARCH)telemetry.Set("cpu", struct {BrandName  string `json:"brand_name,omitempty"`NumLogical int    `json:"num_logical,omitempty"`AESNI      bool   `json:"aes_ni,omitempty"`}{BrandName:  cpuid.CPU.BrandName,NumLogical: runtime.NumCPU(),AESNI:      cpuid.CPU.AesNi(),})if containerized := detectContainer(); containerized {telemetry.Set("container", containerized)}telemetry.StartEmitting()// Twiddle your thumbsinstance.Wait()
}

拓展阅读

caddy(二)启动流程
Caddy源码阅读(三)Caddyfile 解析 by Loader & Parser
Caddy源码阅读(四)Plugin & Controller 安装插件
Caddy源码阅读(五) Instance & Server
caddy-plugins(一)自定义插件
caddy-plugins(二)caddy-grpc 一个插件实例

转载于:https://www.cnblogs.com/abser/p/11385924.html

Caddy源码阅读(一)Run详解相关推荐

  1. 源码阅读及理论详解《 Informer: Beyond Efficient Transformer for Long Sequence Time-Series Forecasting 》

    Informer论文:https://arxiv.org/pdf/2012.07436.pdf Informer源码:GitHub - zhouhaoyi/Informer2020: The GitH ...

  2. Linux内核源码阅读以及工具详解

    接上篇Linux内核源码下载方法 这篇总结了如何利用source insight对Linux内核代码进行阅读和学习(资料来源于网络) 随着linux的逐步普及,现在有不少人对于Linux的安装及设置已 ...

  3. Xposed源码剖析——app_process作用详解

    Xposed源码剖析--app_process作用详解 首先吐槽一下CSDN的改版吧,发表这篇文章之前其实我已经将此篇文章写过了两三次了.就是发表不成功.而且CSDN将我的文章草稿也一带>删除掉 ...

  4. mysql data文件夹恢复_【专注】Zabbix源码安装教程—步骤详解(2)安装并配置mysql...

    四.安装并配置mysql(1) 解压mysql-5.7.26.tar.gz与boost_1_59_0.tar.gz #tar -xvf mysql-5.7.26.tar.gz #tar -xvf bo ...

  5. php+mysql案例含源码_【专注】Zabbix源码安装教程—步骤详解(1)安装前准备

    一.实验环境准备 Rhel 7.6 x86_64(server) 192.168.163.72 Rhel 6.5 x86_64(agent) 192.168.163.61 均已配置操作安装光盘为YUM ...

  6. React 源码系列 | React Context 详解

    目前来看 Context 是一个非常强大但是很多时候不会直接使用的 api.大多数项目不会直接使用 createContext 然后向下面传递数据,而是采用第三方库(react-redux). 想想项 ...

  7. dockerfile源码安装mysql_docker容器详解五: dockerfile实现tomcat环境以及源码安装mysql...

    tomcat 上一节讲到了dockerfile的基础,这一次咱们来作一个小的练习 首先要了解tomcat安装的整个过程 首先搭建 jdk环境: 下载jdk包,解压以后添加环境变量 而后搭建tomcat ...

  8. Caddy 源码阅读

    Caddy Caddy 是一个go编写的轻量配置化web server. 类似于nginx.有丰富的插件,配置也很简单,自定义插件也很容易.更人性化. 官网上是这么介绍的:Caddy is the H ...

  9. 未能找到元数据文件_Flink 源码:Checkpoint 元数据详解

    本文是 Flink 源码解析系列,通过阅读本文你能 get 到以下点: Flink 任务从 Checkpoint 处恢复流程概述 Checkpoint 元数据详解 从源码层分析:JM 该如何合理地给每 ...

最新文章

  1. Tensorflow_yolov3 Intel Realsense D435奇怪的现象,多摄像头连接时一旦能检测到深度马上就会卡(卡住)
  2. java与C++实现判断闰年(百练OJ:2733:判断闰年)
  3. 【TensorFlow】笔记1:入门笔记
  4. Hashtable学习笔记
  5. php 上传sb2,基于标准的http来实现Android多文件上传
  6. 神经网络的Dropout正则化
  7. PGIS大数据量点位显示方案
  8. CVE-2019-0708 微软补丁更新
  9. Python手动安装Jieba库(Win11)
  10. 条形码的码制分类详解
  11. 小程序微信登陆及账号绑定功能开发笔记
  12. 轻松禁止自动更新FLASH插件有绝招
  13. matplotlib如何绘制圆
  14. 前端实现图片验证码效果(数字和字母)
  15. Windows系统下CMD添加删除用户、用户组
  16. C2039 Error: WriteHuge : is not a member of CFile
  17. 阿米洛键盘失灵_改变静电容键盘手感单一限制,阿米洛静电容机械轴V2上手
  18. 动漫免费在线观看,实时更新,资源全
  19. 应用统计学与计算机论文,应用统计硕士论文范文
  20. Python爬取网易云音乐歌单名以及歌单标签

热门文章

  1. 通过DIVA了解APP安全问题
  2. 一个金融行业站SEO优化方案分析
  3. 直接分享5个T的网盘资源
  4. 云服务卸载MySQL
  5. zt_阿里张瑞jacky关于library cache的内部管理机制
  6. html调用外部js报错onClick is not defined at HTMLButtonElement.onclick
  7. Github 配置SSH key 下载项目并关联
  8. thingsboard 编译失败,报错 Failed to execute goal com.mycila:license-maven-plugin:3.0
  9. 浅谈 MySQL 连表查询
  10. 计算机程序设计员(java三级)应注意什么?