当前位置: 代码迷 >> 综合 >> gin框架在 中间件 或 handler 中使用 goroutine
  详细解决方案

gin框架在 中间件 或 handler 中使用 goroutine

热度:90   发布时间:2023-12-09 10:13:40.0

需要写一个中间件来投递请求日志,为了不影响响应效率,使用了协程方式,逻辑代码如下:

// 日志投递
func AddLogToOpdbLog() gin.HandlerFunc {
    return func(c *gin.Context) {
    go func() {
    token := c.DefaultQuery("token", "")// 投递日志操作}()c.Next()}
}

首先,此中间放在最后的位置,确保最后被执行。

我使用的是postman发起的请求,使用的是form-data,参数是一样的,就是连续多点几次。

然而在测试的过程中,发现,偶尔无法从c中获取到请求参数。

将请求方式改成x-www-form-urlencoded,也是一样的。

从逻辑上来讲,中间件中的c已经是内核对参数解析完之后了,不存在还没来得及解析的情况,那么,可能的情况是,此goroutine是在此请求已经被处理完,且上下文已经被释放了之后才被调起的。

要测试也很简单,修改代码:

func AddLogToOpdbLog() gin.HandlerFunc {
    return func(c *gin.Context) {
    go func() {
    time.Sleep(time.Second*3)token := c.DefaultQuery("token", "")// 其他参数// 投递日志操作}()c.Next()}
}

发现,每次都能获取到参数,纳尼,是我理解错了?,难道c在响应完之后不会被框架主动释放掉吗?的确是这样的,它依赖于gc来回收。

使用副本来测试。

func AddLogToOpdbLog() gin.HandlerFunc {
    return func(c *gin.Context) {
    cp := c.Copy()go func() {
    token := cp.DefaultQuery("token", "")// 其他参数// 投递日志操作}()c.Next()}
}

偶尔会报如下错误。

[GIN-debug] error on parse multipart form array: multipart: NextPart: EOF

这个错误是context.go中,显然是解析数据时出错了。

func (c *Context) getFormCache() {
    if c.formCache == nil {
    c.formCache = make(url.Values)req := c.Requestif err := req.ParseMultipartForm(c.engine.MaxMultipartMemory); err != nil {
    if err != http.ErrNotMultipart {
    debugPrint("error on parse multipart form array: %v", err)}}c.formCache = req.PostForm}
}

将请求方式改成x-www-form-urlencoded,就不会报这种错了。

但是,即便使用c.Copy()也没有解决获取不到参数的问题。

扯了这么多,本质问题,其实就是多协程的情况下,并发的读写map,因为c.Request.Formc.Request.PostForm都是map结构,因此,你或许还能看到map争抢的panic,因为gin内部没有给他们加锁。

综上所述,gin 的 context 应该是在协程环境下共享数据出现了点问题。

解决方式:

将获取来自c的参数,放在协程外面,这样就是同步了,就不会出现上面的问题了。

func AddLogToOpdbLog() gin.HandlerFunc {
    return func(c *gin.Context) {
    token := cp.DefaultQuery("token", "")// 其他参数go func() {
               // 投递日志操作}()c.Next()}
}
  相关解决方案