字节笔记本
2026年2月22日
基于 Go 语言构建企业级的 RESTful API 服务
本文详细介绍如何使用 Go 语言构建企业级的 RESTful API 服务,通过一个完整的账号系统实战项目,演示 API 构建的完整流程:从准备、设计、开发、测试到部署。
实现的 API 功能
本教程通过实现一个账号系统,来演示如何构建一个真实的 API 服务器。通过实战展示了 API 构建过程中各个流程的实现方法。
内容涵盖
设计阶段
- API 构建技术选型
- API 基本原理
- API 规范设计
开发阶段
- 如何读取配置文件
- 如何管理和记录日志
- 如何做数据库的 CURD 操作
- 如何自定义错误 Code
- 如何读取和返回 HTTP 请求
- 如何进行业务逻辑开发
- 如何对请求插入自己的处理逻辑
- 如何进行 API 身份验证
- 如何进行 HTTPS 加密
- 如何用 Makefile 管理 API 源码
- 如何给 API 命令添加版本功能
- 如何管理 API 命令
- 如何生成 Swagger 在线文档
测试阶段
- 如何进行单元测试
- 如何进行性能测试(函数性能)
- 如何做性能分析
- API 性能测试和调优
部署阶段
- 如何用 Nginx 部署 API 服务
- 如何做 API 高可用
账号系统业务功能
- API 服务器状态检查
- 登录用户
- 新增用户
- 删除用户
- 更新用户
- 获取指定用户的详细信息
- 获取用户列表
RESTful API 介绍
什么是 API
API(Application Programming Interface,应用程序编程接口)是一些预先定义的函数或者接口,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无须访问源码,或理解内部工作机制的细节。
要实现一个 API 服务器,首先要考虑两个方面:API 风格和媒体类型。Go 语言中常用的 API 风格是 RPC 和 REST,常用的媒体类型是 JSON、XML 和 Protobuf。在 Go API 开发中常用的组合是 gRPC + Protobuf 和 REST + JSON。
REST 简介
REST 代表表现层状态转移(REpresentational State Transfer),由 Roy Fielding 在他的论文中提出。REST 是一种软件架构风格,不是技术框架,REST 有一系列规范,满足这些规范的 API 均可称为 RESTful API。
REST 规范核心要点:
- REST 中一切实体都被抽象成资源,每个资源有一个唯一的标识 —— URI,所有的行为都应该是在资源上的 CRUD 操作
- 使用标准的方法来更改资源的状态,常见的操作有:资源的增删改查操作
- 无状态:每个 RESTful API 请求都包含了所有足够完成本次操作的信息,服务器端无须保持 Session
无状态对于服务端的弹性扩容是很重要的。
REST 风格虽然适用于很多传输协议,但在实际开发中,REST 由于天生和 HTTP 协议相辅相成,因此 HTTP 协议已经成了实现 RESTful API 事实上的标准。在 HTTP 协议中通过 POST、DELETE、PUT、GET 方法来对应 REST 资源的增、删、改、查操作:
| HTTP 方法 | 行为 | URI | 示例说明 |
|---|---|---|---|
| GET | 获取资源列表 | /users | 获取用户列表 |
| GET | 获取一个具体的资源 | /users/admin | 获取 admin 用户的详细信息 |
| POST | 创建一个新的资源 | /users | 创建一个新用户 |
| PUT | 以整体的方式更新一个资源 | /users/1 | 更新 id 为 1 的用户 |
| DELETE | 删除服务器上的一个资源 | /users/1 | 删除 id 为 1 的用户 |
RPC 简介
根据维基百科的定义:远程过程调用(Remote Procedure Call,RPC)是一个计算机通信协议。该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无须额外地为这个交互作用编程。
通俗来讲,就是服务端实现了一个函数,客户端使用 RPC 框架提供的接口,调用这个函数的实现,并获取返回值。RPC 屏蔽了底层的网络通信细节,使得开发人员无须关注网络编程的细节,而将更多的时间和精力放在业务逻辑本身的实现上,从而提高开发效率。
RPC 调用过程:
- Client 通过本地调用,调用 Client Stub
- Client Stub 将参数打包(也叫 Marshalling)成一个消息,然后发送这个消息
- Client 所在的 OS 将消息发送给 Server
- Server 端接收到消息后,将消息传递给 Server Stub
- Server Stub 将消息解包(也叫 Unmarshalling)得到参数
- Server Stub 调用服务端的子程序(函数),处理完后,将最终结果按照相反的步骤返回给 Client
Stub 负责调用参数和返回值的流化(serialization)、参数的打包解包,以及负责网络层的通信。Client 端一般叫 Stub,Server 端一般叫 Skeleton。
REST vs RPC
RPC 相比 REST 的优点:
- RPC+Protobuf 采用的是 TCP 做传输协议,REST 直接使用 HTTP 做应用层协议,这种区别导致 REST 在调用性能上会比 RPC+Protobuf 低
- RPC 不像 REST 那样,每一个操作都要抽象成对资源的增删改查,在实际开发中,有很多操作很难抽象成资源,比如登录操作
- RPC 屏蔽网络细节、易用,和本地调用类似
REST 相较 RPC 的优势:
- 轻量级,简单易用,维护性和扩展性都比较好
- REST 相对更规范,更标准,更通用,无论哪种语言都支持 HTTP 协议,可以对接外部很多系统
- JSON 格式可读性更强,开发调试都很方便
- 在开发过程中,如果严格按照 REST 规范来写 API,API 看起来更清晰,更容易被大家理解
业界普遍采用的做法是:内部系统之间调用用 RPC,对外用 REST。本教程采用 REST 风格来构建 API。
媒体类型选择
媒体类型是独立于平台的类型,设计用于分布式系统间的通信。HTTP 的 REST 能够提供多种不同的响应形式,常见的是 XML 和 JSON。JSON 无论从形式上还是使用方法上都更简单,内容更加紧凑,数据展现形式直观易懂,开发测试都非常方便。
API 流程和代码结构
HTTP API 服务器启动流程
在启动一个 API 命令后,API 命令会首先加载配置文件,根据配置做后面的处理工作:
- 加载配置文件 - 解析配置参数
- 初始化日志 - 加载日志包初始化函数,初始化日志实例
- 初始化数据库 - 建立数据库连接,供后面对数据库的 CRUD 操作使用
- 设置 HTTP
- 设置 Header
- 注册路由
- 注册中间件
- 启动 HTTP 服务器 - 调用
net/http包的ListenAndServe()方法 - 健康检查 - ping HTTP 服务器的
/sd/health接口,确保服务正常启动
HTTP 请求处理流程
一次完整的 HTTP 请求处理流程:
- 建立连接 - 客户端发送 HTTP 请求,服务器进行域名解析,通过 TCP 三次握手建立连接
- 接收请求 - API 服务器根据 HTTP 请求行的信息解析 HTTP 方法和路径,根据路由信息找到具体的处理函数
- 处理请求 - 解析 HTTP 请求报文获取请求头和消息体,进行业务逻辑处理
- 记录事务处理过程 - 记录关键信息,方便后期 Debug
目录结构
├── admin.sh # 进程的 start|stop|status|restart 控制文件
├── conf/ # 配置文件统一存放目录
│ ├── config.yaml # 配置文件
│ ├── server.crt # TLS 配置文件
│ └── server.key
├── config/ # 专门用来处理配置和配置文件的 Go package
├── db.sql # 数据库初始化脚本
├── docs/ # swagger 文档
├── handler/ # 类似 MVC 架构中的 C
│ ├── handler.go
│ ├── sd/ # 健康检查 handler
│ └── user/ # 用户业务逻辑 handler
├── main.go # Go 程序唯一入口
├── Makefile # 编译工具
├── model/ # 数据库相关操作
├── pkg/ # 引用的包
│ ├── auth/ # 认证包
│ ├── constvar/ # 常量
│ ├── errno/ # 错误码
│ ├── token/ # Token 处理
│ └── version/ # 版本包
├── router/ # 路由相关处理
│ ├── middleware/ # Gin 中间件
│ └── router.go
├── service/ # 实际业务处理函数
└── util/ # 工具类函数启动一个最简单的 RESTful API 服务器
REST Web 框架选择
经过调研选择了 GitHub star 数最多的 Gin。采用轻量级的 Gin 框架,具有如下优点:
- 高性能
- 扩展性强
- 稳定性强
- 相对而言比较简洁
核心代码示例
main.go 入口函数:
package main
import (
"log"
"net/http"
"apiserver/router"
"github.com/gin-gonic/gin"
)
func main() {
// Create the Gin engine.
g := gin.New()
// gin middlewares
middlewares := []gin.HandlerFunc{}
// Routes.
router.Load(
g,
middlewares...,
)
log.Printf("Start to listening the incoming requests on http address: %s", ":8080")
log.Printf(http.ListenAndServe(":8080", g).Error())
}路由加载:
// The health check handlers
svcd := g.Group("/sd")
{
svcd.GET("/health", sd.HealthCheck)
svcd.GET("/disk", sd.DiskCheck)
svcd.GET("/cpu", sd.CPUCheck)
svcd.GET("/ram", sd.RAMCheck)
}设置 HTTP Header:
g.Use(gin.Recovery())
g.Use(middleware.NoCache)
g.Use(middleware.Options)
g.Use(middleware.Secure)API 服务器健康状态自检
在启动 HTTP 端口前启动一个 pingServer 协程,不断地 ping /sd/health 路径,如果失败次数超过一定次数,则终止 HTTP 服务器进程。
func pingServer() error {
for i := 0; i < 10; i++ {
resp, err := http.Get("http://127.0.0.1:8080" + "/sd/health")
if err == nil && resp.StatusCode == 200 {
return nil
}
log.Print("Waiting for the router, retry in 1 second.")
time.Sleep(time.Second)
}
return errors.New("Cannot connect to the router.")
}编译与测试
编译源码:
$ gofmt -w .
$ go tool vet .
$ go build -v .使用 cURL 测试 API:
# 健康检查
curl -XGET -H "Content-Type: application/json" http://127.0.0.1:8080/sd/health
# 获取 CPU 信息
curl -XGET -H "Content-Type: application/json" http://127.0.0.1:8080/sd/cpu
# 获取内存信息
curl -XGET -H "Content-Type: application/json" http://127.0.0.1:8080/sd/ram
# 获取磁盘信息
curl -XGET -H "Content-Type: application/json" http://127.0.0.1:8080/sd/disk项目特点
- 完整的实战项目 - 通过账号系统演示完整的 API 开发流程
- 企业级实践 - 涵盖配置管理、日志、数据库、认证、HTTPS 等企业级功能
- 详细的代码示例 - 每个功能都有完整的代码实现
- 测试覆盖 - 包含单元测试、性能测试和调优方法
- 部署方案 - 提供 Nginx 部署和高可用方案
适用人群
- 想要学习 Go 语言 Web 开发的开发者
- 需要构建 RESTful API 服务的后端工程师
- 希望了解企业级 API 开发最佳实践的程序员
- 准备从其他语言转向 Go 语言的开发者