4. 基础HTTP服务搭建:使用net/http与Gin构建RESTful API

在微服务架构中,HTTP服务是限流与熔断策略的承载层。本章将分别使用Go标准库net/http和流行框架Gin搭建一个具备路由、中间件、JSON响应能力的RESTful API服务,为后续集成限流熔断组件奠定基础。

4.1 服务架构概览

一个基础HTTP服务通常包含以下层级:

  • 路由层:定义URL路径与处理函数的映射关系。
  • 中间件层:执行请求日志、鉴权、限流等横切关注点。
  • 处理层:解析请求参数、调用业务逻辑、返回响应。
  • 响应层:统一封装JSON格式的成功或错误信息。

架构要点:限流熔断组件通常以中间件形式插入路由与处理层之间,因此服务搭建时必须预留中间件扩展点。

基础HTTP服务分层架构 外部客户端 路由层 (Router / 路径匹配) 中间件层 (日志 / 限流 / 熔断) 处理层 (Handler / 业务逻辑) 响应层 (JSON 封装 / 错误码)

4.2 使用net/http搭建基础服务

Go标准库net/http提供了完整的HTTP服务能力,无需第三方依赖即可构建RESTful API。以下是一个包含路由、中间件和JSON响应的完整示例。

4.2.1 项目结构

http-basic/
├── main.go
├── handler/
│   └── user.go
├── middleware/
│   └── logger.go
└── response/
    └── json.go

4.2.2 核心代码实现

main.go:注册路由并启动服务。

package main

import (
    "log"
    "net/http"
    "http-basic/handler"
    "http-basic/middleware"
)

func main() {
    mux := http.NewServeMux()

    // 注册路由
    mux.HandleFunc("/api/v1/users", handler.GetUsers)
    mux.HandleFunc("/api/v1/users/", handler.GetUserByID)

    // 包装中间件
    wrappedMux := middleware.Logger(mux)

    log.Println("HTTP服务启动于 :8080")
    if err := http.ListenAndServe(":8080", wrappedMux); err != nil {
        log.Fatalf("服务启动失败: %v", err)
    }
}

middleware/logger.go:请求日志中间件。

package middleware

import (
    "log"
    "net/http"
    "time"
)

func Logger(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        next.ServeHTTP(w, r)
        log.Printf("[%s] %s %s %v", r.Method, r.RequestURI, r.RemoteAddr, time.Since(start))
    })
}

handler/user.go:用户处理函数。

package handler

import (
    "net/http"
    "strconv"
    "strings"
    "http-basic/response"
)

func GetUsers(w http.ResponseWriter, r *http.Request) {
    users := []map[string]interface{}{
        {"id": 1, "name": "Alice"},
        {"id": 2, "name": "Bob"},
    }
    response.JSON(w, http.StatusOK, users)
}

func GetUserByID(w http.ResponseWriter, r *http.Request) {
    idStr := strings.TrimPrefix(r.URL.Path, "/api/v1/users/")
    id, err := strconv.Atoi(idStr)
    if err != nil {
        response.JSON(w, http.StatusBadRequest, map[string]string{"error": "invalid user id"})
        return
    }
    response.JSON(w, http.StatusOK, map[string]interface{}{"id": id, "name": "User" + idStr})
}

response/json.go:统一JSON响应封装。

package response

import (
    "encoding/json"
    "net/http"
)

func JSON(w http.ResponseWriter, status int, data interface{}) {
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(status)
    json.NewEncoder(w).Encode(data)
}

net/http 适用场景:适合对依赖管理要求严格、追求最小二进制体积、或需要深度定制HTTP层的项目。但路由能力较弱(不支持路径参数、方法匹配),需自行解析。

4.3 使用Gin框架搭建服务

Gin是目前Go生态中最流行的HTTP框架之一,提供高性能路由、中间件链、参数绑定、错误恢复等开箱即用的能力。以下使用Gin重构上述服务。

4.3.1 安装Gin

go get -u github.com/gin-gonic/gin

4.3.2 核心代码实现

main.go:使用Gin引擎注册路由与中间件。

package main

import (
    "github.com/gin-gonic/gin"
    "gin-basic/handler"
    "gin-basic/middleware"
)

func main() {
    r := gin.Default() // 默认包含Logger和Recovery中间件

    // 自定义中间件
    r.Use(middleware.CustomLogger())

    // 路由组
    v1 := r.Group("/api/v1")
    {
        v1.GET("/users", handler.GetUsers)
        v1.GET("/users/:id", handler.GetUserByID)
    }

    r.Run(":8080") // 默认监听 :8080
}

middleware/logger.go:自定义日志中间件。

package middleware

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

func CustomLogger() gin.HandlerFunc {
    return func(c *gin.Context) {
        start := time.Now()
        c.Next()
        log.Printf("[GIN] %s %s %d %v",
            c.Request.Method,
            c.Request.URL.Path,
            c.Writer.Status(),
            time.Since(start))
    }
}

handler/user.go:使用Gin的上下文处理请求。

package handler

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

func GetUsers(c *gin.Context) {
    users := []gin.H{
        {"id": 1, "name": "Alice"},
        {"id": 2, "name": "Bob"},
    }
    c.JSON(http.StatusOK, users)
}

func GetUserByID(c *gin.Context) {
    id := c.Param("id")
    c.JSON(http.StatusOK, gin.H{"id": id, "name": "User" + id})
}

4.4 net/http vs Gin 对比

特性 net/http Gin
依赖 无第三方依赖 需引入Gin包
路由能力 仅支持固定路径和前缀匹配 支持路径参数、路由组、方法匹配
中间件 需手动包装Handler 内置中间件链,Use/Group灵活组合
参数解析 手动解析r.URL.Query/r.Form c.Query/c.Param/c.ShouldBindJSON
JSON响应 手动设置Header和Encoder c.JSON自动设置Content-Type
性能 原生高性能 接近原生,路由树优化
错误恢复 需自行实现Recovery 内置Recovery中间件
社区生态 标准库,稳定 丰富插件与文档

选型建议:本课程后续的限流熔断集成将基于Gin框架,因为其中间件机制更贴合限流组件的插入模式。若你所在项目强制使用标准库,也可参考net/http版本自行封装中间件链。

4.5 中间件扩展点设计

无论使用哪种框架,中间件都是限流熔断的核心扩展点。以下是一个预留限流中间件占位的示例:

// Gin 版本:预留限流中间件
func RateLimitMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        // TODO: 第5章实现限流逻辑
        // 如果限流触发,直接返回 429 Too Many Requests
        // c.AbortWithStatusJSON(429, gin.H{"error": "rate limit exceeded"})
        // return
        c.Next()
    }
}

// 注册时插入
r.Use(middleware.RateLimitMiddleware())
Gin中间件链与限流熔断插入点 请求 日志中间件 (Logger) 限流中间件 (Rate Limit) 熔断中间件 (Circuit Breaker) 业务Handler (处理层) 虚线框为限流熔断组件插入位置,后续章节将在此处实现具体算法 中间件顺序决定执行链路:日志 → 限流 → 熔断 → 业务

4.6 启动与验证

完成代码编写后,通过以下步骤验证服务是否正常运行:

  1. 启动服务:在项目根目录执行 go run main.go。
  2. 测试GET请求:
# 获取用户列表
curl http://localhost:8080/api/v1/users

# 获取单个用户
curl http://localhost:8080/api/v1/users/42
  1. 预期响应:
// GET /api/v1/users
[{"id":1,"name":"Alice"},{"id":2,"name":"Bob"}]

// GET /api/v1/users/42
{"id":"42","name":"User42"}

关键检查点:确认中间件日志正常输出、JSON响应格式正确、路由路径参数解析无误。这些是后续集成限流熔断组件时的基础验证条件。

4.7 本章小结

  • 使用net/http搭建了无依赖的基础HTTP服务,理解标准库的路由与中间件机制。
  • 使用Gin框架搭建了具备路由组、中间件链、参数绑定的RESTful API服务。
  • 通过对比表格明确了两种方案的适用场景与差异。
  • 在中间件链中预留了限流与熔断的插入点,为后续章节的组件集成做好准备。