Chapter 01

认识 Gin 与第一个服务

理解 Go web 生态与 Gin 的定位,搞清 httprouter radix 树为何这么快,并跑起第一个 gin.Default() 服务。

1.1 Go 的 Web 生态

Go 天生适合写网络服务:goroutine + channel 让高并发变得廉价,标准库 net/http 直接内置了一个生产级 HTTP Server。写一个最小服务甚至不需要任何第三方库:

package main

import (
    "fmt"
    "net/http"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintln(w, "Hello, net/http")
    })
    http.ListenAndServe(":8080", nil)
}

但标准库的 ServeMux 有明显短板:不支持路径参数(如 /users/:id)、没有路由分组没有中间件链没有请求体绑定与校验。真实项目里这些都要手写,于是社区框架应运而生——Gin 就是其中最流行的一个。

1.2 核心名词速览

net/http
Go 标准库的 HTTP 实现,包含 Server、Client、Handler 接口。所有 Go web 框架(含 Gin)底层都跑在它之上。
Handler / HandlerFunc
处理 HTTP 请求的核心抽象:任何实现了 ServeHTTP(w, r) 的类型都是 Handler。Gin 把它包装成更好用的 gin.HandlerFunc
路由(Router / Mux)
根据请求方法 + 路径把请求分派到对应处理函数的组件。Gin 用 httprouter 的 radix 树实现,匹配极快。
radix 树(基数树 / 压缩前缀树)
一种把公共前缀合并存储的树结构。查找 URL 时按字符逐段下降,无需遍历所有路由,时间复杂度接近 O(路径长度)。
Context(gin.Context)
Gin 贯穿一次请求的核心对象,封装了 Request/ResponseWriter,并提供参数读取、绑定、响应、中间件传值等方法,简写为 c
Engine(gin.Engine)
Gin 应用的实例,本身也是一个 http.Handler,内部持有路由树、中间件栈、配置。由 gin.New()gin.Default() 创建。
中间件(Middleware)
在真正的业务处理前后执行的函数(日志、鉴权、恢复 panic 等),按洋葱模型层层包裹请求。
零内存分配(Zero Allocation)
Gin 路由匹配过程不产生堆分配,路径参数复用预分配切片——这是它高性能的关键指标之一。

1.3 Gin vs Echo vs Fiber vs net/http

维度GinEchoFibernet/http
底层net/http + httprouternet/httpfasthttp标准库
路由radix 树(零分配)radix 树radix 树朴素 map/前缀
API 风格简洁、Context 中心类似 GinExpress 风格原生
中间件生态最丰富丰富丰富需自建
兼容 net/http✅ 完全❌(fasthttp)
社区/Stars80K+(最高)29K+34K+
适用通用首选类 Gin 备选极致吞吐、可容忍非标准极简/学习
如何选

• 通用后端、想要最大生态与稳定性 → Gin
• 喜欢 Gin 但想要更多内置能力 → Echo
• 追求极限 QPS、且不依赖 net/http 中间件 → Fiber(fasthttp 不兼容标准库)
• 只做学习或极简服务 → net/http

1.4 radix 树为什么快

Martini 等早期框架用反射做依赖注入和路由匹配,每次请求都要反射查找,慢且分配多。httprouter 把所有注册路由压缩进一棵 radix 树:

// 假设注册了这些路由
// GET /users        GET /users/:id      GET /users/:id/posts
// radix 树把公共前缀 /users 合并:
//   /users
//     ├── (end)          -> 列表 handler
//     └── /:id
//           ├── (end)    -> 详情 handler
//           └── /posts   -> 帖子 handler

匹配 /users/42/posts 时,只需沿树下降三步,路径参数 id=42 直接从节点提取,无正则、无遍历、无堆分配。官方基准显示 Gin 比 Martini 快约 40 倍。

1.5 安装 Gin

# 初始化模块(Go 1.16+)
$ mkdir hello-gin && cd hello-gin
$ go mod init example.com/hello-gin

# 拉取 Gin(当前稳定版 v1.10.x)
$ go get github.com/gin-gonic/gin

# 查看依赖
$ go list -m github.com/gin-gonic/gin
Go 版本要求

Gin v1.10 需要 Go 1.20+。建议用 go version 确认,低版本请升级。所有依赖会记录在 go.mod / go.sum,团队协作直接 go mod download 即可复现。

1.6 第一个服务:Hello World

// main.go
package main

import (
    "net/http"

    "github.com/gin-gonic/gin"
)

func main() {
    // gin.Default() = gin.New() + Logger + Recovery 两个中间件
    r := gin.Default()

    r.GET("/ping", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{
            "message": "pong",
        })
    })

    // 默认监听 0.0.0.0:8080
    r.Run(":8080")
}
$ go run main.go
# [GIN-debug] GET  /ping --> main.main.func1 (3 handlers)
# [GIN-debug] Listening and serving HTTP on :8080

$ curl http://localhost:8080/ping
# {"message":"pong"}

逐行拆解

1.7 gin.New() vs gin.Default()

// Default:带 Logger + Recovery,开发期首选
r := gin.Default()

// New:干净的 Engine,中间件全部自己挂
r := gin.New()
r.Use(gin.Logger())
r.Use(gin.Recovery())
生产环境记得切模式

默认是 debug 模式,会打印大量路由日志且性能略低。生产设 gin.SetMode(gin.ReleaseMode) 或环境变量 GIN_MODE=release,关闭 debug 输出。

1.8 项目结构建议

hello-gin/
├── go.mod
├── go.sum
├── main.go            # 入口:装配路由、启动服务
├── internal/
│   ├── handler/       # HTTP 处理层
│   ├── service/       # 业务逻辑
│   └── repository/    # 数据访问
└── config/            # 配置

本章先跑通单文件,第 9 章会把它拆成规范的分层结构。

1.9 小结与下一步