为什么我们放弃 python 而选择 go?(getstream.io 的架构变迁) | go优质外文翻译 | go 技术论坛-江南app体育官方入口
更新于2019年5月14日, 为了更好的反映过去两年 go 的提升(包管理,更好的性能,更快的编译时间和更成熟的生态系统)。 切换到新的编程语言总归来说是一大步改动,特别是团队就你一人有该语言的使用经验。年初,我们把 的主要编程语言从 python 切换到 go。这篇文章将解释为甚我们决定放弃python并转而使用go。
理由一、性能
go 很快!go 是相当的快。其性能比肩 java 或 c 。在我们的用例中,go 通常比 python 快40倍。这里有一个 小的基准比较游戏。
理由二、语言性能很重要
对于大部分应用来说,编程语言只是应用程序和数据库之间的粘合剂。语言本身的表现通常无关紧要。然而,stream 一个提供 api 的程序,为 700 家公司和 5 亿多最终用户提供 和 的基础设施。多年来,我们一直在优化 cassandra、postgresql、redis 等,但最终,达到了所使用语言的极限。python 是一种很棒的语言,但是对于序列化/反序列化、排名和聚合等用例来说,它的性能相当缓慢。我们经常遇到性能问题,cassandra 需要 1 毫秒来检索数据,而 python 则需要 10 毫秒来将其转换为对象。
理由三、开发人员的生产率和创新能力不足
package main
type openweathermap struct{}
func (w openweathermap) temperature(city string) (float64, error) {
resp, err := http.get("http://api.openweathermap.org/data/2.5/weather?appid=your_api_key&q=" city)
if err != nil {
return 0, err
}
defer resp.body.close()
var d struct {
main struct {
kelvin float64 json:"temp"
} json:"main"
}
if err := json.newdecoder(resp.body).decode(&d); err != nil {
return 0, err
}
log.printf("openweathermap: %s: %.2f", city, d.main.kelvin)
return d.main.kelvin, nil
}
即便是 go 语言的新手,在阅读这小代码片段时,也不会出现难以理解的地方。它演示了多赋值函数,数据结构,指针,格式和内置的 http 库。刚开始编程时,我总是喜欢使用 python 的高级功能。使用 python 编码时你可以发挥创意。例如,你可以:
- 在代码初始化时使用 metaclasses 自注册类
- 交换 true 和 false
- 将函数添加到内置函数列表中
- 通过魔术方法重载运算符
- 通过
@property
装饰器将函数用作属性
这些功能很有趣,但是正如大多数程序员所同意的那样,它们通常使代码可读性降低,使你难以理解别人的工作。 go 迫使您坚持基础知识。这样一来,您就可以轻松阅读任何人的代码并立即了解发生了什么。 注:到底有多“简单”取决于您的用例。如果您想创建基本的crud api,我仍然建议您使用django 或 rails。
理由 4 - 并发与通道
作为一种语言,go 尽量使事情简单化。它没有引入很多新概念。go 语言的作者致力于创建一种,速度非常快,并且易于使用的语言。它唯一创新的领域是协程和通道。(准确的来说, 的概念始于1977 年,所以这一创新更多的是一种对旧思想的新方法。) 协程是 go 的线程化轻量级方法,通道是协程之间通信的首选方式。协程的创建成本很低,只需要几 kb 的额外内存。因为协程是轻量级的,所以有可能同时运行数百甚至数千个协程。您可以使用通道在协程之间进行通信。go 运行时处理所有的复杂性。协程和基于通道的并发方法使得使用所有可用的 cpu 内核和处理并发 io 变得非常容易,而不需要复杂的开发。与 python/java 相比,在协程上运行函数只需要少量的代码。你只需在函数调用前加上关键字「go」:
package main
import (
"fmt"
"time"
)
func say(s string) {
for i := 0; i < 5; i {
time.sleep(100 * time.millisecond)
fmt.println(s)
}
}
func main() {
go say("world")
say("hello")
}
go 的并发方法非常容易使用。与开发人员必须密切关注异步代码如何处理的 node 相比,这是一种有趣的方法。go 中并发性的另一个重要方面是。这样就很容易判断异步代码中是否存在竞争条件。
咚咚!(敲门声) 竞争条件你在哪?
— 我是开发人员(@iamdevloper)
这里有一些好的资源可以帮助你开始使用 go 和通道:
理由 5 - 快速的编译时间
我们用 go 编写的最大的微服务目前需要4秒来编译。与 java 和 c 等以编译速度缓慢著称的语言相比,go 的快速编译时间是提高生产力的一大优势。我喜欢剑斗,但是在我还记得代码应该做什么的情况下完成工作会更好:
理由 6 - 建立一个团队的能力
首先,让我们从一个明显的事实开始:与 c 和 java 等老牌的语言相比,go 开发人员并不多。根据 来看, 38% 的开发者知道 java, 19.3% 的知道 c ,只有仅仅 4.6% 的知道 go。显示了一个:go 比 erlang、scala 和 elixir 等语言使用得更广泛,但不如 java 和 c 流行。幸运的事,go 是一门非常简单易学的编程语言。它只提供了您需要的基本功能。它引入的新概念是「」语句和内置的「go 协程」和通道并发管理。(对于纯粹主义者来说:go 并不是实现这些概念的第一种语言,它只是使这些概念流行起来的第一种语言。) 任何加入团队的 python、elixir、c 、scala 或 java dev 都可以在一个月内高效地使用 go,因为它非常简单。我们发现,与其他语言相比,建立一个 go 开发团队更容易。如果你在竞争激烈的招聘环境中招聘员工,这是非常有益的。
理由 7 - 强大的生态系统
对于我们这样规模的团队(约20人)来说,生态系统很重要。如果你必须重新设计每一个小功能,你就无法为你的客户创造价值。go 为我们使用的工具提供了强大的支持。go 可靠的库已经可以用于 redis、rabbitmq、postgresql、模板解析、任务调度、表达式解析和 rocksdb。与 rust 或 elixir 等新语言相比,go 的生态系统是一个重大的胜利。它当然没有 java、python 或 node 等语言那么好,但它是可靠的,对于许多基本需求,您可以找到高质量的包。
理由 8 - gofmt, 强制格式化代码
让我们从什么是 gofmt 开始? 不,这不是骂人的话。gofmt 是一个非常棒的命令行实用工具,内置在 go 编译器中,用于格式化代码。在功能方面,它与 python 的 autopep8 非常相似。尽管硅谷的描述与此不同,但我们大多数人并不是真的喜欢讨论制表符与空格之间的区别。格式的一致性很重要,但是实际的格式标准并没有那么重要。gofmt 通过一种官方的格式来避免这些讨论。
理由 9 - grpc 和协议缓冲区
go 对协议缓冲区和 grpc 有一流的支持。这两个工具在构建需要通过 rpc 进行通信的微服务时配合得非常好。您只需要编写一个清单,其中定义可以进行的 rpc 调用,以及它们采用的参数。然后,服务器和客户端代码都会从这个清单中自动生成。这个生成的代码速度很快,占用的网络空间很小,而且很容易使用。通过相同的清单,您甚至可以为许多不同的语言(如 c 、java、python 和 ruby)生成客户端代码。因此,对于内部通信流,不再有不明确的 rest 端点,您也不必每次都编写几乎相同的客户端和服务器代码。
缺点 1 - 缺少框架
go 缺少一个主流的框架,比如 ruby 的 rails、python 的 django 或 php 的 laravel。这是 go 社区中的一个热议话题,因为许多人主张不应该从使用框架开始。我完全同意在某些用例中是这样的。但是,如果有人想构建一个简单的crud api,那么他们将更容易使用 django/djrf、rails、laravel 或 .
更新: 正如评论指出的 目前有几个不错的 go 框架。,,、 和 似乎都是不错的选项。
对于 stream 的用例,我们更喜欢不使用框架。然而,对于许多希望提供简单 crud api 的新项目来说,缺少一个主导框架将是一个严重的缺点。
缺点 2 - 错误处理
go 通过简单地从一个函数返回一个错误并期望你的调用代码来处理这个错误(或者将它返回到调用堆栈)来处理错误。虽然这种方法有效,但是不能有效的定位错误位置,从而无法确保您可以向用户提供有意义的错误。解决了这个问题,它允许您向错误添加上下文和堆栈跟踪。另一个问题是它很容易忘记处理意外的错误。像 errcheck 和 megacheck 这样的静态分析工具可以很方便地避免犯这些错误。虽然这些变通方法很有效,但感觉却不太对。您希望该语言支持正确的错误处理。
缺点 3 - 包管理
更新:自本文撰写以来,go 的包管理系统已经有了很大的进步。 是一个有效的江南app体育官方入口的解决方案,我看到的唯一问题是它们破坏了一些静态分析工具,比如 errcheck。这里有一个学习使用的教程。go 的包管理绝不是完美的。默认情况下,它无法指定依赖项的特定版本,也无法创建可复制的构建。python、node 和 ruby 都有更好的包管理方式。然而,只要有合适的工具,go 的包管理就可以很好地工作。您可以使用 来管理依赖项,以允许指定和固定版本。除此之外,我们还提供了一个叫做 的开源工具,它可以让你更轻松地在多个项目中工作。
python vs go
更新:自本文撰写以来,python 和 go 之间的性能差异有所增加。( go 变得更快了,而 python 没有) 我们进行了一个有趣的实验,用 python 实现了功能,然后用 go 重写它。看看这个排名方法的例子:
{
"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"
}
python 和 go 代码都需要执行以下操作来支持这种排序方法:
- 解析表达式以获取分数。在本例中,我们希望将这个字符串「simple_gauss(time)* popularity」转换为一个函数,该函数将活动作为输入返回分数作为输出。
- 基于 json 配置创建局部函数。例如,我们希望「simple_gauss」以 5 天的比例,1 天的偏移量和 0.3 的衰减因子来调用「decay_gauss」。
- 解析「默认」配置,这样,如果某个字段没有在某个活动上定义,您就可以进行回退。
- 使用步骤 1 中的函数对提要中的所有活动进行评分。
开发 python 版本的排名代码大约需要 3 天。这包括编写代码,单元测试和文档。接下来,我们花了大约 2 周的时间来优化代码。一种优化是将得分表达式 simple_gauss(time)* popularity)
转换为。我们还实现了缓存逻辑,该逻辑可在将来的某些时间预先计算分数。相反,开发此代码的 go 版本大约需要 4 天。性能不需要任何进一步的优化。因此,虽然 python 的初始开发速度更快,但基于 go 的版本最终需要我们团队做的工作却少得多。另外一个优势是,go 代码的执行速度比高度优化的 python 代码快 40 倍。现在,这只是我们切换到 go 所获得的性能提升的一个示例。当然,这是将苹果与橙子进行比较:
- 排名代码是我在 go 中的第一个项目
- go 代码是在 python 代码之后构建的,因此可以更好地理解用例
- 用于表达式解析的 go 库具有非常高的质量
速度的优势需视场景而定。与 python 相比,我们系统的其他一些组件在 go 中构建花费的时间要多得多。作为一个大趋势,我们发现开发 go 代码开发起来需要花费更多的时间。但是,我们花费更少的时间 优化 代码的性能。
elixir vs go - 谁跑的更快
我们评估的另一种语言是. elixir构建在erlang虚拟机之上。这是一种令人着迷的语言,因为我们的团队成员之一对erlang有着丰富的经验,因此我们考虑了这一点。对于我们的用例,我们注意到go的原始性能要好得多。 go和elixir都将在处理数千个并发请求方面做得很好。但是,如果您查看单个请求的性能,那么对于我们的用例而言,go实质上要快得多。我们选择go over elixir的另一个原因是生态系统。对于我们所需的组件,go具有更成熟的库,而在许多情况下,elixir库尚未准备好用于生产。培训/寻找开发人员与elixir合作也更加困难。这些原因使go变得更加平衡。不过,elixir的phoenix框架看起来很棒,绝对值得一看。
结论
go是一种性能非常好的语言,具有很好的并发性支持。它几乎与c 和java等语言一样快。虽然与python或ruby相比,使用go构建东西确实需要更多的时间,但您可以节省大量的时间用于优化代码。我们在 有一个小型开发团队,为超过5亿的最终用户提供feed和 。 go结合了强大的生态系统,新开发者易于入门,快速性能,并发可靠支持和高效的编程环境的组合,使其成为绝佳的选择。 stream仍然将python用于的仪表板,站点和机器学习。 我们不会在不久的将来与python道别,但今后所有性能密集型代码都将用go编写。我们的新 也完全用go编写。如果您想了解有关go的更多信息,请查看下面列出的博客文章。 如果您想了解有关go的更多信息,请查看下面列出的博客文章。要了解有关stream的更多信息, 是一个很好的起点。
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 cc 协议,如果我们的工作有侵犯到您的权益,请及时联系江南app体育官方入口。
原文地址:
这是一篇很棒的文章,当事人讲解架构 变迁
翻译失败,有一些单词实在不知道怎么把握语义翻译才合适。
@icy 翻译是一个像『编程』一样的技术活哈,需要多练习才能不断提高。
不要纠结于原文,尽量意译:
主流框架没有 beego :grin:
正解 go vs python