Go的web框架有好多,详细对比可以参考《最好的6个Go语言Web框架》
其中iris算是比较出类拔萃的,于是在Web应用的开发中,就用它啦。
自己想用websocket来做一个简单的在线聊天应用,刚好iris也支持,于是就用它自带的 _examples/websocket ,愉快的玩耍啦。
如果只是玩一玩,这个事情也就过去了。
只是我对并发请求会出现的线程安全性问题比较有兴趣,于是看了看iris做的websockect长连接是怎么维护的。
不看不知道,一看吓一跳,直接用的 slice ,也没有用到锁。
这不是在开玩笑嘛?虽然 slice 结构在并发操作的时候不像 map 结构那样经常报错,但肯定不是线程安全的啊。
而且iris自己的网站还基于websocket做了一个聊天室,难道也就这么放心大胆的线上运行着?
【6月8日】
发现了问题,我没有马上去反馈,而是去历史的问题清单里面找了下关于websocket的问题。
还真有人【akiraho】质疑过websocket的connections&rooms怎么不用map来替换slice结构,效率会更高啊?
然而,这个人【akiraho】的质疑被无情的拒绝了,理由是:map虽然在更新的时候快,但是读写时全部要加锁,整体效率也就比不上slice的结构。
看到上面的问题和回答,谈到的只是性能方面,并没有谈到线程安全方面。
于是我就补了一刀,说现在的实现方式存在线程安全性问题。
很快,开发者【kataras】就给了我答复,鼓励我发现问题后可以直接提交修复方案.如果没有准备好来修复的话,也可以把问题出现的示例给出来,以便于他们复现问题。
【6月11日】
那好吧,我就直接上去改下代码吧。
于是,要在github上第一次写代码了。蹩脚的表演开始了,见笑。
https://github.com/yz124/iris/
我把 websocket/server.go 中关于connections的读写给加上了互斥锁。
过程中,我还考虑性能方面怎么能更好些,在 func (cs *connections) add 里面使用互斥锁,想节省点运算。
这次提交也很快得到了【kataras】的答复,回复了4个质疑,我改动的方法不合理。
嗯,他说的都很有道理的,比如:锁是值类型传递的话,会导致新的锁,效率低是一方面,最重要的是锁会失效,因为值类型copy出来是新锁。
他也建议,如果用锁的话,为什么不在更高层来加,既简单也不容易出问题。
【6月15日】
本来之前想为了性能而考虑的地方,反而成了很不好的代码实现。
于是,我就再次修正,把锁从方法里面提到了方法外面。
而这次修改提交是在github上直接修改提交的,并没有在本地验证过。
大家能想到后果是什么吗?
github对提交的代码会做自动测试和检查,我提交的代码无法编译通过,而自动拒绝提交。
有没有感觉很强大,机器审一遍,节省了多少人力啊,效率极大提升有木有?
于是又做了两次提交,终于通过了。
——自己测试验证是多么重要呀,也是负责任的表现
这次的提交,我附带把一个线程安全的测试用例也一起发出来了。
“用现在的程序,因为线程不安全,我发起1w次并发链接,最后服务端的connections只有不到8k的连接,丢失了2k连接”
这次的提交就没有那么快得到响应了(6月15日 - 8月8日),我还以为就这么不了了之了,还做了好多种猜想。
“是不是我提的这个线程安全性问题,他们就不觉得是多大点事,不理了啊”
“是不是我的提交和整个问答互动过程,让人家觉得太菜,不予理睬了啊”
一开始几天,我还每天上github看下有没有新消息,时间久了我也就不怎么关注这个问题了。
【8月8日】
直到昨天,我竟然收到一个邮件,【kataras】修复并且关闭了这个问题,而且还用了两种方法。
一种是用 map + 读写锁,一种是用 sync.Map 。
最终在iris主干上的版本,使用的 sync.Map 。
https://github.com/kataras/iris/pull/1023#event-1777396646
过去了这么长时间,我都对这个问题不关注了,没想到iris的开发者还是把这个问题修复了,赞。
【总结一下】
这还是自己第一次在github上主动提问题和更新代码,到现在也对整个流程不是很了解,所以很菜,也请大家理解。
从这次经历中,大家是否也能感受到,参与开源并没有想象中那么难。
开源的优势也能有所体现:哪怕牛人写的代码也是有bug的,很多人共同来维护和提出建议,才能更快的前进。
最后,希望更多人能参与到开源中来,我自己也希望能发挥更多的贡献吧。
欢迎关注我的实战课程《PHP秒杀系统 高并发高性能的极致挑战》
热门评论
不测试就提交?不敢想象
真大佬也,使用websocket挖到坟,大佬考虑的太深刻了
老师你好,我前端在用isis-ws.js发送消息给后端的时候,后端收不到消息,前端的readtstate为1,也不报错