[Go WebSocket] 为什么我选用 Go 重构 Python 版本的 WebSocket 服务?
我是 HullQin,公众号线下聚会游戏的作者(欢迎关注公众号,发送加微信,交个朋友),转发本文前需获得作者 HullQin 授权。我独立开发了《联机桌游合集》,是个网页,可以很方便的跟朋友联机玩斗地主、五子棋等游戏,不收费没广告。还独立开发了《合成大西瓜重制版》。还开发了《Dice Crush》参加 Game Jam 2022。喜欢可以关注我 HullQin 噢~我有空了会分享做游戏的相关技术。
前言:标题其实有 2 重含义:为什么重构、为什么选 Go。
背景
如果你读过《我做了个《联机桌游合集: UNO+斗地主+五子棋》无需下载,点开即玩!叫上朋友,即刻开局!不看广告,不做任务,享受「纯粹」的游戏!》这篇文章,就知道我联机桌游的后端选型是 python,用了ASGI协议,用了Daphne实现。
但是 Python,众所周知,效率有限,为了让我的小破服务器承受更多并发压力,我需要让 CPU 更高效的运行,我希望针对每一个请求,CPU 花的时间更短,这样可以增加并发量。
应用瓶颈
其实很多 websocket 应用瓶颈在 CPU 性能和内存上。如果请求量大,CPU 处理不及时,源源不断的客户端请求会堆积在内存中,导致内存占用增加,进一步导致处理时间变慢,CPU 高负荷运转。这样显著的特点是:CPU 打满、内存占用持续上升。用户的感受就是:点击按钮后没反应,要等一会才有反应,或者一直没响应。
当然,以上只是常见理论。具体应用瓶颈需要你做一下压测,寻找那个「短板」。这样当流量突增时,你知道应该优先给哪里「扩容」。
为什么重构
自研的 WebSocket 框架,架构非常简洁,没有任何外部组件依赖(不依赖 Redis、MySQL、MQ 等数据库或队列)。使得我们更容易去分析。
整个链路是:用户浏览器 -> OpenResty -> Daphne(Python)。
其中 OpenResty 最大连接数我配置了 6w,近一段时间内都不会成为「短板」。如果以后用户量够高,我们再想办法。
瓶颈最可能出现在 Python 服务上。
目前存在如下问题:
代码执行效率可优化。Python 是动态解释,效率不如编译成机器码的语言(C++、Go)高;有个核心逻辑是用 Python 序列化和反序列化 Protocol Buffer 二进制,目前是用纯 Python 实现,可优化。
目前是基于协程处理各个连接,由于 PIL 存在,Python 无法基于多线程充分利用多核 CPU。
所有数据存在内存中,随着时间增长,内存占用会越来越高。
无法容灾+难以扩容。现在扩容只能对单台机器扩容,无法将程序运行在多台机器上。而且单机宕机后,100%用户受影响。
解决方案:
针对问题一和问题二:放弃 Python,选用其它语言实现功能。选用的语言,要解决这两个问题。
针对问题三和问题四:我们以后再聊。
这一段,解答了我为什么要重构,其实核心就是:降本增效。没钱买贵的服务器,所以只能在不影响开发效率的前提下,压榨性能。
为什么选用 Go
Goroutine
大家可以了解下 C10K 的并发问题,Linux 引入 epoll 解决了这个问题。随后又有了 C10M 等问题。
如果靠传统的多线程模型,每个线程处理 1 个连接。那么当连接太多时,线程调度就会花费很多时间,导致效率低。
所以有了协程(coroutine)这个概念。我之前使用 Python,就是利用协程处理的,每 1 个连接就开启 1 个协程。整体只是 1 个线程,减少了操作系统调度多个线程的成本。
此外,我又了解到了 Go,它提出了 goroutine,goroutine 不是协程、也不是线程。
线程特点:调度完全由操作系统调度,开发者无法直接控制,只能通过加锁来管理多个线程的执行顺序。
协程特点:调度完全由开发者掌控,但是某个协程主动放弃执行权之前,其它协程都必须挂起。
goroutine 特点:调度不是操作系统直接执行,但开发者也无法控制,因为 goroutine 的调度是由 go 的 runtime 调度的,一个比操作系统线程更轻量级的调度器。某个 goroutine 主动放弃执行权之前,其它 goroutine 也有机会执行(例如有多个 goroutine 时,限制每个 goroutine 最多连续执行 10ms,就得让出执行权)。此外开发者可以通过 channel 控制多个 goroutine 的执行顺序。
总结一下:
Goroutine 效率高于线程。跟协程相似。
Goroutine 开发成本比线程低,使用 channel 可很大程度降低并发编程的心智负担。但有时候还是需要依赖锁,这点不如协程。
Goroutine 避免了协程的这个问题:单一协程如果遇到了计算量大的任务,会阻塞其它协程。
开发效率与运行效率的权衡
Go 在保证开发效率的同时,也保证了优秀的运行效率,被称为下一代 C++。
这是网上的一张压测数据图,可以看到,综合来看,在 Python、NodeJS、Java 中,Golang 是比较全面的选手。
写在最后
我是 HullQin,公众号线下聚会游戏的作者(欢迎关注公众号,发送加微信,交个朋友),转发本文前需获得作者 HullQin 授权。我独立开发了《联机桌游合集》,是个网页,可以很方便的跟朋友联机玩斗地主、五子棋等游戏,不收费没广告。还独立开发了《合成大西瓜重制版》。还开发了《Dice Crush》参加 Game Jam 2022。喜欢可以关注我 HullQin 噢~我有空了会分享做游戏的相关技术。
版权声明: 本文为 InfoQ 作者【HullQin】的原创文章。
原文链接:【http://xie.infoq.cn/article/aa3cd7580f1566614a8d25269】。文章转载请联系作者。
评论