切换到一种新的编程语言通常是一件大事,特别是当团队成员对原始语言有丰富经验时。今年年初,Stream将其主要编程语言从Python切换到了Go。本文将会解释他们决定从Python切换到Go的一些原因。
Go非常快。它的性能接近Java或C。Go的速度比Python快30倍。
对于许多应用程序而言,编程语言只是应用程序和数据库之间的粘合剂。语言本身的性能通常并不重要。
Stream是一家API提供商,它为500家公司和超过2亿的最终用户提供了反馈基础设施。多年来,我们一直在优化Cassandra、PostgreSQL、Redis等软件的性能,但是现在我们已经达到了我们所使用编程语言的极限。
Python是一门伟大的语言,但是对于序列化/反序列化、排序和聚合等示例,它的性能非常差。我们经常会遇到性能问题,Cassandra花费1ms的时间来检索数据,而Python将其转换成对象则需要10ms的时间。
请看下“如何开始学习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使我们能从正在编写的代码中获得很好的想法。例如,我们可以:
这些特性非常有趣,但是,大多数程序员都认为这会增加阅读他人代码的难度。
Go会迫使我们使用最基本的东西,这使得阅读他人代码变得更容易。
注:当然,“容易”取决于具体的项目。如果只是创建一个基本的CRUD API,我仍然建议使用Django&DRF或Rails。
作为一门编程语言,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的重要资源:
用Go编写的最大的微服务项目只需6秒就可以编译完成。与Java和C等语言的龟速(turtle-speed)编译相比,Go的极快编译速度是它的主要生产力。
让我们从这些数据开始: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开发团队更加容易。如果我们在竞争激烈的环境中(例如在博尔德和阿姆斯特丹)招聘,这是一个非常重要的优势。
生态系统对于我们这样规模的团队(大约20人)来说非常重要。如果你不得不重新设计所有的功能,你就不能为你的客户创造价值。Go为我们经常使用的工具提供了强大的支持。例如,Redis、RabbitMQ、PostgreSQL、模板解析、任务调度、表达式解析和DBRocks都可以使用现有的库。
与其他新语言(例如Rust或Elixir)相比,Go具有巨大的生态系统优势。尽管它不能与Java、Python或Node相提并论,但是我们是可以找到许多能够满足基本需求的高质量软件包。
Gofmt是一个优秀的命令行程序,它内置于Go编译器中,可用于格式化代码。在功能方面,它类似于Python的autopep 8。我们大多数人都不喜欢争论制表符(tabs)和空格(spaces),但格式化的目标始终是一致的,实际的格式标准则无关紧要。Gofmt以一种形式化的方式来格式化代码,以避免所有这些争论。
Go为Protocol Buffers和gRPC提供了一流的支持。它将这两个工具完美地结合在一起,构建了一个通过RPC进行通信的微服务。我们只需编写一个定义了RPC调用及其参数的清单文件,服务端和客户端就可以据此自动生成适当的代码了。这不仅速度快,而且网络占用空间小,使用起来更方便。
其他语言(如C、Java、Python和Ruby)中的客户端代码也可以基于相同的清单文件生成。这样,就不会与内部REST接口发生冲突了,而且我们也不必每次都编写几乎相同的客户端和服务端代码。
Go不像Ruby的Rails、Python或Django或PHP的Laravel,它没有一个主要的框架。这个话题在Go社区引起了激烈的争论,许多人认为不应该使用现有的框架来启动项目。在某些情况下,我完全同意这一点。但是,如果我们想要构建一个简单的CRUD API,那么使用Django/DJRF、Rails Laravel或Phoenix则会更简单。
Go通过简单地从函数中返回错误的形式来处理错误。尽管这种方案是可行的,但是它很容易失去错误的范围,从而很难向用户提供有价值的错误信息。错误包可以通过返回错误的上下文和错误堆栈来解决该问题。
还有一个问题,那就是它很容易忘记去处理错误。尽管诸如errcheck和megacheck之类的静态分析工具可以避免这些错误,但这始终并不完善。也许我们应该期待一种语言级别的错误处理方案。
Go的包管理并不完善。默认情况下,它无法指定依赖项的特定版本,也无法创建可重用的构建方案。 Python、Node和Ruby都有更好的包管理系统。但是,如果能使用正确的工具,Go的包管理也可以变得更简单。
我们可以使用Dep来管理指定固定版本的依赖项。此外,我们还提供了一个名为VirtualGo的开源工具,用于多项目管理。
我们做了一个有趣的实验,用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的代码都需要执行如下操作来支持此排序方法:
开发Python版的排序(Sort )代码花了大约三天的时间,其中包括代码编写、单元测试和文档编写。接下来,我们花了大约2周的时间来优化代码。其中一种优化方法是将分数表达式simple_gauss(time)*popularity 转换为抽象语法树。我们还实现了可用于预测分数的缓存逻辑。
相比之下,开发此代码的Go版花了大约四天的时间,并且在后期不需要进一步地优化性能。因此,尽管Python最初的开发速度更快,但是Go版最终需要的工作量更少。另一个优势是,Go代码比我们高度优化的Python代码还要快40倍。
当然,这只是说明我们切换到Go后性能提升的一个简单示例:
你的经历可能会有所不同。与Python相比,使用Go构建系统中的某些其他组件需要花费更多的时间。通常,编写Go代码需要付出更多的努力。但是,优化代码性能所需的时间会更少。
我们想要评估的另一种语言是Elixir。 Elixir是一门建立在Erlang虚拟机上的引人入胜的语言。我之所以这么说,是因为我们的一个项目团队非常精通该语言。
出于这个原因,我们注意到Go的原始性能更好。 Go和Elixir都能支持数千个并发请求。但是,如果我们查看单个请求的性能,Go要快得多。我们选择Go的另一个原因是它的生态系统。 对于我们需要的组件来说,Go具有更成熟的库,而Elixir尚不适合用于生产。同时,也很难招聘到Elixir开发人员或对开发人员进行Elixir培训。
Go是一种性能非常高的语言,并且它对并发的支持非常强大。它差不多与C和Java一样快了。尽管Go的编译速度比Python或Ruby慢,但我们可以节省出大量的优化代码时间。
Go对于新手而言具有庞大的生态系统,它易于学习使用,具有超高的性能,并且对并发有强大的支持,此外,它还具有非常高效的开发环境。这些特性使Go成为开发人员最合适的选择。
如果你想要了解更多关于Go的信息,请阅读下面列出的文章。如果想了解更多关于Stream的信息,请浏览此交互式教程。
相关阅读:
Go学习资料:
原文链接:
领取专属 10元无门槛券
私享最新 技术干货