核心代码从Python换成Go语言,提速30倍!


Stream公司最近将其核心服务的后端从Python切换成了Go,虽然他们内部还在使用Python,但是公司已经决定从现在开始在Go中编写所有性能密集型代码。本文,Stream首席执行官和创始人Thierry Schellenbach解释了公司的这一决定。

选择项目或产品的编程语言会受到许多因素驱动,与所有技术决策一样,没有完美的答案足以解决所有问题。Stream之所以这么做,是因为感受到了Go语言的巨大好处。

这与Stream的产品有关。Stream是用于构建,缩放、个性化新闻源和活动流的API。每月为3亿多用户提供约10亿次API请求。因此,性能和可靠性是Stream制定每项技术决策的最重要原因。

Python和GO:性能对比!

Go最大的卖点可能就是性能,无论是运行时间还是编译时间。它在大多数计算基准测试中与Java或C ++相当。在Stream的实际使用中,GO比Python快大约30倍。

选择性能优秀的工具非常重要(Stream已经优化了Cassandra,PostgreSQL,Redis和许多其他技术)。然而,有时发现系统中的瓶颈确实是Python引起的,像序列化,排序和聚合等计算繁重的任务有时会比从网络数据存储检索数据花费更长的时间。

Go编译器(本身是用Go编写的)也非常快。使用Go编写的Stream中最复杂的微服务只需要6秒即可编译完成,与Java和C ++等工具链相比,这是一个重大胜利。

此外,阅读Go语言代码往往非常简单,GO干净的风格让读取和推理更容易。

本地并发

通过goroutines和channel将并发性融入到语言中。Goroutines在概念上类似于操作系统线程,但非常便宜——每个成本只有几KB的堆栈空间。Go运行时可以处理智能多路复用goroutines,这一切对程序员来说是透明的。单个程序拥有数千个goroutines并不罕见。例如,net / http软件包中的服务器为每个传入的HTTP请求创建一个goroutine。

在真正的Go语言中,goroutine非常简单:只需在“go”关键字前添加一个函数调用,让它运行在自己的goroutine中即可。

Go世界的传统观点是“不通过共享内存来交流,相反的是,通过通信来共享内存“。在goroutines之间进行通信的原语是channel,它们与goroutines一样易于使用。channel有一个类型,可以通过直观的箭头语法轻松地在goroutine之间传递数据。虽然简单,但channel非常强大。通过预先考虑,与传统系统相比,制作大规模并发系统是一件轻而易举的事情。

使用简单的并发工具,可以解决那些经常导致错误的复杂问题。Go随附内置竞速检测器,可以更轻松检测异步代码中的竞争状态。

生态系统

Go仍然是编译语言环境的新手,远比不上C ++和Java等传统语言的普及程度。虽然只有大约5%的程序员知道Go,但是这个数字还在不断增长,而且这种增长是由于语言的易用性所致。虽然语言快速且功能强大,但该语言只有25个保留字(与C ++ 92或Java 53相比),对于大多数开发人员来说,它只会引入很少的新概念。

建立一个Go开发团队比大多数语言更容易,因为它更容易学习。

随Go提供的内置库在开箱即用,功能强大。使用`net / http`包制作HTTP服务只需要几行代码,并且本地支持http / 2,TLS和websocket等。社区软件包的生态系统也很出色,适用于Redis,RabbitMQ,PostgreSQL和RocksDB等。

其他福利

Go节省时间的另一种方式是使用Gofmt。它是一个命令行工具,可与大多数编辑器集成并自动将代码格式化为事实标准。如果格式不正确,代码仍会编译,但除非通过gofmt运行代码以保持整个代码库格式一致,否则将不会查看pull请求。这使代码审查人员能够专注于代码而不是花时间挑剔格式。

Go有助于开发微服务架构,gRPC和Google的协议缓冲区是管理微服务之间通信的好方法,Go有一流的支持。

Python与Go

Stream服务中的一个强大功能是排名提要。排名提要允许用户为提要指定一个评分函数,以便控制提取时的排序方式。评分算法可以提供很多变量来确定排名,但基于流行度的一个很好的例子可能是这样的:

图3:核心代码从Python换成Go语言,提速30倍!

1.要支持这种排名方法,Python和Go代码都需要:

解析分数的表达式。在这种情况下,我们想将字符串“simple_gauss(time)* popular”变成一个函数,它将一个活动作为输入并返回一个分数作为输出。

2.根据JSON配置创建部分函数。例如,我们希望“simple_gauss”以五天的刻度,一天的偏移量和0.3的衰减因子来调用“decay_gauss”。

3.如果在活动中没有定义某个字段,则应对“默认值”配置进行压缩,以便进行回退。

4.使用步骤1中的功能对Feed中的所有活动进行评分。

开发Python版本的示例花了大约三天的时间编写代码,单元测试和文档。接下来,团队花了大约两周的时间来优化代码。其中一项优化是将分数表达式(simple_gauss(time)* popular)转换为抽象语法树。该团队还实施了高速缓存逻辑,预先计算了将来某些时间的分数。

相比之下,开发此代码的Go版本需要大约四天的时间,并且性能不需要任何进一步的优化。虽然Python的开发初期看来更快,但Go版本最终需要的工作量大大减少。

在优化代码库时节省的时间归功于Go语言的特点。使用Python,程序员不得不将表达式解析为抽象语法树,并优化/剖析通过排名公开的每个函数。

结论

Go是编写微服务的伟大语言。它的速度非常快,具有原生并发原语,对现有工具的卓越支持,并且开发起来非常有趣。与Ruby或Python等脚本语言相比,Go语言可能需要更长的时间,但维护成本要低得多,而且将节省大量时间优化代码。

重要的是,Stream仍然在使用Python,它是有意义的。例如,仪表板,网站和个性化订阅源的机器学习使用Python,因为工具更好。Stream不会马上告别Python,但是今后会在Go中编写所有性能密集型代码。

余下全文(1/3)
分享这篇文章:

请关注我们:

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注