多路由器

有时需要在一个应用中运行多个 HTTP 服务器,比如管理接口和业务接口分离。

多端口服务

package main

import (
    "log"
    "net/http"
    
    "github.com/gin-gonic/gin"
)

func main() {
    api := gin.Default()
    api.GET("/", func(c *gin.Context) {
        c.String(200, "API Server")
    })
    
    admin := gin.Default()
    admin.GET("/", func(c *gin.Context) {
        c.String(200, "Admin Server")
    })
    
    go func() {
        log.Println("API server on :8080")
        api.Run(":8080")
    }()
    
    go func() {
        log.Println("Admin server on :8081")
        admin.Run(":8081")
    }()
    
    select {}
}

使用 http.Server

func main() {
    api := gin.Default()
    api.GET("/api", func(c *gin.Context) {
        c.JSON(200, gin.H{"server": "api"})
    })
    
    admin := gin.Default()
    admin.GET("/admin", func(c *gin.Context) {
        c.JSON(200, gin.H{"server": "admin"})
    })
    
    apiServer := &http.Server{
        Addr:    ":8080",
        Handler: api,
    }
    
    adminServer := &http.Server{
        Addr:    ":8081",
        Handler: admin,
    }
    
    go func() {
        apiServer.ListenAndServe()
    }()
    
    go func() {
        adminServer.ListenAndServe()
    }()
    
    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
    <-quit
    
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()
    
    apiServer.Shutdown(ctx)
    adminServer.Shutdown(ctx)
}

共享中间件

func CommonMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        start := time.Now()
        c.Next()
        log.Printf("%s %s - %v", c.Request.Method, c.Request.URL.Path, time.Since(start))
    }
}

func main() {
    common := gin.New()
    common.Use(CommonMiddleware())
    
    api := common.Group("/api")
    api.GET("/users", func(c *gin.Context) {
        c.JSON(200, gin.H{"users": []string{"张三", "李四"}})
    })
    
    admin := common.Group("/admin")
    admin.GET("/stats", func(c *gin.Context) {
        c.JSON(200, gin.H{"stats": "ok"})
    })
    
    common.Run(":8080")
}

不同配置的服务器

func main() {
    public := gin.Default()
    public.GET("/", func(c *gin.Context) {
        c.String(200, "Public API")
    })
    
    internal := gin.New()
    internal.Use(gin.Recovery())
    internal.GET("/", func(c *gin.Context) {
        c.String(200, "Internal API")
    })
    
    publicServer := &http.Server{
        Addr:         ":8080",
        Handler:      public,
        ReadTimeout:  10 * time.Second,
        WriteTimeout: 10 * time.Second,
    }
    
    internalServer := &http.Server{
        Addr:         ":8081",
        Handler:      internal,
        ReadTimeout:  30 * time.Second,
        WriteTimeout: 30 * time.Second,
    }
    
    go publicServer.ListenAndServe()
    go internalServer.ListenAndServe()
    
    select {}
}

共享数据

type SharedState struct {
    mu      sync.RWMutex
    Counter int
}

func main() {
    state := &SharedState{}
    
    api := gin.Default()
    api.GET("/increment", func(c *gin.Context) {
        state.mu.Lock()
        state.Counter++
        state.mu.Unlock()
        c.JSON(200, gin.H{"counter": state.Counter})
    })
    
    admin := gin.Default()
    admin.GET("/stats", func(c *gin.Context) {
        state.mu.RLock()
        counter := state.Counter
        state.mu.RUnlock()
        c.JSON(200, gin.H{"counter": counter})
    })
    
    go api.Run(":8080")
    go admin.Run(":8081")
    
    select {}
}

路由复用

func setupRoutes(r *gin.Engine, prefix string) {
    r.GET(prefix+"/health", func(c *gin.Context) {
        c.JSON(200, gin.H{"status": "ok"})
    })
    
    r.GET(prefix+"/info", func(c *gin.Context) {
        c.JSON(200, gin.H{"version": "1.0.0"})
    })
}

func main() {
    api := gin.Default()
    setupRoutes(api, "/api")
    
    admin := gin.Default()
    setupRoutes(admin, "/admin")
    
    go api.Run(":8080")
    go admin.Run(":8081")
    
    select {}
}

小结

多路由器适合需要不同端口、不同配置的场景。使用 http.Server 可以更精细地控制服务器行为。注意共享数据时的并发安全,以及优雅关闭所有服务器。