gin中使用Socket.io踩的坑
最近在使用golang来搭建后台系统,框架使用非常著名的gin框架,由于在项目中需要使用socket.io进行长连接管理,官方推荐的golang服务端为go-socket.io,于是按照官方的文档示例很简单的就搭建起来了后台服务。 但是使用client 进行连接的时候就出现了各种问题,本文记录一下解决方案。
版本不兼容问题¶
我按照官方的示例 go-socket.io/main.go at master · googollee/go-socket.io · GitHub 来搭建后台,启动也很正常,由于没有golang的客户端,一般情况下我们是使用web与后台进行长连接,但是我的项目需要使用python 进行长连接,于是我又简单的写了一个python 客户端连接脚本. python使用的也是socketio推荐的 python-socketio
脚本也很简单,就是在连接成功以后,打印 "I'm connected!", 然后发送一个hello 事件,发送 "yyx" 字符串,在失去连接的时候,打印 "I'm disconnected!".
运行脚本以后,一开始都还挺正常的,都按照预期的流程进行打印。
可是没过一会儿,就会打印出 "I'm disconnected!"!
虽然socketio会自动连接,但是在正常的业务中,一般是除了一些网络问题会导致断开,我们并不希望它自动断开,一开始我以为是偶发的,于是又观察了一会,还是会自动断开,我判断这可能不是由于网络原因导致的,可能是程序有什么bug!
于是我开始找规律,发现断开的时间是有规律了,每隔1分钟就会断一次,我猜想这个断开时间应该由某个参数导致的,但是代码都是官方的示例代码,我看了一下并没有什么设置的地方,于是我开始查看go-socket.io 的源码
我在初始化socket server 使用的是 server := socketio.NewServer(nil)
, 查看这个函数的原型, func NewServer(opts *engineio.Options) *Server
, 这里是需要传一个*engineio.Options
的配置信息的,上面传了一个 nil 进去,engineio.Options是个结构体,
这里继续探索源码发现,如果传入的是nil,那么各自都结构体都有相应的默认值
我敏感的觉得,自动断开可能和这两个timeout有关系,因为之前观察的现象是差不多1分钟就会断开一次,这里的PingTimeout 默认值就是一分钟,于是我尝试将这个时间缩短一下来验证一下。
这时,再运行服务端与客户端,则此时就会每隔5秒钟断一次了! 关于PintTimeout与pingInterval的作用参考这篇文章
终于找到了这个参数,但是要怎么解决自动断开的问题呢? 我总不能将这个PingTimeout 设置一个非常大的值吧? 那么它还是会每隔那么长的时间就会断一次,还是没有从根本上解决问题。
我又开始观察访问的url, http://127.0.0.1:8000/socket.io/?transport=websocket&EIO=4&t=1658570826.4926221
这里有个参数,EIO=4, 我之前在使用python 写服务端的时候,依稀记得这个socketio对于版本号很敏感,不同的版本之间是不兼容的。
我想这个EIO应该是指的Engine.IO 的版本号,这里应该是4的意思,我又查看了我使用python-socketio的版本,是5.2.1, 根据上面的表格这个就说的通了,我就在想是不是go-socket.io 的版本不对呢? 可是我下载的是最新的呀? 我在它的github上也没有看找到它用的是哪个版本的Engine.IO,也许是我找的不够仔细,算了,我先降一下python-socketio的版本试一下吧,于是我按照上面表格中的对应关系,使用4.x的python-socketio, pip install "python-socketio[client]"==4.6.1
, 找了一个4.x中最高的版本4.6.1,其它的代码都没有改,这次居然可以了,不再每隔一定时间就断开了!
问题就这样的被解决了,没想到原来是go-socket.io还在使用3版本的Engine,后来我在它的issue中也有一个很显示的issue, Support Socket.IO v4,原来很早就有人提到了这个问题,但是作者本人答复就是4的版本变化太大,目前没精力改。。。
在网上搜索,其实很多问题都是由于版本不兼容导致的,在socketio中使用Engine.IO的版本相互不兼容,这个一定要注意!
跨域的问题¶
由于项目中还是需要使用web端来和后端进行长连接的,有了上面的经验,这次我使用了老版本的问题socketio.js,但是上来就报跨域问题了,依然还是查看源码,还是在初始化socketio服务器的时候,传入的 *engineio.Options
配置信息中,是有关于跨域的配置的 Transports
这里对polling与websocket的Transport的CheckOrigin都进行的设置,全都返回true,当然具体的项目中可以根据实际情况进行单独的设置。
总结¶
本次的这两个问题解决起来都不复杂,但是排查起来有点困难,也走了不少弯路,像排查兼容性的问题,我一开始就没有往go-socket.io 支持的版本不是4这方面想,总想着我都用最新的了应该是支持4的吧。 遇到问题当发现网上的资料太少的时候,最后还得靠看源码来发现问题。
