引言
前两天做的数据导入功能,考虑到后端处理比较慢,所以前端上传完文件,后端开启协程异步进行处理,同时立即返回给前端一个上传成功标识及本次上传的uuid。前端拿着返回的uuid进行轮训查询后端处理状态。逻辑上没有问题,但偶现获取 ctx 中存储的信息为空。
简化后代码如下:
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"time"
)
func main() {
r := gin.Default()
// 上传接口
r.GET("/upload", func(c *gin.Context) {
// 设置tid的值
c.Set("tid", "abc")
// 异步处理业务
go func() {
// 打印 ctx 的地址,以及tid的值
fmt.Printf("\nupload-1------:%p, tid:%+v\n", c, c.Value("tid"))
// 处理业务,使用sleep替代处理逻辑
time.Sleep(time.Second * 10)
// 再次打印 ctx 的地址,以及tid的值
fmt.Printf("\nupload-2======:%p, tid:%+v\n", c, c.Value("tid"))
}()
// 立即返回给前端
c.JSON(200, gin.H{
"message": "upload pong",
"uuid": "123",
})
})
// 获取上传后的处理状态
r.GET("/check-status", func(c *gin.Context) {
fmt.Printf("\ncheck-status ctx***********:%p\n", c)
c.JSON(200, gin.H{
"message": "check status pong",
"uuid": c.Query("uuid"),
})
})
// 启动http服务
if err := r.Run(":80"); err != nil {
fmt.Println("listen err")
}
}
经过一顿分析,发现问题是Gin的缓冲池,引起的。想要查看详细分析过程请搜索微信公众号:大胡几哥哥,或访问:golang的Gin框架异步Context异常
Gin使用缓冲池是为了提高性能,但我没有正确使用导致多个http请求,使用相同的ctx,而新请求会把缓冲池中的ctx存储的内容清空,所以异步中未完成的业务获取ctx中的内容就失败。解决办法就是使用 `c.copy` 复制一份ctx:
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"time"
)
func main() {
r := gin.Default()
// 上传接口
r.GET("/upload", func(c *gin.Context) {
// 设置tid的值
c.Set("tid", "abc")
ctx := c.Copy()
// 异步处理业务
go func() {
// 打印 ctx 的地址,以及tid的值
fmt.Printf("\nupload-1------:%p, tid:%+v\n", ctx, ctx.Value("tid"))
// 处理业务,使用sleep替代处理逻辑
time.Sleep(time.Second * 10)
// 再次打印 ctx 的地址,以及tid的值
fmt.Printf("\nupload-2======:%p, tid:%+v\n", ctx, ctx.Value("tid"))
}()
// 立即返回给前端
c.JSON(200, gin.H{
"message": "upload pong",
"uuid": "123",
})
})
// 获取上传后的处理状态
r.GET("/check-status", func(c *gin.Context) {
fmt.Printf("\ncheck-status ctx***********:%p\n", c)
c.JSON(200, gin.H{
"message": "check status pong",
"uuid": c.Query("uuid"),
})
})
// 启动http服务
if err := r.Run(":80"); err != nil {
fmt.Println("listen err")
}
}
更新精彩文章请关注微信公众号:大胡几哥哥