6.1 Caddy插件系统概述

6.1.1 插件系统架构

Caddy采用模块化架构,通过插件系统提供强大的扩展能力。插件系统的特点:

  1. 模块化设计:每个功能都是独立的模块
  2. 热插拔支持:可以动态加载和卸载插件
  3. 类型安全:Go语言的类型系统确保插件安全
  4. 性能优化:编译时优化,运行时高效
  5. 丰富生态:社区提供大量第三方插件

6.1.2 插件类型

  1. HTTP中间件:处理HTTP请求和响应
  2. DNS提供商:用于DNS验证的ACME挑战
  3. 存储后端:证书和配置存储
  4. 日志编写器:自定义日志输出
  5. 认证提供商:身份验证和授权
  6. 缓存后端:缓存存储和管理
  7. 监控插件:指标收集和监控

6.1.3 插件管理

# 查看已安装的插件
caddy list-modules

# 查看特定类型的插件
caddy list-modules --type http.handlers

# 查看插件版本信息
caddy version

# 构建包含特定插件的Caddy
xcaddy build --with github.com/caddyserver/cache-handler

6.2 常用官方插件

6.2.1 文件服务器插件

# 基本文件服务器
example.com {
    file_server {
        root /var/www/html
        index index.html index.htm
        browse
        hide .git .env
        precompressed gzip br
    }
}

# 高级文件服务器配置
example.com {
    file_server {
        root /var/www/html
        
        # 自定义错误页面
        status 404 /404.html
        status 403 /403.html
        
        # 文件过滤
        hide *.log *.tmp .DS_Store
        
        # 目录浏览模板
        browse {
            template browse.html
        }
    }
}

6.2.2 反向代理插件

# 基本反向代理
example.com {
    reverse_proxy localhost:8080 {
        # 负载均衡
        lb_policy round_robin
        
        # 健康检查
        health_uri /health
        health_interval 30s
        health_timeout 5s
        
        # 请求修改
        header_up X-Real-IP {remote_host}
        header_up X-Forwarded-Proto {scheme}
        
        # 响应修改
        header_down -Server
        header_down X-Powered-By "Caddy"
    }
}

# 高级代理配置
example.com {
    reverse_proxy localhost:8080 localhost:8081 {
        # 会话保持
        lb_policy ip_hash
        
        # 故障转移
        fail_duration 30s
        max_fails 3
        
        # 传输配置
        transport http {
            dial_timeout 10s
            response_header_timeout 30s
            keep_alive 30s
            compression off
        }
        
        # 重试配置
        @retry {
            status 502 503 504
        }
        handle_response @retry {
            reverse_proxy localhost:8082
        }
    }
}

6.2.3 重定向插件

# 基本重定向
example.com {
    # 简单重定向
    redir /old-page /new-page 301
    
    # 模式重定向
    redir /blog/* /posts/{uri} 302
    
    # 条件重定向
    @mobile {
        header User-Agent *Mobile*
    }
    redir @mobile /mobile{uri}
    
    file_server
}

# 高级重定向
example.com {
    # 基于查询参数的重定向
    @old_search {
        query q=*
    }
    redir @old_search /search?query={query.q}
    
    # 基于主机的重定向
    @www {
        host www.example.com
    }
    redir @www https://example.com{uri} 301
    
    # 正则表达式重定向
    redir /user/([0-9]+) /users/{re.1} 301
    
    file_server
}

6.2.4 重写插件

# URL重写
example.com {
    # 简单重写
    rewrite /api/v1/* /api/v2/*
    
    # 条件重写
    @api {
        path /api/*
        method GET
    }
    rewrite @api /rest{uri}
    
    # 正则表达式重写
    rewrite ^/user/([0-9]+)$ /users.php?id=$1
    
    # 查询参数重写
    rewrite /search /search.php?q={query}
    
    file_server
}

# 高级重写
example.com {
    # 多条件重写
    @rewrite {
        path /old-api/*
        header Accept application/json
    }
    rewrite @rewrite /new-api{path_regexp ^/old-api(.*)$ $1}
    
    # 基于时间的重写
    @maintenance {
        expression {time.now.hour} >= 2 && {time.now.hour} <= 4
    }
    rewrite @maintenance /maintenance.html
    
    file_server
}

6.3 第三方插件

6.3.1 缓存插件

# 安装缓存插件
xcaddy build --with github.com/caddyserver/cache-handler
# 基本缓存配置
example.com {
    cache {
        # 缓存键
        key {method} {host} {path} {query}
        
        # 缓存时间
        ttl 1h
        stale_ttl 24h
        
        # 缓存条件
        match {
            status 200
            header Content-Type text/html
        }
        
        # 缓存后端
        redis {
            host localhost:6379
            password {env.REDIS_PASSWORD}
            db 0
        }
    }
    
    reverse_proxy localhost:8080
}

# 高级缓存配置
example.com {
    # 不同内容的缓存策略
    @static {
        path *.css *.js *.png *.jpg *.gif
    }
    
    cache @static {
        key {method} {host} {path}
        ttl 24h
        stale_ttl 168h
    }
    
    @api {
        path /api/*
        method GET
    }
    
    cache @api {
        key {method} {host} {path} {query} {header.Authorization}
        ttl 5m
        stale_ttl 1h
        vary Accept-Encoding Authorization
    }
    
    # 缓存清除
    @purge {
        method PURGE
    }
    
    handle @purge {
        cache_purge {path}
        respond "Cache purged" 200
    }
    
    reverse_proxy localhost:8080
}

6.3.2 限流插件

# 安装限流插件
xcaddy build --with github.com/mholt/caddy-ratelimit
# 基本限流
example.com {
    rate_limit {
        zone general
        key {remote_host}
        rate 100r/m
        window 1m
    }
    
    file_server
}

# 高级限流配置
example.com {
    # 基于用户的限流
    @authenticated {
        header Authorization *
    }
    
    rate_limit @authenticated {
        zone users
        key {http.request.header.X-User-ID}
        rate 1000r/h
        window 1h
        response_code 429
        response_body "Rate limit exceeded for user"
    }
    
    # 基于IP的限流
    rate_limit not @authenticated {
        zone anonymous
        key {remote_host}
        rate 100r/h
        window 1h
    }
    
    # API端点的特殊限流
    @api {
        path /api/*
    }
    
    rate_limit @api {
        zone api
        key {remote_host}
        rate 300r/h
        window 1h
        burst 50
    }
    
    # 登录端点的严格限流
    @login {
        path /auth/login
        method POST
    }
    
    rate_limit @login {
        zone login
        key {remote_host}
        rate 5r/m
        window 5m
        response_code 429
        response_body `{"error": "Too many login attempts"}`
    }
    
    reverse_proxy localhost:8080
}

6.3.3 JWT认证插件

# 安装JWT插件
xcaddy build --with github.com/greenpau/caddy-security
# JWT认证配置
example.com {
    # JWT中间件
    jwt {
        primary yes
        
        # JWT密钥
        trusted_tokens {
            static_secret {env.JWT_SECRET}
        }
        
        # 或使用RSA公钥
        # trusted_tokens {
        #     rsa_file /path/to/public.pem
        # }
        
        # 认证URL
        auth_url /auth/login
        forbidden_url /auth/forbidden
        
        # 令牌位置
        token_sources {
            header Authorization
            query_string access_token
            cookie jwt_token
        }
        
        # 用户声明
        user_claims {
            username sub
            roles groups
            email email
        }
    }
    
    # 受保护的路径
    @protected {
        path /admin/* /api/private/*
    }
    
    # 需要特定角色
    @admin_only {
        path /admin/*
        expression {http.jwt.claims.roles} contains "admin"
    }
    
    handle @admin_only {
        file_server {
            root /var/www/admin
        }
    }
    
    handle @protected {
        reverse_proxy localhost:8080 {
            header_up X-User-ID {http.jwt.claims.sub}
            header_up X-User-Roles {http.jwt.claims.roles}
        }
    }
    
    # 公开路径
    file_server
}

6.3.4 Prometheus监控插件

# 安装Prometheus插件
xcaddy build --with github.com/caddyserver/caddy/v2/modules/caddyhttp/metrics
# Prometheus监控
example.com {
    # 启用指标收集
    metrics /metrics {
        # 基本认证保护
        basicauth {
            prometheus $2a$14$Zkx19XLiW6VYouLHR5NmfOFU0z2GTNqq9qB6FY9gZKOOdOoKw6Uw.
        }
    }
    
    # 自定义指标
    @api_requests {
        path /api/*
    }
    
    handle @api_requests {
        # 增加API请求计数
        metrics_increment api_requests_total {
            labels {
                method {method}
                endpoint {path}
            }
        }
        
        reverse_proxy localhost:8080
    }
    
    # 错误指标
    @errors {
        status 4xx 5xx
    }
    
    handle_response @errors {
        metrics_increment http_errors_total {
            labels {
                status {status}
                method {method}
            }
        }
    }
    
    file_server
}

# 全局指标配置
{
    servers {
        metrics
    }
}

6.3.5 日志插件

# 安装高级日志插件
xcaddy build --with github.com/caddyserver/caddy/v2/modules/logging
# 高级日志配置
example.com {
    # 结构化日志
    log {
        output file /var/log/caddy/access.log {
            roll_size 100mb
            roll_keep 30
            roll_keep_for 720h
        }
        
        format json {
            time_format "2006-01-02T15:04:05.000Z07:00"
            message_key "msg"
            level_key "level"
            time_key "ts"
        }
        
        level INFO
        
        # 自定义字段
        include {
            http.request.method
            http.request.uri
            http.request.headers.User-Agent
            http.request.headers.X-Forwarded-For
            http.response.status
            http.response.size
            http.response.duration
        }
        
        # 排除字段
        exclude {
            http.request.headers.Authorization
            http.request.headers.Cookie
        }
    }
    
    # 错误日志
    log error {
        output file /var/log/caddy/error.log
        level ERROR
        format console
    }
    
    # 安全日志
    @security_events {
        status 403 429
    }
    
    log @security_events {
        output file /var/log/caddy/security.log
        format json
        level WARN
    }
    
    file_server
}

6.4 自定义插件开发

6.4.1 插件开发基础

// hello.go - 简单的Hello World插件
package hello

import (
    "fmt"
    "net/http"
    
    "github.com/caddyserver/caddy/v2"
    "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
    "github.com/caddyserver/caddy/v2/modules/caddyhttp"
)

func init() {
    caddy.RegisterModule(Hello{})
}

// Hello 插件结构
type Hello struct {
    Message string `json:"message,omitempty"`
}

// CaddyModule 返回模块信息
func (Hello) CaddyModule() caddy.ModuleInfo {
    return caddy.ModuleInfo{
        ID:  "http.handlers.hello",
        New: func() caddy.Module { return new(Hello) },
    }
}

// Provision 初始化插件
func (h *Hello) Provision(ctx caddy.Context) error {
    if h.Message == "" {
        h.Message = "Hello, World!"
    }
    return nil
}

// ServeHTTP 处理HTTP请求
func (h Hello) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
    w.Header().Set("Content-Type", "text/plain")
    fmt.Fprintf(w, "%s\n", h.Message)
    return nil
}

// UnmarshalCaddyfile 解析Caddyfile配置
func (h *Hello) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
    for d.Next() {
        if d.NextArg() {
            h.Message = d.Val()
        }
    }
    return nil
}

// 接口验证
var (
    _ caddy.Provisioner           = (*Hello)(nil)
    _ caddyhttp.MiddlewareHandler = (*Hello)(nil)
    _ caddyfile.Unmarshaler       = (*Hello)(nil)
)

6.4.2 中间件插件

// middleware.go - 自定义中间件插件
package middleware

import (
    "context"
    "net/http"
    "time"
    
    "github.com/caddyserver/caddy/v2"
    "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
    "github.com/caddyserver/caddy/v2/modules/caddyhttp"
    "go.uber.org/zap"
)

func init() {
    caddy.RegisterModule(RequestLogger{})
}

// RequestLogger 请求日志中间件
type RequestLogger struct {
    IncludeHeaders bool   `json:"include_headers,omitempty"`
    ExcludePaths   []string `json:"exclude_paths,omitempty"`
    
    logger *zap.Logger
}

// CaddyModule 返回模块信息
func (RequestLogger) CaddyModule() caddy.ModuleInfo {
    return caddy.ModuleInfo{
        ID:  "http.handlers.request_logger",
        New: func() caddy.Module { return new(RequestLogger) },
    }
}

// Provision 初始化插件
func (rl *RequestLogger) Provision(ctx caddy.Context) error {
    rl.logger = ctx.Logger(rl)
    return nil
}

// ServeHTTP 处理HTTP请求
func (rl RequestLogger) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
    // 检查是否需要排除此路径
    for _, path := range rl.ExcludePaths {
        if r.URL.Path == path {
            return next.ServeHTTP(w, r)
        }
    }
    
    start := time.Now()
    
    // 创建响应记录器
    rec := caddyhttp.NewResponseRecorder(w, nil, nil)
    
    // 处理请求
    err := next.ServeHTTP(rec, r)
    
    // 记录请求信息
    duration := time.Since(start)
    
    fields := []zap.Field{
        zap.String("method", r.Method),
        zap.String("path", r.URL.Path),
        zap.String("query", r.URL.RawQuery),
        zap.String("remote_addr", r.RemoteAddr),
        zap.String("user_agent", r.UserAgent()),
        zap.Int("status", rec.Status()),
        zap.Int64("size", rec.Size()),
        zap.Duration("duration", duration),
    }
    
    if rl.IncludeHeaders {
        headers := make(map[string][]string)
        for k, v := range r.Header {
            headers[k] = v
        }
        fields = append(fields, zap.Any("headers", headers))
    }
    
    rl.logger.Info("HTTP request", fields...)
    
    return err
}

// UnmarshalCaddyfile 解析Caddyfile配置
func (rl *RequestLogger) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
    for d.Next() {
        for d.NextBlock(0) {
            switch d.Val() {
            case "include_headers":
                rl.IncludeHeaders = true
            case "exclude_paths":
                rl.ExcludePaths = d.RemainingArgs()
            }
        }
    }
    return nil
}

// 接口验证
var (
    _ caddy.Provisioner           = (*RequestLogger)(nil)
    _ caddyhttp.MiddlewareHandler = (*RequestLogger)(nil)
    _ caddyfile.Unmarshaler       = (*RequestLogger)(nil)
)

6.4.3 DNS提供商插件

// dnsprovider.go - 自定义DNS提供商插件
package dnsprovider

import (
    "context"
    "fmt"
    "time"
    
    "github.com/caddyserver/caddy/v2"
    "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
    "github.com/libdns/libdns"
)

func init() {
    caddy.RegisterModule(Provider{})
}

// Provider 自定义DNS提供商
type Provider struct {
    APIKey    string `json:"api_key,omitempty"`
    APISecret string `json:"api_secret,omitempty"`
    BaseURL   string `json:"base_url,omitempty"`
    
    client *APIClient
}

// CaddyModule 返回模块信息
func (Provider) CaddyModule() caddy.ModuleInfo {
    return caddy.ModuleInfo{
        ID:  "dns.providers.custom",
        New: func() caddy.Module { return new(Provider) },
    }
}

// Provision 初始化提供商
func (p *Provider) Provision(ctx caddy.Context) error {
    if p.APIKey == "" {
        return fmt.Errorf("API key is required")
    }
    
    if p.BaseURL == "" {
        p.BaseURL = "https://api.example.com"
    }
    
    p.client = NewAPIClient(p.APIKey, p.APISecret, p.BaseURL)
    return nil
}

// AppendRecords 添加DNS记录
func (p *Provider) AppendRecords(ctx context.Context, zone string, records []libdns.Record) ([]libdns.Record, error) {
    var created []libdns.Record
    
    for _, record := range records {
        createdRecord, err := p.client.CreateRecord(ctx, zone, record)
        if err != nil {
            return created, fmt.Errorf("failed to create record %s: %w", record.Name, err)
        }
        created = append(created, createdRecord)
    }
    
    return created, nil
}

// DeleteRecords 删除DNS记录
func (p *Provider) DeleteRecords(ctx context.Context, zone string, records []libdns.Record) ([]libdns.Record, error) {
    var deleted []libdns.Record
    
    for _, record := range records {
        err := p.client.DeleteRecord(ctx, zone, record.ID)
        if err != nil {
            return deleted, fmt.Errorf("failed to delete record %s: %w", record.ID, err)
        }
        deleted = append(deleted, record)
    }
    
    return deleted, nil
}

// GetRecords 获取DNS记录
func (p *Provider) GetRecords(ctx context.Context, zone string) ([]libdns.Record, error) {
    return p.client.GetRecords(ctx, zone)
}

// SetRecords 设置DNS记录
func (p *Provider) SetRecords(ctx context.Context, zone string, records []libdns.Record) ([]libdns.Record, error) {
    // 先删除现有记录,再创建新记录
    existing, err := p.GetRecords(ctx, zone)
    if err != nil {
        return nil, err
    }
    
    _, err = p.DeleteRecords(ctx, zone, existing)
    if err != nil {
        return nil, err
    }
    
    return p.AppendRecords(ctx, zone, records)
}

// UnmarshalCaddyfile 解析Caddyfile配置
func (p *Provider) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
    for d.Next() {
        for d.NextBlock(0) {
            switch d.Val() {
            case "api_key":
                if !d.NextArg() {
                    return d.ArgErr()
                }
                p.APIKey = d.Val()
            case "api_secret":
                if !d.NextArg() {
                    return d.ArgErr()
                }
                p.APISecret = d.Val()
            case "base_url":
                if !d.NextArg() {
                    return d.ArgErr()
                }
                p.BaseURL = d.Val()
            }
        }
    }
    return nil
}

// APIClient DNS API客户端
type APIClient struct {
    apiKey    string
    apiSecret string
    baseURL   string
}

// NewAPIClient 创建新的API客户端
func NewAPIClient(apiKey, apiSecret, baseURL string) *APIClient {
    return &APIClient{
        apiKey:    apiKey,
        apiSecret: apiSecret,
        baseURL:   baseURL,
    }
}

// CreateRecord 创建DNS记录
func (c *APIClient) CreateRecord(ctx context.Context, zone string, record libdns.Record) (libdns.Record, error) {
    // 实现API调用逻辑
    // ...
    return record, nil
}

// DeleteRecord 删除DNS记录
func (c *APIClient) DeleteRecord(ctx context.Context, zone, recordID string) error {
    // 实现API调用逻辑
    // ...
    return nil
}

// GetRecords 获取DNS记录
func (c *APIClient) GetRecords(ctx context.Context, zone string) ([]libdns.Record, error) {
    // 实现API调用逻辑
    // ...
    return nil, nil
}

// 接口验证
var (
    _ caddy.Provisioner     = (*Provider)(nil)
    _ libdns.RecordAppender = (*Provider)(nil)
    _ libdns.RecordDeleter  = (*Provider)(nil)
    _ libdns.RecordGetter   = (*Provider)(nil)
    _ libdns.RecordSetter   = (*Provider)(nil)
    _ caddyfile.Unmarshaler = (*Provider)(nil)
)

6.4.4 插件构建和部署

# 创建插件模块
go mod init github.com/username/caddy-plugin

# 添加依赖
go get github.com/caddyserver/caddy/v2
go get github.com/caddyserver/caddy/v2/modules/caddyhttp

# 构建包含插件的Caddy
xcaddy build --with github.com/username/caddy-plugin

# 或者本地开发
xcaddy build --with github.com/username/caddy-plugin=./
# Dockerfile - 构建包含自定义插件的Caddy镜像
FROM caddy:builder AS builder

# 添加自定义插件
RUN xcaddy build \
    --with github.com/username/caddy-plugin \
    --with github.com/caddyserver/cache-handler \
    --with github.com/mholt/caddy-ratelimit

FROM caddy:latest

# 复制自定义构建的Caddy
COPY --from=builder /usr/bin/caddy /usr/bin/caddy

# 复制配置文件
COPY Caddyfile /etc/caddy/Caddyfile

# 暴露端口
EXPOSE 80 443

# 启动Caddy
CMD ["caddy", "run", "--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile"]

6.5 插件配置和使用

6.5.1 插件配置语法

# 基本插件使用
example.com {
    # 使用自定义插件
    hello "Welcome to my site!"
    
    # 使用中间件插件
    request_logger {
        include_headers
        exclude_paths /health /metrics
    }
    
    file_server
}

# 复杂插件配置
example.com {
    # 缓存插件
    cache {
        key {method} {host} {path} {query}
        ttl 1h
        stale_ttl 24h
        
        match {
            status 200
            header Content-Type text/html application/json
        }
        
        redis {
            host localhost:6379
            password {env.REDIS_PASSWORD}
            db 0
            pool_size 10
        }
    }
    
    # 限流插件
    rate_limit {
        zone api
        key {remote_host}
        rate 100r/m
        window 1m
        burst 20
        response_code 429
        response_body "Rate limit exceeded"
    }
    
    reverse_proxy localhost:8080
}

6.5.2 插件组合使用

# 多插件组合
example.com {
    # 请求日志
    request_logger {
        include_headers
    }
    
    # 限流
    rate_limit {
        zone general
        key {remote_host}
        rate 300r/m
    }
    
    # JWT认证
    jwt {
        trusted_tokens {
            static_secret {env.JWT_SECRET}
        }
    }
    
    # 缓存
    cache {
        ttl 10m
        match {
            method GET
            status 200
        }
    }
    
    # 反向代理
    reverse_proxy localhost:8080 {
        header_up X-User-ID {http.jwt.claims.sub}
    }
    
    # 指标收集
    metrics /metrics {
        basicauth {
            admin $2a$14$Zkx19XLiW6VYouLHR5NmfOFU0z2GTNqq9qB6FY9gZKOOdOoKw6Uw.
        }
    }
}

6.5.3 条件插件使用

example.com {
    # 基于路径的插件使用
    @api {
        path /api/*
    }
    
    @static {
        path /static/* /assets/*
    }
    
    @admin {
        path /admin/*
    }
    
    # API路径使用JWT和限流
    handle @api {
        jwt {
            trusted_tokens {
                static_secret {env.JWT_SECRET}
            }
        }
        
        rate_limit {
            zone api
            key {http.jwt.claims.sub}
            rate 1000r/h
        }
        
        reverse_proxy localhost:8080
    }
    
    # 静态资源使用缓存
    handle @static {
        cache {
            ttl 24h
            key {method} {host} {path}
        }
        
        file_server
    }
    
    # 管理区域使用基本认证和严格限流
    handle @admin {
        basicauth {
            admin $2a$14$Zkx19XLiW6VYouLHR5NmfOFU0z2GTNqq9qB6FY9gZKOOdOoKw6Uw.
        }
        
        rate_limit {
            zone admin
            key {remote_host}
            rate 10r/m
        }
        
        file_server {
            root /var/www/admin
        }
    }
    
    # 默认处理
    file_server
}

6.6 插件性能优化

6.6.1 插件性能监控

example.com {
    # 性能监控插件
    performance_monitor {
        # 监控阈值
        slow_request_threshold 1s
        memory_threshold 100MB
        
        # 监控指标
        metrics {
            request_duration
            memory_usage
            goroutine_count
            gc_duration
        }
        
        # 告警配置
        alerts {
            webhook https://alerts.example.com/webhook
            email admin@example.com
        }
    }
    
    # 其他插件
    cache {
        ttl 1h
    }
    
    reverse_proxy localhost:8080
}

6.6.2 插件缓存优化

example.com {
    # 多层缓存
    
    # 内存缓存(L1)
    cache_memory {
        max_size 100MB
        ttl 5m
        key {method} {host} {path}
    }
    
    # Redis缓存(L2)
    cache_redis {
        host localhost:6379
        ttl 1h
        key {method} {host} {path} {query}
        
        # 连接池优化
        pool_size 20
        max_idle 10
        idle_timeout 5m
    }
    
    # CDN缓存(L3)
    cache_cdn {
        provider cloudflare
        api_key {env.CLOUDFLARE_API_KEY}
        zone_id {env.CLOUDFLARE_ZONE_ID}
        ttl 24h
    }
    
    reverse_proxy localhost:8080
}

6.6.3 插件资源管理

{
    # 全局资源限制
    servers {
        max_header_size 16KB
        read_timeout 30s
        write_timeout 30s
        idle_timeout 120s
        
        # 连接限制
        max_connections 10000
        
        # 内存限制
        max_memory 1GB
    }
}

example.com {
    # 请求大小限制
    request_body {
        max_size 10MB
    }
    
    # 连接限制
    connection_limit {
        max_connections_per_ip 100
        max_connections_total 5000
    }
    
    # 内存使用监控
    memory_monitor {
        max_usage 500MB
        check_interval 30s
        
        # 超出限制时的动作
        on_exceed {
            log_warning
            reject_new_connections
            # gc_force
        }
    }
    
    reverse_proxy localhost:8080
}

6.7 实战案例

6.7.1 企业级API网关

# 企业级API网关配置
api.company.com {
    # 请求日志
    request_logger {
        include_headers
        exclude_paths /health /metrics
        format json
        output file /var/log/caddy/api-requests.log
    }
    
    # 全局限流
    rate_limit {
        zone global
        key {remote_host}
        rate 1000r/m
        window 1m
        burst 100
    }
    
    # CORS处理
    @cors_preflight {
        method OPTIONS
    }
    
    handle @cors_preflight {
        header {
            Access-Control-Allow-Origin "*"
            Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
            Access-Control-Allow-Headers "Content-Type, Authorization, X-API-Key"
            Access-Control-Max-Age "86400"
        }
        respond "" 204
    }
    
    # API密钥验证
    @api_key_required {
        not path /public/*
        not path /health
    }
    
    handle @api_key_required {
        api_key_auth {
            header X-API-Key
            redis_store {
                host localhost:6379
                key_prefix "api_keys:"
            }
            
            # 密钥限流
            rate_limit {
                zone api_keys
                key {http.request.header.X-API-Key}
                rate 10000r/h
                window 1h
            }
        }
    }
    
    # JWT认证(高级API)
    @jwt_required {
        path /v2/* /premium/*
    }
    
    handle @jwt_required {
        jwt {
            trusted_tokens {
                rsa_file /etc/caddy/jwt-public.pem
            }
            
            user_claims {
                user_id sub
                plan subscription.plan
                permissions permissions
            }
        }
        
        # 基于订阅计划的限流
        @premium_users {
            expression {http.jwt.claims.plan} == "premium"
        }
        
        rate_limit @premium_users {
            zone premium
            key {http.jwt.claims.sub}
            rate 100000r/h
        }
        
        rate_limit not @premium_users {
            zone standard
            key {http.jwt.claims.sub}
            rate 10000r/h
        }
    }
    
    # 缓存层
    @cacheable {
        method GET
        not path /user/* /account/*
    }
    
    cache @cacheable {
        key {method} {host} {path} {query} {header.X-API-Key}
        ttl 5m
        stale_ttl 1h
        
        redis {
            host redis-cluster:6379
            password {env.REDIS_PASSWORD}
            db 1
        }
        
        # 缓存预热
        warmup {
            urls /api/popular /api/trending
            interval 10m
        }
    }
    
    # 服务路由
    handle /v1/users/* {
        reverse_proxy user-service:8080 {
            lb_policy least_conn
            health_uri /health
            
            header_up X-User-ID {http.jwt.claims.sub}
            header_up X-API-Key {http.request.header.X-API-Key}
        }
    }
    
    handle /v1/orders/* {
        reverse_proxy order-service:8080 {
            lb_policy ip_hash
            health_uri /health
        }
    }
    
    handle /v1/payments/* {
        # 支付服务需要额外安全
        security_headers {
            strict_transport_security "max-age=31536000"
            content_security_policy "default-src 'none'"
        }
        
        reverse_proxy payment-service:8080 {
            transport http {
                tls
                tls_client_auth /etc/caddy/client.pem /etc/caddy/client.key
            }
        }
    }
    
    # WebSocket支持
    handle /ws/* {
        reverse_proxy websocket-service:8080 {
            lb_policy ip_hash
        }
    }
    
    # 监控端点
    handle /metrics {
        basicauth {
            monitor $2a$14$Zkx19XLiW6VYouLHR5NmfOFU0z2GTNqq9qB6FY9gZKOOdOoKw6Uw.
        }
        
        metrics {
            prometheus
            include_labels method path status
        }
    }
    
    # 健康检查
    handle /health {
        health_check {
            upstream_checks {
                user-service:8080
                order-service:8080
                payment-service:8080
            }
            
            response_format json
        }
    }
    
    # 默认处理
    respond "API Gateway" 200
}

6.7.2 多租户SaaS平台

# 多租户SaaS平台
*.saas.example.com {
    # 租户识别
    tenant_resolver {
        source subdomain
        redis_store {
            host localhost:6379
            key_prefix "tenants:"
        }
        
        # 租户不存在时的处理
        fallback_url https://www.example.com/signup
    }
    
    # 租户级别的限流
    rate_limit {
        zone tenants
        key {http.tenant.id}
        rate {http.tenant.rate_limit}r/h
        window 1h
    }
    
    # 租户级别的缓存
    cache {
        key {http.tenant.id} {method} {path} {query}
        ttl {http.tenant.cache_ttl}
        
        redis {
            host localhost:6379
            db {http.tenant.cache_db}
        }
    }
    
    # 租户特定的安全策略
    @enterprise_tenants {
        expression {http.tenant.plan} == "enterprise"
    }
    
    handle @enterprise_tenants {
        # 企业级安全
        security_headers {
            strict_transport_security "max-age=31536000; includeSubDomains"
            content_security_policy {http.tenant.csp_policy}
        }
        
        # 客户端证书认证
        tls {
            client_auth {
                mode require_and_verify
                trusted_ca_cert_file /etc/caddy/tenant-ca/{http.tenant.id}.pem
            }
        }
    }
    
    # 租户数据隔离
    reverse_proxy app-backend:8080 {
        header_up X-Tenant-ID {http.tenant.id}
        header_up X-Tenant-Plan {http.tenant.plan}
        header_up X-Tenant-Features {http.tenant.features}
        
        # 基于租户的路由
        @premium_tenants {
            expression {http.tenant.plan} in ["premium", "enterprise"]
        }
        
        # 高级租户使用专用后端
        to @premium_tenants premium-backend:8080
    }
    
    # 租户级别的监控
    metrics {
        labels {
            tenant_id {http.tenant.id}
            tenant_plan {http.tenant.plan}
        }
    }
    
    # 租户日志
    log {
        output file /var/log/caddy/tenants/{http.tenant.id}.log
        format json
        include http.tenant.id http.tenant.plan
    }
}

本章总结

本章我们全面学习了Caddy的插件系统和扩展功能:

  1. 插件系统架构:理解了Caddy的模块化设计和插件类型
  2. 官方插件:掌握了常用官方插件的配置和使用
  3. 第三方插件:学习了热门第三方插件的安装和配置
  4. 自定义插件开发:了解了插件开发的基础知识和最佳实践
  5. 插件配置:掌握了插件的配置语法和组合使用
  6. 性能优化:学习了插件的性能监控和优化技巧
  7. 实战案例:通过企业级案例学习了插件的综合应用

通过本章的学习,你应该能够: - 选择和配置适合的插件 - 开发自定义插件满足特殊需求 - 优化插件性能和资源使用 - 构建复杂的企业级应用架构 - 扩展Caddy的功能以满足业务需求

练习题

基础练习

  1. 插件安装和配置

    • 安装和配置缓存插件
    • 配置限流插件
    • 使用JWT认证插件
  2. 插件组合

    • 组合使用多个插件
    • 配置条件性插件使用
    • 实现插件间的数据传递
  3. 插件监控

    • 配置Prometheus监控
    • 实现插件性能监控
    • 设置插件告警

进阶练习

  1. 自定义插件开发

    • 开发简单的中间件插件
    • 创建DNS提供商插件
    • 实现存储后端插件
  2. 插件优化

    • 优化插件性能
    • 实现插件缓存
    • 管理插件资源使用
  3. 企业级配置

    • 构建API网关
    • 实现多租户架构
    • 配置企业级安全

实战练习

  1. 插件生态系统

    • 评估和选择插件
    • 构建插件组合方案
    • 实现插件版本管理
  2. 性能调优

    • 分析插件性能瓶颈
    • 优化插件配置
    • 实现插件负载均衡
  3. 运维管理

    • 建立插件监控体系
    • 实现插件自动化部署
    • 配置插件故障恢复

下一章我们将学习Caddy的部署和运维,这将帮助你在生产环境中成功运行Caddy。