Golang使用Gin框架实现REST API开发指南
Golang使用Gin框架实现REST API开发指南 我们有一个运行正常的 Golang Gin REST API,但在运行 24 小时后会停止响应。
5 回复
不知道。
可能是数据库连接池的问题。
为什么它停止工作了?
所以从日志中你发现是 Gin 导致了问题?
问题分析
根据描述,您的 Gin REST API 在运行 24 小时后停止响应,这通常是由于资源泄漏或连接管理问题导致的。以下是常见原因和解决方案:
1. 数据库连接泄漏
// 错误的示例 - 连接未关闭
func getUser(c *gin.Context) {
db, _ := sql.Open("mysql", "user:pass@/dbname")
defer db.Close() // 只关闭了连接池,但查询的连接可能未释放
rows, err := db.Query("SELECT * FROM users")
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
// 缺少 rows.Close() 会导致连接泄漏
}
// 正确的示例
func getUser(c *gin.Context) {
db, err := sql.Open("mysql", "user:pass@/dbname")
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
defer db.Close()
rows, err := db.Query("SELECT * FROM users")
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
defer rows.Close() // 确保关闭结果集
// 处理数据...
}
2. HTTP 连接未正确关闭
// 设置连接超时和保活参数
func main() {
router := gin.Default()
srv := &http.Server{
Addr: ":8080",
Handler: router,
ReadTimeout: 15 * time.Second,
WriteTimeout: 15 * time.Second,
IdleTimeout: 60 * time.Second,
}
// 优雅关闭
go func() {
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("listen: %s\n", err)
}
}()
// 处理信号
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()
if err := srv.Shutdown(ctx); err != nil {
log.Fatal("Server forced to shutdown:", err)
}
}
3. 内存泄漏监控
// 添加性能监控中间件
func monitorMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
// 记录请求前的内存状态
var m runtime.MemStats
runtime.ReadMemStats(&m)
beforeAlloc := m.Alloc
c.Next()
// 记录请求后的内存状态
runtime.ReadMemStats(&m)
afterAlloc := m.Alloc
log.Printf("Request: %s | Duration: %v | Memory delta: %v bytes",
c.Request.URL.Path,
time.Since(start),
afterAlloc-beforeAlloc)
}
}
func main() {
router := gin.Default()
router.Use(monitorMiddleware())
// 定期输出内存统计
go func() {
for {
time.Sleep(1 * time.Hour)
var m runtime.MemStats
runtime.ReadMemStats(&m)
log.Printf("Memory Stats: Alloc=%v, TotalAlloc=%v, Sys=%v, NumGC=%v",
m.Alloc, m.TotalAlloc, m.Sys, m.NumGC)
}
}()
router.Run(":8080")
}
4. 协程泄漏检查
// 监控协程数量
func monitorGoroutines() {
go func() {
for {
time.Sleep(30 * time.Second)
count := runtime.NumGoroutine()
log.Printf("Current goroutines: %d", count)
if count > 1000 { // 设置阈值
log.Printf("WARNING: High goroutine count: %d", count)
// 输出堆栈信息
buf := make([]byte, 1<<16)
stackSize := runtime.Stack(buf, true)
log.Printf("Stack trace:\n%s", buf[:stackSize])
}
}
}()
}
func main() {
monitorGoroutines()
router := gin.Default()
// 使用带超时的上下文处理请求
router.GET("/long-task", func(c *gin.Context) {
ctx, cancel := context.WithTimeout(c.Request.Context(), 10*time.Second)
defer cancel()
done := make(chan bool)
go func() {
// 模拟长时间任务
time.Sleep(15 * time.Second)
done <- true
}()
select {
case <-ctx.Done():
c.JSON(408, gin.H{"error": "request timeout"})
case <-done:
c.JSON(200, gin.H{"status": "completed"})
}
})
router.Run(":8080")
}
5. 完整的健康检查端点
func healthCheck(c *gin.Context) {
// 检查数据库连接
db, err := sql.Open("mysql", "user:pass@/dbname")
if err != nil {
c.JSON(503, gin.H{"status": "unhealthy", "error": "database connection failed"})
return
}
defer db.Close()
err = db.Ping()
if err != nil {
c.JSON(503, gin.H{"status": "unhealthy", "error": "database ping failed"})
return
}
// 检查内存状态
var m runtime.MemStats
runtime.ReadMemStats(&m)
health := gin.H{
"status": "healthy",
"timestamp": time.Now().Unix(),
"memory": gin.H{
"alloc": m.Alloc,
"total_alloc": m.TotalAlloc,
"sys": m.Sys,
"num_gc": m.NumGC,
},
"goroutines": runtime.NumGoroutine(),
}
c.JSON(200, health)
}
func main() {
router := gin.Default()
router.GET("/health", healthCheck)
router.Run(":8080")
}
6. 使用 pprof 进行性能分析
import (
_ "net/http/pprof"
"net/http"
)
func main() {
// 启动 pprof 服务器
go func() {
http.ListenAndServe("localhost:6060", nil)
}()
router := gin.Default()
router.Run(":8080")
}
运行后可以通过以下命令获取性能数据:
# 获取 30 秒的 CPU 性能分析
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30
# 获取内存快照
go tool pprof http://localhost:6060/debug/pprof/heap
# 获取协程信息
go tool pprof http://localhost:6060/debug/pprof/goroutine
建议按以下步骤排查:
- 首先添加健康检查端点监控服务状态
- 启用 pprof 分析内存和协程泄漏
- 检查所有数据库连接是否正确关闭
- 确保 HTTP 连接配置了合理的超时时间
- 监控日志中是否有异常错误信息
这些代码示例可以直接集成到您的项目中,帮助诊断和解决服务停止响应的问题。

