限时领|《AI 百问百答》专栏课+实体书(包邮)! 了解详情
写点什么

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:223102

评论

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

用APICloud开发iOS App Clip(苹果小程序)详细教程

YonBuilder低代码开发平台

小程序云开发 大前端 移动开发 APP开发

十年磨一剑,腾讯云数据库品牌重磅升级, “企业级分布式数据库TDSQL”来了!

数据君

腾讯云数据库品牌升级,大咖解读数据库三大变化

数据君

七大步骤、备战60天,4面拿下字节跳动offer:时间规划+知识点+画脑图+做笔记+看书+看视频+刷题刷题

Java 程序员 面试

产品思维和意识

让我思考一会儿

2020出行之变(三):智能交通的星罗棋布

脑极体

Flink 中极其重要的 Time 与 Window 详细解析(深度好文,建议收藏)

五分钟学大数据

大数据 flink

阿里P8亲测能实战落地的SpringCloud开发笔记已开源

996小迁

Java 程序员 架构 面试 SpringCloud

LeetCode题解:389. 找不同,ASCII码求和,JavaScript,详细注释

Lee Chen

算法 大前端 LeetCode

《程序员修炼之道》- 务实的哲学(1)

石云升

程序员 28天写作

在nodejs中创建child process

程序那些事

多线程 事件驱动 nodejs 并发 程序那些事

搞定万亿级MySQL海量存储的索引与分表设计实战

Java架构师迁哥

从根上理解高性能、高并发(六):通俗易懂,高性能服务器到底是如何实现的

JackJiang

网络编程 高并发 高性能 即时通讯

Flink SQL 在字节跳动的优化与实践

Apache Flink

flink

第二周-作业-胡赵凯

hisun胡

产品经理 产品经理训练营

极客时间产品训练营第二周作业

云随心

产品 第二周作业 产品训练营

挑战前端知识点HTTP/ECMAScript

我是哪吒

大前端 HTTP ES6

极客时间产品训练营第二周总结

云随心

产品 总结 产品训练营

懂点EXCEL就行!教你利用Python做数据筛选(上)

智分析

Python Excel 数据清洗

连肝7个晚上,总结了计算机网络的知识点!(共66条)

我是哪吒

程序员 面试 浏览器 计算机网络 HTTP

阿里巴巴十亿级并发系统设计手册已开源(2021最新版)

比伯

Java 编程 架构 面试 计算机

Vue.js笔试题解决业务中常见问题

我是哪吒

程序员 面试 Vue 大前端

惊呆了!某东Java大咖的MySQL笔记手册流传出来了,胜过看10本书

Java架构之路

Java 程序员 架构 面试 编程语言

第二周-总结-胡赵凯

hisun胡

产品经理 产品经理训练营

图解计算机中的数据表示形式

冰河

程序员 计算机 二进制数据

boltdb源码阅读

行如风

数据库 源码剖析 Go 语言

给新春一台S,给用户三个S:华为智慧屏的新旅程

脑极体

深圳程序员自谋生路的2020

鸠摩智首席音效师

开源 程序员 在线教育 创业者 深圳

品牌升级后,TBase更名为TDSQL和TDSQL-A,CynosDB更名为TDSQL-C

数据君

产品经理训练营第 0 期 第二次作业

孙行者

第0期 产品经理训练营

Postgreshub中文资源网介绍

PostgreSQLChina

数据库 postgresql 开源 软件 开源社区

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