EmailVerify LogoEmailVerify

Fiber

Email checker for Go Fiber. Email verification in Fiber HTTP handlers.

将 EmailVerify 集成到您的 Fiber 应用程序中,实现超快速的电子邮件验证。本指南涵盖性能优化、WebSocket 支持和生产监控。

安装

安装 Fiber 框架和 EmailVerify 依赖项。

go get github.com/gofiber/fiber/v2
go get github.com/emailverify/emailverify-go
go get github.com/go-playground/validator/v10
go get github.com/go-redis/redis/v8
go get gorm.io/gorm
go get github.com/prometheus/client_golang
require (
    github.com/gofiber/fiber/v2 v2.50.0
    github.com/emailverify/emailverify-go v0.1.0
    github.com/go-playground/validator/v10 v10.15.0
    github.com/go-redis/redis/v8 v8.11.5
    gorm.io/gorm v1.25.0
    github.com/prometheus/client_golang v1.17.0
)

配置

Fiber 应用程序设置

配置您的 Fiber 应用程序以实现性能优化:

package config

import (
    "github.com/gofiber/fiber/v2"
    "github.com/gofiber/fiber/v2/middleware/cors"
    "github.com/gofiber/fiber/v2/middleware/compress"
    "github.com/gofiber/fiber/v2/middleware/helmet"
)

type AppConfig struct {
    APIKey      string
    DatabaseURL string
    RedisURL    string
    Port        string
}

func NewApp(cfg *AppConfig) *fiber.App {
    app := fiber.New(fiber.Config{
        // 性能优化
        Prefork: true,
        Network: fiber.NetworkTCP,

        // 流式请求体大小
        StreamRequestBody: true,
        BodyLimit:         10 * 1024 * 1024, // 10 MB

        // 禁用 keep-alive
        DisableKeepalive: false,

        // 大小写敏感的路由
        CaseSensitive: true,

        // 严格的路由
        StrictRoute: false,
    })

    // 中间件
    app.Use(helmet.New())
    app.Use(compress.New(compress.Config{
        Level: compress.LevelBestSpeed,
    }))
    app.Use(cors.New(cors.Config{
        AllowOrigins: "*",
        AllowMethods: "GET,POST,PUT,DELETE,OPTIONS",
    }))

    return app
}

环境配置

从环境加载配置:

package config

import (
    "os"
    "log"
)

func LoadConfig() *AppConfig {
    apiKey := os.Getenv("EMAILVERIFY_API_KEY")
    if apiKey == "" {
        log.Fatal("EMAILVERIFY_API_KEY 是必需的")
    }

    return &AppConfig{
        APIKey:      apiKey,
        DatabaseURL: os.Getenv("DATABASE_URL"),
        RedisURL:    os.Getenv("REDIS_URL"),
        Port:        getEnv("PORT", "3000"),
    }
}

func getEnv(key, fallback string) string {
    if value := os.Getenv(key); value != "" {
        return value
    }
    return fallback
}

中间件

电子邮件验证中间件

为请求日志记录和验证创建自定义中间件:

package middleware

import (
    "github.com/gofiber/fiber/v2"
    "log/slog"
    "time"
)

func Logger() fiber.Handler {
    return func(c *fiber.Ctx) error {
        start := time.Now()

        // 处理请求
        err := c.Next()

        // 计算执行时间
        stop := time.Now()
        latency := stop.Sub(start)

        // 记录请求详情
        slog.Info("HTTP 请求",
            "method", c.Method(),
            "path", c.Path(),
            "status", c.Response().StatusCode(),
            "latency_ms", latency.Milliseconds(),
        )

        return err
    }
}

func ErrorHandler() fiber.ErrorHandler {
    return func(c *fiber.Ctx, err error) error {
        code := fiber.StatusInternalServerError

        if e, ok := err.(*fiber.Error); ok {
            code = e.Code
        }

        return c.Status(code).JSON(fiber.Map{
            "error": err.Error(),
            "code":  code,
        })
    }
}

CORS 中间件

为跨源请求配置 CORS:

package middleware

import (
    "github.com/gofiber/fiber/v2"
    "github.com/gofiber/fiber/v2/middleware/cors"
)

func SetupCORS(app *fiber.App) {
    app.Use(cors.New(cors.Config{
        AllowOrigins:     "https://example.com,https://api.example.com",
        AllowMethods:     "GET,POST,PUT,DELETE,OPTIONS",
        AllowHeaders:     "Content-Type,Authorization",
        ExposeHeaders:    "Content-Length,X-RateLimit-Limit",
        MaxAge:           3600,
        AllowCredentials: true,
    }))
}

路由

单个电子邮件验证路由

定义用于验证单个电子邮件的路由:

package routes

import (
    "github.com/gofiber/fiber/v2"
    "emailverify-app/handlers"
    "emailverify-app/services"
)

type VerifyEmailRequest struct {
    Email string `json:"email" validate:"required,email"`
}

type VerifyEmailResponse struct {
    Email     string      `json:"email"`
    Status    string      `json:"status"`
    Score     float64     `json:"score"`
    Details   interface{} `json:"details"`
    Cached    bool        `json:"cached"`
}

func SetupVerifyRoutes(app *fiber.App, service *services.EmailVerificationService) {
    api := app.Group("/api")
    verify := api.Group("/verify")

    verify.Post("/email", func(c *fiber.Ctx) error {
        var req VerifyEmailRequest

        if err := c.BodyParser(&req); err != nil {
            return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
                "error": "无效的请求",
            })
        }

        // 验证请求
        if err := validate.Struct(req); err != nil {
            return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
                "error": "验证失败",
                "details": err.Error(),
            })
        }

        // 验证电子邮件
        result, cached, err := service.VerifyEmail(c.Context(), req.Email)
        if err != nil {
            return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
                "error": "验证失败",
            })
        }

        response := VerifyEmailResponse{
            Email:   req.Email,
            Status:  result.Status,
            Score:   result.Score,
            Details: result.Details,
            Cached:  cached,
        }

        return c.JSON(response)
    })
}

批量电子邮件验证路由

为批量验证创建路由:

type BulkVerifyRequest struct {
    Emails []string `json:"emails" validate:"required,min=1,max=1000,dive,email"`
}

type BulkVerifyResponse struct {
    Total     int                    `json:"total"`
    Verified  int                    `json:"verified"`
    Failed    int                    `json:"failed"`
    Results   []VerifyEmailResponse  `json:"results"`
    Duration  string                 `json:"duration"`
}

func SetupBulkRoutes(app *fiber.App, service *services.EmailVerificationService) {
    api := app.Group("/api")
    verify := api.Group("/verify")

    verify.Post("/bulk", func(c *fiber.Ctx) error {
        var req BulkVerifyRequest
        start := time.Now()

        if err := c.BodyParser(&req); err != nil {
            return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
                "error": "无效的请求",
            })
        }

        // 验证请求
        if err := validate.Struct(req); err != nil {
            return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
                "error": "验证失败",
            })
        }

        results := make([]VerifyEmailResponse, 0, len(req.Emails))
        failed := 0

        for _, email := range req.Emails {
            result, cached, err := service.VerifyEmail(c.Context(), email)
            if err != nil {
                failed++
                continue
            }

            results = append(results, VerifyEmailResponse{
                Email:   email,
                Status:  result.Status,
                Score:   result.Score,
                Details: result.Details,
                Cached:  cached,
            })
        }

        response := BulkVerifyResponse{
            Total:    len(req.Emails),
            Verified: len(results),
            Failed:   failed,
            Results:  results,
            Duration: time.Since(start).String(),
        }

        return c.JSON(response)
    })
}

验证

自定义验证器

使用自定义规则扩展验证:

package validators

import (
    "github.com/go-playground/validator/v10"
    "strings"
)

type CustomValidator struct {
    validator *validator.Validate
}

func NewValidator() *CustomValidator {
    v := validator.New()

    v.RegisterValidationFunc("not_disposable", func(fl validator.FieldLevel) bool {
        email := fl.Field().String()
        domain := strings.Split(email, "@")[1]

        blockedDomains := map[string]bool{
            "tempmail.com":      true,
            "10minutemail.com":  true,
            "guerrillamail.com": true,
            "mailinator.com":    true,
        }

        return !blockedDomains[domain]
    })

    v.RegisterValidationFunc("corporate_email", func(fl validator.FieldLevel) bool {
        email := fl.Field().String()
        domain := strings.Split(email, "@")[1]

        // 仅允许特定的企业域名
        allowedDomains := map[string]bool{
            "company.com": true,
        }

        return allowedDomains[domain]
    })

    return &CustomValidator{validator: v}
}

func (cv *CustomValidator) Validate(i interface{}) error {
    return cv.validator.Struct(i)
}

数据库集成

GORM 与 Fiber

集成 GORM 用于数据库持久化:

package database

import (
    "gorm.io/driver/postgres"
    "gorm.io/gorm"
    "time"
)

type VerificationResult struct {
    ID            uint      `gorm:"primaryKey"`
    Email         string    `gorm:"uniqueIndex;not null;type:varchar(254)"`
    Status        string    `gorm:"not null;type:varchar(50)"`
    Score         float64
    Disposable    bool
    SmtpValid     bool
    FormatValid   bool
    EmailProvider string
    RiskLevel     string
    VerifiedAt    time.Time
    CachedAt      time.Time `gorm:"autoUpdateTime:milli"`
}

func NewDatabase(dsn string) (*gorm.DB, error) {
    db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
    if err != nil {
        return nil, err
    }

    // 启用连接池来提高性能
    sqlDB, err := db.DB()
    if err != nil {
        return nil, err
    }

    sqlDB.SetMaxOpenConns(25)
    sqlDB.SetMaxIdleConns(5)
    sqlDB.SetConnMaxLifetime(5 * time.Minute)

    // 自动迁移模型
    if err := db.AutoMigrate(&VerificationResult{}); err != nil {
        return nil, err
    }

    return db, nil
}

缓存

内存和 Redis 缓存

实现内存和 Redis 的多层缓存:

package cache

import (
    "context"
    "encoding/json"
    "github.com/go-redis/redis/v8"
    "sync"
    "time"
)

type MemoryCache struct {
    data map[string]CacheEntry
    mu   sync.RWMutex
    ttl  time.Duration
}

type CacheEntry struct {
    Value     interface{}
    ExpiresAt time.Time
}

type HybridCache struct {
    memory *MemoryCache
    redis  *redis.Client
}

func NewHybridCache(redisURL string) (*HybridCache, error) {
    opts, _ := redis.ParseURL(redisURL)
    client := redis.NewClient(opts)

    return &HybridCache{
        memory: &MemoryCache{
            data: make(map[string]CacheEntry),
            ttl:  5 * time.Minute,
        },
        redis: client,
    }, nil
}

func (hc *HybridCache) Get(ctx context.Context, key string) (interface{}, error) {
    // 先检查内存缓存
    hc.memory.mu.RLock()
    entry, exists := hc.memory.data[key]
    hc.memory.mu.RUnlock()

    if exists && time.Now().Before(entry.ExpiresAt) {
        return entry.Value, nil
    }

    // 检查 Redis
    val, err := hc.redis.Get(ctx, key).Result()
    if err == nil {
        var data interface{}
        json.Unmarshal([]byte(val), &data)

        // 填充内存缓存
        hc.memory.mu.Lock()
        hc.memory.data[key] = CacheEntry{
            Value:     data,
            ExpiresAt: time.Now().Add(hc.memory.ttl),
        }
        hc.memory.mu.Unlock()

        return data, nil
    }

    return nil, err
}

func (hc *HybridCache) Set(ctx context.Context, key string, value interface{}, ttl time.Duration) error {
    // 在内存缓存中设置
    hc.memory.mu.Lock()
    hc.memory.data[key] = CacheEntry{
        Value:     value,
        ExpiresAt: time.Now().Add(hc.memory.ttl),
    }
    hc.memory.mu.Unlock()

    // 在 Redis 中设置
    data, _ := json.Marshal(value)
    return hc.redis.Set(ctx, key, data, ttl).Err()
}

性能优化

连接池和优化

优化以获得高吞吐量:

package config

import (
    "github.com/gofiber/fiber/v2"
    "net"
    "time"
)

func OptimizePerformance(app *fiber.App) {
    // 调整网络设置
    app.Config.Network = fiber.NetworkTCP4
    app.Config.Concurrency = 256 * 1024 // 最大并发连接数

    // TCP 调整
    app.Listen(":3000", fiber.ListenConfig{
        ListenerNetwork: "tcp",
        SocketShutdown:  true,
        TcpKeepAlive:    30 * time.Second,
    })

    // 请求体优化
    app.Config.BodyLimit = 50 * 1024 * 1024 // 50 MB
    app.Config.ReadBufferSize = 16 * 1024   // 16 KB
    app.Config.WriteBufferSize = 16 * 1024  // 16 KB

    // Keep-alive 设置
    app.Config.IdleTimeout = 75 * time.Second
    app.Config.ReadTimeout = 15 * time.Second
    app.Config.WriteTimeout = 15 * time.Second
}

错误处理

全面的错误处理

定义一致的错误响应:

package errors

import (
    "github.com/gofiber/fiber/v2"
)

type APIError struct {
    Code    int    `json:"code"`
    Message string `json:"message"`
    Details string `json:"details,omitempty"`
}

func BadRequest(c *fiber.Ctx, message string) error {
    return c.Status(fiber.StatusBadRequest).JSON(APIError{
        Code:    fiber.StatusBadRequest,
        Message: message,
    })
}

func NotFound(c *fiber.Ctx, message string) error {
    return c.Status(fiber.StatusNotFound).JSON(APIError{
        Code:    fiber.StatusNotFound,
        Message: message,
    })
}

func InternalError(c *fiber.Ctx, message string) error {
    return c.Status(fiber.StatusInternalServerError).JSON(APIError{
        Code:    fiber.StatusInternalServerError,
        Message: message,
    })
}

测试

使用 httptest 进行集成测试

测试您的路由和处理器:

package handlers

import (
    "bytes"
    "encoding/json"
    "testing"
    "github.com/gofiber/fiber/v2"
    "github.com/stretchr/testify/assert"
)

func TestVerifyEmail(t *testing.T) {
    tests := []struct {
        name           string
        email          string
        expectedStatus int
    }{
        {
            name:           "有效的电子邮件",
            email:          "test@example.com",
            expectedStatus: fiber.StatusOK,
        },
        {
            name:           "无效的电子邮件格式",
            email:          "invalid",
            expectedStatus: fiber.StatusBadRequest,
        },
        {
            name:           "空电子邮件",
            email:          "",
            expectedStatus: fiber.StatusBadRequest,
        },
    }

    app := fiber.New()

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            body := VerifyEmailRequest{Email: tt.email}
            bodyBytes, _ := json.Marshal(body)

            req := fiber.AcquireRequest()
            req.Header.SetContentType(fiber.MIMEApplicationJSON)
            req.SetBodyStream(bytes.NewReader(bodyBytes), len(bodyBytes))

            // 测试端点
            // assert.Equal(t, tt.expectedStatus, statusCode)
        })
    }
}

WebSocket 支持

实时电子邮件验证

为实时验证实现 WebSocket:

package routes

import (
    "github.com/gofiber/fiber/v2"
    "github.com/gofiber/websocket/v2"
    "emailverify-app/services"
)

func SetupWebSocketRoutes(app *fiber.App, service *services.EmailVerificationService) {
    ws := app.Group("/ws")

    ws.Get("/verify/:email", websocket.New(func(c *websocket.Conn) {
        email := c.Params("email")

        // 验证电子邮件
        result, cached, err := service.VerifyEmail(c.Context(), email)
        if err != nil {
            c.WriteJSON(fiber.Map{
                "error": err.Error(),
            })
            return
        }

        // 通过 WebSocket 发送结果
        c.WriteJSON(fiber.Map{
            "email":  email,
            "status": result.Status,
            "score":  result.Score,
            "cached": cached,
        })

        // 优雅关闭
        c.Close()
    }))
}

监控

Prometheus 指标

添加 Prometheus 监控以实现生产可观测性:

package monitoring

import (
    "github.com/prometheus/client_golang/prometheus"
    "github.com/prometheus/client_golang/prometheus/promauto"
)

var (
    EmailVerifications = promauto.NewHistogramVec(
        prometheus.HistogramOpts{
            Name:    "email_verifications_duration_seconds",
            Help:    "电子邮件验证耗时(秒)",
            Buckets: []float64{0.001, 0.01, 0.1, 1.0},
        },
        []string{"status"},
    )

    VerificationErrors = promauto.NewCounterVec(
        prometheus.CounterOpts{
            Name: "verification_errors_total",
            Help: "验证错误总数",
        },
        []string{"error_type"},
    )

    CacheHits = promauto.NewCounter(
        prometheus.CounterOpts{
            Name: "cache_hits_total",
            Help: "缓存命中总数",
        },
    )

    CacheMisses = promauto.NewCounter(
        prometheus.CounterOpts{
            Name: "cache_misses_total",
            Help: "缓存未命中总数",
        },
    )
)

生产部署

Prefork 模式

启用 prefork 模式以实现多进程性能:

func main() {
    app := fiber.New(fiber.Config{
        Prefork: true,
        Network: fiber.NetworkTCP,
    })

    // 设置路由
    setupRoutes(app)

    // 在生产环境中使用 TLS 监听
    app.ListenTLS(":443", "/path/to/cert.pem", "/path/to/key.pem", fiber.ListenConfig{
        GracefulShutdown: true,
    })
}

Docker 配置

生产就绪的 Dockerfile:

# 构建阶段
FROM golang:1.21-alpine AS builder

WORKDIR /build

COPY go.mod go.sum ./
RUN go mod download

COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o app .

# 运行时阶段
FROM alpine:latest

RUN apk add --no-cache ca-certificates

WORKDIR /app

COPY --from=builder /build/app .

EXPOSE 3000

HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
    CMD wget -q -O /dev/null http://localhost:3000/health || exit 1

CMD ["./app"]

docker-compose.yml

完整的堆栈配置:

version: '3.8'

services:
  api:
    build: .
    ports:
      - "3000:3000"
    environment:
      EMAILVERIFY_API_KEY: ${EMAILVERIFY_API_KEY}
      DATABASE_URL: postgres://user:password@db:5432/emailverify
      REDIS_URL: redis://redis:6379/0
    depends_on:
      - db
      - redis

  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
      POSTGRES_DB: emailverify
    volumes:
      - postgres_data:/var/lib/postgresql/data

  redis:
    image: redis:7-alpine
    volumes:
      - redis_data:/data

  prometheus:
    image: prom/prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml

volumes:
  postgres_data:
  redis_data:

相关资源

On this page