Go Iris 11.1 框架 Websocket 错误处理优化
公司的项目使用了 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)
}
Next:HamZone 业余无线电社区已上线
踩个点