1

[Go WebSocket] 为什么我选用Go重构Python版本的WebSocket服务?

 2 years ago
source link: https://blog.51cto.com/hullqin/5638386
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

[Go WebSocket] 为什么我选用Go重构Python版本的WebSocket服务?

精选 原创

大家好,我是公众号「线下聚会游戏」作者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的执行顺序。

总结一下:

  1. Goroutine效率高于线程。跟协程相似。
  2. Goroutine开发成本比线程低,使用channel可很大程度降低并发编程的心智负担。但有时候还是需要依赖锁,这点不如协程。
  3. Goroutine避免了协程的这个问题:单一协程如果遇到了计算量大的任务,会阻塞其它协程。

开发效率与运行效率的权衡

Go在保证开发效率的同时,也保证了优秀的运行效率,被称为下一代C++。

这是网上的一张压测数据图,可以看到,综合来看,在Python、NodeJS、Java中,Golang是比较全面的选手。

[Go WebSocket] 为什么我选用Go重构Python版本的WebSocket服务?_html

我是HullQin,公众号线下聚会游戏的作者,转发本文前需获得作者HullQin授权。我独立开发了 《联机桌游合集》,是个网页,可以很方便的跟朋友联机玩斗地主、五子棋等游戏,不收费没广告。还独立开发了 《合成大西瓜重制版》。还开发了 《Dice Crush》参加Game Jam 2022。喜欢可以关注我  HullQin 噢~我有空了会分享做游戏的相关技术。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK