发布在即!企业 AIGC 应用程度测评,3 步定制专属评估报告。抢首批测评权益>>> 了解详情
写点什么

Stream 从 Python 切换到 Go 的原因

  • 2018-07-21
  • 本文字数:3416 字

    阅读完需:约 11 分钟

Stream 最近将其后端核心服务从 Python 改成了 Go。虽然他们的某些模块仍然在使用 Python,但是公司已决定从现在开始使用 Go 来编写对性能要求较高的代码。文中,Stream 的 CEO 兼创始人 Thierry Schellenbach 将解释他们决定转向 Go 的原因。

影响项目或者产品编程语言选型的因素有很多。与任何技术决策一样,选择编程语言时同样需要多方面权衡,即使这样,最终的选择结果都很难是完美的。我们最近将后端的核心服务从 Python 改成了 Go,原因有很多,好处也很多。

为了理解这一变化的重要性,需要先了解我们的产品。Stream 是一套用于构建、伸缩、定制化新闻源和活动流的 API。每个月为 3 亿多用户提供约 10 亿次 API 请求。我们尤其关注性能和可靠性,这两点因素决定了我们制定的每项技术决策。

性能更优

Go 最大的卖点在于它的性能,无论在运行还是编译时它都有突出的性能优势。它与 Java 或者 C++ 的运算速度几乎相当。在实际使用中,我们发现它比 Python 大约快 30 倍。

选择快速工具对提升系统性能非常重要,因此我们对 Cassandra、PostgreSQL、Redis 以及其他一些技术进行了优化。然而,很多时候我们发现系统仍然存在瓶颈,而瓶颈正好在于我们的编程语言 Python。Python 在执行序列化、排序和聚合等计算密集型任务时需要花费很长的时间,有时比从网络上存取和检索数据花费的时间更长。我们知道这个时间是可以优化的。从 Python 切换到 Go 就可以缩短时间,这样一来,应用程序代码就更像是服务之间的粘合剂,而不再是优化中的主要瓶颈。

用 Go 编写的 Go 编译器也非常快。Stream 中最复杂的微服务就采用 Go 编写,它的编译时间仅仅需要 6 秒,Java 和 C++ 等工具链则慢得多,快则一分钟,慢则数小时。

名副其实的简单

简单是 Go 的重要特征!我敢向你保证,阅读 Go 语言的代码明显感觉更加简单。我们已经从多个 Python 代码库中迁移出来,我们发现这些 Python 代码的风格和框架会因为作者的不同而风格各异,往往带有很多作者个性化的东西。而 Go 恰恰相反,它推崇干净的代码风格,同时要求作者编写代码时严格遵守规范,禁止作者“自作聪明”。虽然这样有时候会使用更加冗长的代码,牺牲了代码的简洁性,但是却让代码更容易阅读和理解了。这样一来,Go 才得以加快开发人员阅读他人代码的速度,同时,阅读自己曾经编写的代码也更容易。

原生并发性

Go 在语言层面通过 goroutine 和 channel 支持了并发。此概念源自 Tony Hoare 的 CSP 模式,它让程序员处理并发变得不再困难。

goroutine 类似于操作系统的线程,但其运行消耗的系统资源更小,每个 goroutine 仅需几 KB 的堆栈空间。Go 运行时可以在操作系统线程之上处理多路 goroutine。虽然在后台执行,但它对于程序员来说是可见的。单个程序拥有数千个 goroutine 也并不罕见。比如,net/http 软件包中的服务器程序针对每个 HTTP 请求都会创建一个 goroutine。

在 Go 中启动 goroutine 非常简单,只需通过 go 关键字添加一个函数调用,即可启动一个 goroutine,并让该函数运行在自己的 goroutine 中。

Go 有一句重要的格言,即:不要通过共享内存来通信,相反,通过通信来共享内存 。Goroutine 之间通过 channel 进行通信,channel 的使用方法与 goroutine 一样简单。Channel 拥有类型,可以通过直观的箭头语法轻松实现 goroutine 之间的数据传递。尽管 channel 使用简单,但是其功能非常强大。在设计时只要预先稍作考虑,与传统的系统相比,使用 Go 便能够轻而易举地开发大规模并发系统。

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

语言生态

跟 C++ 和 Java 这样已经高度普及的传统语言相比,Go 仍然是编译语言领域的新手。虽然目前大约只有 5%的程序员知道 Go,但是得益于它的易用性,这个数字在不断增长。虽然 Go 语言速度快且功能强,但它只有 25 个保留字。相比于 C++ 的 92 个保留字,以及 Java 的 53 个保留字,Go 显得非常简洁。过多的保留字会增加程序员的学习成本。

由于 Go 上手非常容易,因此组建 Go 开发团队相比其他语言来说更容易。Go 初学者可以很快入门并精通该语言。这使得雇主甚至可以招聘其他背景的开发人员,然后加以短期培训即可使其成为合格的 Go 工程师。

Go 提供的内置库开箱即用且功能强大。使用“net/http”仅需几行代码即可实现 HTTP 服务器,并且还支持 http/2、TLS 和 websocket。Go 社区软件包的生态系统也很出色,已经出现了很多与 Redis、RabbitMQ、PostgreSQL、模板以及 RocksDB 相关的库,它们运行稳定且更新频繁。

其他优势

在前文中我提到了 Go 并不鼓励程序员“自作聪明”,它并没有提供可能会节省时间的功能,比如可嵌套的三元运算符。

Go 采用另一种方式来节省时间,它既没有选择制表符也没有选择空格,而是转而使用了 gofmt。它是一种命令行工具,可与大多数编辑器集成并自动将代码格式化为特定的格式。即使格式不正确代码仍会编译,但是拉取请求会被忽略,除非代码通过 gofmt 并且能够保持整个代码库格式一致。这使得代码评审人员能够专注在代码上,而不必在格式上浪费时间。

Go 有助于开发微服务。谷歌的 protobuf 和 gRPC 是微服务间通信的基础,Go 对它们提供了很好的支持。作为开发人员,我们只需在清单文件中定义一项服务,工具便会自动生成客户端和服务器端代码,并且保证代码的高性能以及很低的网络负载。此外,清单文件还可以被其他语言用来生成他们自己的客户端和服务器端代码。所以,如果我们决定用其他技术来替代部分架构,之后的任务会更加简单。

Python vs. Go

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

复制代码
{
"functions":{
"simple_gauss":{
"base":"decay_gauss",
"scale":"5d",
"offset":"1d",
"decay":"0.3"
},
"popularity_gauss":{
"base":"decay_gauss",
"scale":"100",
"offset":"5",
"decay":"0.5"
}
},
"defaults": {
"popularity": 1
},
"score":"simple_gauss(time)*popularity"
}
  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 比 Python 快得多,因此我们不需要花太多精力优化代码。最终的结果是,Go 代码的执行速度比精心优化的 Python 代码大约快 40 倍。

用 Go 来构建 Stream 系统中的某些组件相比用 Python 花费了更多的时间。总体来说,开发 Go 代码要花费更多的精力,但团队用来优化代码性能的时间则更少。

结论

Go 非常适用于开发微服务。它的速度非常快,具有原生并发原语,完美支持多种现有工具,并且开发起来乐趣无穷。与 Ruby 或 Python 等脚本语言相比,编写 Go 代码可能需要更长的时间,但其维护成本要低得多,加之其代码无需太多优化,因此你可以节省大量的时间。

需要注意的是,对于某些适合使用 Python 开发的模块,Stream 仍然使用 Python。例如,我们的仪表板、网站以及用于个性化订阅的机器学习都使用 Python 实现,因为 Python 提供的这些工具更好用。我们不会马上完全弃用 Python,但是对于性能要求较高的代码,我们今后会使用 Go 来编写。

原文链接: https://jaxenter.com/stream-switch-go-142279.html

感谢无明对本文的审校。

2018-07-21 18:222648

评论

发布
暂无评论
发现更多内容

运力升级助力算力流转,中国数字经济的加速时刻

脑极体

OpenInfra Days China 2022|SelectDB与你共享 Apache Doris 在互联网广告业务中的实践

SelectDB

MySQL 数据库 数据仓库 数据湖 Doris

倒数 3 天|RocketMQ 能力全景图即将发布,定义下一代消息队列未来方向

阿里巴巴云原生

阿里云 RocketMQ 云原生 消息队列

企业即时通讯软件有哪些功能?对企业有什么帮助?

WorkPlus

安装失败怎么办

和牛

测试

基于 eBPF 的 Kubernetes 可观测实践

阿里巴巴云原生

阿里云 Kubernetes 云原生 可观测

《机器学习理论到应用》电子书免费下载

计算机与AI

Python 机器学习 数据科学

【web自动化测试】Playwright快速入门,5分钟上手

和牛

自动化 测试 playwright Python. 8月月更

WEB前端面授培训课程

小谷哥

容器化 | 在 NFS 备份恢复 RadonDB MySQL 集群数据

RadonDB

MySQL 数据库 容器化 Kubernetes 集群 备份 & 恢复

如何模拟后台API调用场景,很细!

Liam

前后端分离 开发 Postman API 前后端协作

开发一套高容错分布式系统

JAVA活菩萨

Java 程序员 后端 java程序员 java编程

JWT主动校验Token是否过期

源字节1号

软件开发 后端开发

框架整合(二)- 使用Apache ShardingSphere实现数据分片

大菠萝蜜

MySQL 8月月更

荣科科技:未来主要围绕在线互联化、生态化和智能大数据运营这三个方向发展

WorkPlus

【K8s入门必看】第三篇 —— K8s必备基础概念大梳理

Albert Edison

Docker Kubernetes 容器 云原生 8月月更

从云计算到函数计算

阿里巴巴云原生

阿里云 Serverless 云原生 函数计算

浅谈运用低代码技术如何实现物流企业的降本增效

王平

深圳大数据培训多长时间可以找工作

小谷哥

《机器学习的随机矩阵方法》

计算机与AI

Python 机器学习 数学

字节二面被问到mysql事务与锁问题,我蚌埠住了

程序员小毕

Java MySQL 数据库 程序员 面试

硬核!阿里P7技术员身经百战总结出这份210页的Java突击面试指南

了不起的程序猿

阿里巴巴 算法 高并发 Java 面试 java程序员

荣耀互联对外开放,赋能智能硬件合作伙伴,促进全场景生态产品融合

荣耀开发者服务平台

服务器

武师叔

8月月更

移动平台助力推进智慧型科研院所信息化建设

WorkPlus

基础到高级涵盖11个技术,Alibaba最新出品711页Java面试神册真香

JAVA活菩萨

Java 程序员面试 大厂技能 秋招 大厂面经

web前端培训课程哪个比较好?

小谷哥

小程序+自定义插件的混合模式

Geek_99967b

小程序

【注册荣耀开发者】赢【荣耀70】手机

荣耀开发者服务平台

开发者 手机 活动 应用 荣耀

全新出品!Github总榜排行第七的SpringCloud生态全栈笔记我粉了

JAVA活菩萨

Java 程序员面试 大厂技能 秋招 大厂面经

荣耀发布开发者服务平台,智慧生态合作提速

荣耀开发者服务平台

Stream从Python切换到Go的原因_Python_Thierry Schellenbach_InfoQ精选文章