Go Iris 11.1 框架 Websocket 错误处理优化

共码了2675个字
文内使用到的标签:

公司的项目使用了 iris 框架的较早版本,市场那边经常反馈服务会出现异常断连,经过多次远程调试,日志分析,始终没有发现有价值的信息,初步定位到,是属于 websocket 的问题,于是开始研究这部分是否能给故障分析带来帮助。

寻找入口

项目里的 websocket 使用了 iris mvc 依赖注入的方式,以下是主要代码片段,也是在其它地方常见的 iris 11 websocket 使用方法

func main() {
  app := iris.Default()
        mvc.Configure(app.Party("/ws"), websocket.ConfigureWS)
        .....
}
func ConfigureWS(m *mvc.Application) {
    ws := websocket.New(websocket.Config{MaxMessageSize: 3145728, ReadTimeout: time.Minute,
        Error: func(w http.ResponseWriter, r *http.Request, status int, reason error) {
            util.GetLog().Error("websocket new err", status, reason.Error())
        }})
       ws.OnConnection(func(c websocket.Connection) {
        fmt.Println(c.Context())
        fmt.Println(c)
        fmt.Println(ws.GetConnections())
        c.OnDisconnect(func() {
            fmt.Println(c)
        })
        c.OnError(func(e error) {
            fmt.Println(e.Error())
        })
    })
       m.Register(
        ws.Upgrade, ws,
    )
    websocketController := new(WebsocketListen)
    m.Handle(websocketController)
}

type WebsocketListen struct {
    Ctx                   iris.Context
    Conn                  websocket.Connection
    WS                    *websocket.Server
}
func (c *WebsocketListen) Get() {
       c.WS.OnConnection(func(c websocket.Connection) {
        fmt.Println(c.Err())
    })

    c.Conn.OnError(func(e error) {
        fmt.Println(e)
    })
    //fmt.Println(c.Conn.ID())
    c.Conn.OnLeave(c.onLeave)
    c.Conn.OnMessage(c.OnMessage)

    c.Conn.OnError(func(err error) {
        fmt.Println(err.Error())
    })

    c.Conn.OnDisconnect(func() {
        fmt.Println(c.Conn.Err())
    })

    c.Conn.Wait()
}

func (c *WebsocketListen) onLeave(roomName string) {
    fmt.Println(c.Ctx.Request().Context().Err())
        util.GetWsLog().Debug("Leave channelId", c.Conn.ID())
}

通过以上代码分析,可以发现,框架提供了 OnError 方法,但实际使用的时候,无论是超出了 MaxMessageSize 或者 ReadTimeout 的配置,都无法正常捕获错误情况。

多方查询之后,也没有发现对应的解决方法,于是就先往官方 Repo 提了 issue: https://github.com/kataras/iris/issues/1911 ,顺便研究一下源码,看能不能尝试做一些修改。

修改源码

官方已经把 v11.1 版本的代码删除了,所以没办法贴出对应 Repo 的代码。

从源码里不难发现,iris 框架也是基于 github.com/gorilla/websocket 这个包进行的二次封装,其中,有关对外报错的方法,都集中在 FireOnError,以下是本次关键代码片段分析:

iris@v11.1.1 / websocket / connection.go > line 376 > func (c *connection) startReader()

for {
    if hasReadTimeout {
        conn.SetReadDeadline(time.Now().Add(c.server.config.ReadTimeout))
    }
    _, data, err := conn.ReadMessage()
    if err != nil {
                // i try to remove this IF condition, it works, get error message "websocket: read limit exceeded" by method c.Conn.OnError
        if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) {
            c.FireOnError(err)
        }
                // when beyond MaxMessageSize config setting, it just break which can't get error message
        break
    } else {
        c.messageReceived(data)
    }
}

有关超时控制的代码:

iris@v11.1.1 dependent on gorilla / websocket @ v1.4.1 > conn.go > line 890

var ErrReadLimit = errors.New("websocket: read limit exceeded")

if c.readLimit > 0 && c.readLength > c.readLimit {
    c.WriteControl(CloseMessage, FormatCloseMessage(CloseMessageTooBig, ""), time.Now().Add(writeWait))
    return noFrame, ErrReadLimit
}

在刚刚提到的 startReader 方法里,有这么一个判断,限制了对外的错误提示,将此处的判断条件去掉之后,就可以拿到更多的错误信息了:

if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) {
    c.FireOnError(err)
}
Prev:
Next:

《“Go Iris 11.1 框架 Websocket 错误处理优化”》 有 1 条评论

  1. 小方说道:

    踩个点

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注