从 Python 切换到 Go 的 9 个理由
云栖号资讯:【点击查看更多行业资讯】
在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来!
切换到一种新的编程语言通常是一件大事,特别是当团队成员对原始语言有丰富经验时。今年年初, Stream 将其主要编程语言从 Python 切换到了 Go。本文将会解释他们决定从 Python 切换到 Go 的一些原因。
使用 Go 的理由
- 理由 1:性能
Go 非常快。它的性能接近 Java 或 C。Go 的速度比 Python 快 30 倍。
- 理由 2:语言本身的性能很重要
对于许多应用程序而言,编程语言只是应用程序和数据库之间的粘合剂。语言本身的性能通常并不重要。
Stream 是一家 API 提供商,它为 500 家公司和超过 2 亿的最终用户提供了反馈基础设施。多年来,我们一直在优化 Cassandra、PostgreSQL、Redis 等软件的性能,但是现在我们已经达到了我们所使用编程语言的极限。
Python 是一门伟大的语言,但是对于序列化 / 反序列化、排序和聚合等示例,它的性能非常差。我们经常会遇到性能问题,Cassandra 花费 1ms 的时间来检索数据,而 Python 将其转换成对象则需要 10ms 的时间。
- 理由 3:开发人员的效率,而无需太多创新
请看下“如何开始学习 Go ”教程中的如下 Go 代码片段。
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”
- 将一个函数添加到内置函数列表中
- 通过魔术方法(Magic Method)重载运算符
这些特性非常有趣,但是,大多数程序员都认为这会增加阅读他人代码的难度。
Go 会迫使我们使用最基本的东西,这使得阅读他人代码变得更容易。
注:当然,“容易”取决于具体的项目。如果只是创建一个基本的 CRUD API,我仍然建议使用 Django& DRF 或 Rails。
- 理由 4 :并发和通道
作为一门编程语言,Go 总是尽可能地保持简单。它没有引入太多的新概念,因为它的目标是创建一门易于使用的编程语言。它唯一具有创新性的地方是 Goroutines(go 协程)和 Channels(通道)。Goroutines 是 Go 的轻量级线程解决方案,而 Channels 是与 Goss 交互的首选方式。
Goroutines 非常轻量,仅需要几千字节的额外内存。而且由于 Goroutine 如此轻量,因此可以同时运行数百甚至数千个 Goroutine。
我们可以使用 Channels 在 Goroutines 之间进行通信。Go 运行时处理所有的内部复杂性。基于 Goroutines 和 Channels 的并发方案使应用程序能够轻松使用所有可用的 CPU 内核并处理并发 IoO,而无需进行复杂的开发。与 Python/Java 相比,在 Goroutines 上运行函数只需要很少的固定代码。我们只需要使用关键字“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") }
https://tour.golang.org/concurrency/1
Go 的并发解决方案非常易于使用。与开发人员必须密切关注异步代码处理方式的 Node 相比,这是一个非常有趣的方案。
Go 并发的另一个关注点是竞态检测。它使应用程序能够很容易地知道异步代码中是否存在任何竞态条件。
以下是一些学习 Go 和 Channels 的重要资源:
https://gobyexample.com/channels
https://tour.golang.org/concurrency/2
http://guzalexander.com/2013/12/06/golang-channels-tutorial.html
https://www.golang-book.com/books/intro/10
https://www.goinggo.net/2014/02/the-nature-of-channels-in-go.html
https://softwareengineering.stackexchange.com/questions/222642/are-go-langs-goroutine-pools-just-green-threads
- 理由 5:编译速度快
用 Go 编写的最大的微服务项目只需 6 秒就可以编译完成。与 Java 和 C 等语言的龟速(turtle-speed)编译相比,Go 的极快编译速度是它的主要生产力。
- 理由 6:组件团队的能力
让我们从这些数据开始:Go 的开发人员没有 C 和 Java 的开发人员多。根据 StackOverflow 的统计,有 38%的开发人员使用 Java,19.3%的开发人员使用 C,但只有 4.6%的开发人员使用 Go。GitHub 数据也显示出了类似的趋势:Go 比 Erlang、Scala 和 Elixir 等语言使用得更广泛,但不如 Java 和 C 那么流行。
幸运的是,Go 是一门非常简单易学的语言。它只提供了我们需要的基本功能,而没有提供其他附加功能。它引入了一些新概念,例如“ defer ”声明和内置的“go routines”以及 Channels 并发管理等。团队中的任何 Python、Elixir、C、Scala 或 Java 开发人员都可以在一个月内学习会怎么使用 Go 编程,因为 Go 非常简单。
与其他语言相比,我们发现建立 Go 开发团队更加容易。如果我们在竞争激烈的环境中(例如在博尔德和阿姆斯特丹)招聘,这是一个非常重要的优势。
- 理由 7:强大的生态系统
生态系统对于我们这样规模的团队(大约 20 人)来说非常重要。如果你不得不重新设计所有的功能,你就不能为你的客户创造价值。Go 为我们经常使用的工具提供了强大的支持。例如,Redis、RabbitMQ、PostgreSQL、模板解析、任务调度、表达式解析和 DBRocks 都可以使用现有的库。
与其他新语言(例如 Rust 或 Elixir)相比,Go 具有巨大的生态系统优势。尽管它不能与 Java、Python 或 Node 相提并论,但是我们是可以找到许多能够满足基本需求的高质量软件包。
- 理由 8:Gofmt,强制代码格式化
Gofmt 是一个优秀的命令行程序,它内置于 Go 编译器中,可用于格式化代码。在功能方面,它类似于 Python 的 autopep 8。我们大多数人都不喜欢争论制表符(tabs)和空格(spaces),但格式化的目标始终是一致的,实际的格式标准则无关紧要。Gofmt 以一种形式化的方式来格式化代码,以避免所有这些争论。
- 理由 9:gRPC 以及 Protocol Buffers
Go 为 Protocol Buffers 和 gRPC 提供了一流的支持。它将这两个工具完美地结合在一起,构建了一个通过 RPC 进行通信的微服务。我们只需编写一个定义了 RPC 调用及其参数的清单文件,服务端和客户端就可以据此自动生成适当的代码了。这不仅速度快,而且网络占用空间小,使用起来更方便。
其他语言(如 C、Java、Python 和 Ruby)中的客户端代码也可以基于相同的清单文件生成。这样,就不会与内部 REST 接口发生冲突了,而且我们也不必每次都编写几乎相同的客户端和服务端代码。
使用 Golang 的缺点
- 缺点 1 :缺乏框架
Go 不像 Ruby 的 Rails、Python 或 Django 或 PHP 的 Laravel,它没有一个主要的框架。这个话题在 Go 社区引起了激烈的争论,许多人认为不应该使用现有的框架来启动项目。在某些情况下,我完全同意这一点。但是,如果我们想要构建一个简单的 CRUD API,那么使用 Django/DJRF、Rails Laravel 或 Phoenix 则会更简单。
- 缺点 2:错误处理
Go 通过简单地从函数中返回错误的形式来处理错误。尽管这种方案是可行的,但是它很容易失去错误的范围,从而很难向用户提供有价值的错误信息。错误包可以通过返回错误的上下文和错误堆栈来解决该问题。
还有一个问题,那就是它很容易忘记去处理错误。尽管诸如 errcheck 和 megacheck 之类的静态分析工具可以避免这些错误,但这始终并不完善。也许我们应该期待一种语言级别的错误处理方案。
- 缺点 3:包管理
Go 的包管理并不完善。默认情况下,它无法指定依赖项的特定版本,也无法创建可重用的构建方案。Python、Node 和 Ruby 都有更好的包管理系统。但是,如果能使用正确的工具,Go 的包管理也可以变得更简单。
我们可以使用 Dep 来管理指定固定版本的依赖项。此外,我们还提供了一个名为 VirtualGo 的开源工具,用于多项目管理。
Python vs Go
我们做了一个有趣的实验,用 Go 重写了原来由 Python 编写的 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" }
Python 和 Go 的代码都需要执行如下操作来支持此排序方法:
1.解析分数表达式,将“simple_gauss”转换为函数,输入活动并输出分数
2.通过 JSON 配置创建函数。例如,我们想要“simple_gauss”在 scale 为 5 天、offset 为 1 天、factor 为 0.3 时调用“decay_gauss”。
3.当字段没有值时,解析“defaults”配置并采用默认值。
4.从步骤 1 开始使用该函数,对 feed 中的所有活动进行评分。
开发 Python 版的排序(Sort )代码花了大约三天的时间,其中包括代码编写、单元测试和文档编写。接下来,我们花了大约 2 周的时间来优化代码。其中一种优化方法是将分数表达式 simple_gauss(time)*popularity 转换为抽象语法树。我们还实现了可用于预测分数的缓存逻辑。
相比之下,开发此代码的 Go 版花了大约四天的时间,并且在后期不需要进一步地优化性能。因此,尽管 Python 最初的开发速度更快,但是 Go 版最终需要的工作量更少。另一个优势是,Go 代码比我们高度优化的 Python 代码还要快 40 倍。
当然,这只是说明我们切换到 Go 后性能提升的一个简单示例:
- 排序代码是我用 Go 编写的第一个项目。
- Go 代码是在 Python 代码之后编写的,因此对项目的理解更加深入。
- Go 的表达式解析库的质量更高
你的经历可能会有所不同。与 Python 相比,使用 Go 构建系统中的某些其他组件需要花费更多的时间。通常,编写 Go 代码需要付出更多的努力。但是,优化代码性能所需的时间会更少。
Elixir vs Go
我们想要评估的另一种语言是 Elixir 。Elixir 是一门建立在 Erlang 虚拟机上的引人入胜的语言。我之所以这么说,是因为我们的一个项目团队非常精通该语言。
出于这个原因,我们注意到 Go 的原始性能更好。Go 和 Elixir 都能支持数千个并发请求。但是,如果我们查看单个请求的性能,Go 要快得多。我们选择 Go 的另一个原因是它的生态系统。对于我们需要的组件来说,Go 具有更成熟的库,而 Elixir 尚不适合用于生产。同时,也很难招聘到 Elixir 开发人员或对开发人员进行 Elixir 培训。
结 论
Go 是一种性能非常高的语言,并且它对并发的支持非常强大。它差不多与 C 和 Java 一样快了。尽管 Go 的编译速度比 Python 或 Ruby 慢,但我们可以节省出大量的优化代码时间。
Go 对于新手而言具有庞大的生态系统,它易于学习使用,具有超高的性能,并且对并发有强大的支持,此外,它还具有非常高效的开发环境。这些特性使 Go 成为开发人员最合适的选择。
【云栖号在线课堂】每天都有产品技术专家分享!
课程地址:https://yqh.aliyun.com/zhibo立即加入社群,与专家面对面,及时了解课程最新动态!
【云栖号在线课堂 社群】https://c.tb.cn/F3.Z8gvnK
原文发布时间:2020-04-19
本文作者:Shiv McIntyre
本文来自:“InfoQ”,了解相关信息可以关注“InfoQ”

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
SAP Commerce(原Hybris)的一些架构图,持续更新
版本号:v1.00 2020年4月13日 @TOC 模块图 layer chart 类型 类型继承 At the bottom is the Service layer, which includes fine-grained(细粒的; 精准的) business methods, such as the ones responsible for adding promotions to a cart, or for calculating the total value of the cart. These services expose the data model, which persists in the database. On top of the Service layer, there are facades, which implement specific business use-cases, such as adding a product to a cart, placing an order, or searching for a product. The...
- 下一篇
简述负载均衡&CDN技术
云栖号资讯:【点击查看更多行业资讯】在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来! 曾经见到知乎上有人问“为什么像facebook这类的网站需要上千个工程师维护?”,下面的回答多种多样,但总结起来就是:一个高性能的web系统需要从无数个角度去考虑他,大到服务器的布局,小到软件中某个文件的实现,甚至于某个循环内的运算如果出现不严谨都可能导致全盘崩溃。 上面提到web性能优化需要多个角度去考虑,我们无法考虑到所有的优化细节,但可以从我们已知的层面去优化,我们就先从网络层面说起。 ①网络请求路径: 在这个用例中我们可以很清晰的看出网络请求到返回的过程,虽然非常抽象,但足够我们以他为基础来进行优化了。 1)负载均衡 在计算机上负载均衡也类似如此,我们的大BOSS客户端将请求发送至服务器,然而一台服务器是无法承受很高的并发量的,我们就会将请求转发到其他服务器,当然真正的负载均衡架构并不是由一台server转发的另一台server,而在客户端与服务器端中间加入了一个负责分配请求的负载均衡硬件(软件)。 DNS 最早的负载均衡就是利用搭建本地DNS服务器实现的,实现方式简单易懂,为同...
相关文章
文章评论
共有0条评论来说两句吧...