切换到新语言始终是一大步,尤其是当您的团队成员只有一个时有该语言的先前经验。现在,Stream的主要编程语言从Python切换到了Go。这篇文章将解释stream决定放弃Python并转向Go的一些原因。原因1性能 Go非常快。性能类似于Java或C。对于用例,Go通常比Python快40倍。 原因2语言表现很重要 对于许多应用程序来说,编程语言只是应用程序和数据库之间的粘合剂。语言本身的性能通常并不重要。然而,Stream是一个API提供商,为700家公司和超过5亿最终用户提供提要和聊天平台。多年来,我们一直在优化Cassandra、PostgreSQL、Redis等,但最终,您会达到所使用语言的极限。Python是一门很棒的语言,但对于序列化反序列化、排名和聚合等用例,它的性能相当缓慢。我们经常遇到性能问题,Cassandra需要1毫秒来检索数据,而Python会花费接下来的10毫秒将其转换为对象。 原因3开发人员的生产力和缺乏创意 看看我如何开始Go教程中的一小段Go代码。(这是一个很棒的教程,也是学习Go的一个很好的起点。)packagemaintypeopenWeatherMapstruct{}func(wopenWeatherMap)temperature(citystring)(float64,error){resp,err:http。Get(http:api。openweathermap。orgdata2。5weather?APPIDYOURAPIKEYqcity)iferr!nil{return0,err}deferresp。Body。Close()vardstruct{Mainstruct{Kelvinfloat64json:temp}json:main}iferr:json。NewDecoder(resp。Body)。Decode(d);err!nil{return0,err}log。Printf(openWeatherMap:s:。2f,city,d。Main。Kelvin)returnd。Main。Kelvin,nil} 如果您是Go新手,那么在阅读那个小代码片段时不会有太多让您感到惊讶的事情。它展示了多个赋值、数据结构、指针、格式和一个内置的HTTP库。当我第一次开始编程时,我一直喜欢使用Python更高级的功能。Python允许您在编写代码时获得相当的创意。例如,您可以:在代码初始化时使用MetaClasses自注册类交换True和False将函数添加到内置函数列表通过魔术方法重载运算符通过property装饰器将函数用作属性 这些功能玩起来很有趣,但是,正如大多数程序员会同意的那样,在阅读别人的作品时,它们通常会使代码更难理解。Go迫使你坚持基础。这使得阅读任何人的代码并立即了解发生了什么变得非常容易。注意:当然,它实际上有多容易取决于您的用例。如果你想创建一个基本的CRUDAPI,我仍然推荐DjangoDRF或Rails。 原因4并发和通道 作为一门语言,Go试图让事情变得简单。它没有引入许多新概念。重点是创建一种非常快速且易于使用的简单语言。它唯一具有创新性的领域是goroutine和通道。(100正确CSP的概念始于1977年,所以这项创新更多是对旧思想的一种新方法。)Goroutines是Go的轻量级线程方法,通道是goroutines之间通信的首选方式。Goroutines的创建非常便宜,并且只需要几KB的额外内存。因为Goroutine非常轻量,所以有可能同时运行数百甚至数千个。您可以使用通道在goroutine之间进行通信。Go运行时处理所有复杂性。goroutines和基于通道的并发方法使得使用所有可用的CPU内核和处理并发IO变得非常容易所有这些都不会使开发复杂化。与PythonJava相比,在goroutine上运行函数需要最少的样板代码。您只需在函数调用前加上关键字go:packagemainimport(fmttime)funcsay(sstring){fori:0;i5;i{time。Sleep(100time。Millisecond)fmt。Println(s)}}funcmain(){gosay(world)say(hello)} Go的并发方法很容易使用。与Node相比,这是一种有趣的方法,开发人员必须密切关注异步代码的处理方式。Go中并发的另一个重要方面是竞争检测器。这样可以很容易地确定异步代码中是否存在任何竞争条件。 原因5快速编译时间 我们目前用Go编写的最大的微服务编译需要4秒。与以编译速度慢而闻名的Java和C等语言相比,Go的快速编译时间是一项重大的生产力胜利。我喜欢在程序编译的时候摸鱼,但在我还记得代码应该做什么的同时完成事情会更好。 理由6建立团队的能力 首先,让我们从显而易见的开始:与C和Java等旧语言相比,Go开发人员的数量并不多。根据StackOverflow的数据,38的开发人员知道Java,19。3的人知道C,只有4。6的人知道Go。GitHub数据显示了类似的趋势:Go比Erlang、Scala和Elixir等语言使用更广泛,但不如Java和C流行。幸运的是,Go是一种非常简单易学的语言。它提供了您需要的基本功能,仅此而已。它引入的新概念是延迟声明和内置的并发管理与goroutines和通道。(对于纯粹主义者来说:Go并不是第一种实现这些概念的语言,只是第一种使它们流行起来的语言。)任何加入团队的Python、Elixir、C、Scala或Java开发人员都可以在一个月内在Go上发挥作用,因为它的简单性。与许多其他语言相比,我们发现组建Go开发人员团队更容易。如果您在博尔德和阿姆斯特丹等竞争激烈的生态系统中招聘人员,这是一项重要的优势。 理由7强大的生态系统 对于我们这样规模的团队(约20人)来说,生态系统很重要。如果您必须重新发明每一个小功能,您根本无法为您的客户创造价值。Go对我们使用的工具有很好的支持。实体库已经可用于Redis、RabbitMQ、PostgreSQL、模板解析、任务调度、表达式解析和RocksDB。与Rust或Elixir等其他较新的语言相比,Go的生态系统是一个重大胜利。它当然不如Java、Python或Node之类的语言好,但它很可靠,而且对于许多基本需求,你会发现已经有高质量的包可用。 原因8Gofmt,强制代码格式化 Gofmt是一个很棒的命令行实用程序,内置在Go编译器中,用于格式化代码。就功能而言,它与Python的autopep8非常相似。我们大多数人并不真正喜欢争论制表符与空格。格式的一致性很重要,但实际的格式标准并不那么重要。Gofmt通过使用一种正式的方式来格式化您的代码来避免所有这些讨论。 原因9gRPC和协议缓冲区 Go对协议缓冲区和gRPC具有一流的支持。这两个工具非常适合构建需要通过RPC通信的微服务。您只需要编写一个清单,在其中定义可以进行的RPC调用以及它们采用的参数。然后从这个清单中自动生成服务器和客户端代码。生成的代码既快速又具有非常小的网络占用空间并且易于使用。从同一个清单中,您甚至可以为许多不同的语言生成客户端代码,例如C、Java、Python和Ruby。因此,内部流量不再有模棱两可的REST端点,您每次都必须编写几乎相同的客户端和服务器代码。。 缺点1缺乏框架 Go没有像Rails用于Ruby、Django用于Python或Laravel用于PHP那样的单一主导框架。这是Go社区内激烈争论的话题,因为许多人主张你不应该一开始就使用框架。我完全同意这对于某些用例是正确的。但是,如果有人想构建一个简单的CRUDAPI,他们将更容易使用DjangoDJRF、RailsLaravel或Phoenix。对于Stream的用例,我们更喜欢不使用框架。然而,对于许多希望提供简单CRUDAPI的新项目来说,缺乏主导框架将是一个严重的劣势。 缺点2错误处理 Go通过简单地从函数返回错误并期望调用代码来处理错误(或将其返回到调用堆栈)来处理错误。虽然这种方法有效,但很容易失去问题的范围,以确保您可以向用户提供有意义的错误。错误包通过允许您向错误添加上下文和堆栈跟踪来解决此问题。另一个问题是很容易忘记处理错误。像errcheck和megacheck这样的静态分析工具可以方便地避免犯这些错误。虽然这些变通办法效果很好,但感觉不太对劲。您希望该语言支持正确的错误处理。 缺点3包管理 Go的包管理绝不是完美的。默认情况下,它无法指定特定版本的依赖项,也无法创建可重现的构建。Python、Node和Ruby都有更好的包管理系统。但是,使用正确的工具,Go的包管理工作得很好。您可以使用Dep来管理您的依赖项,以允许指定和固定版本。除此之外,我们还贡献了一个名为的开源工具VirtualGo,它可以更轻松地处理用Go编写的多个项目。 PythonvsGo 我们进行的一个有趣的实验是在Python中使用我们的排名提要功能并在Go中重写它。看看这个排名方法的例子:{functions:{simplegauss:{base:decaygauss,scale:5d,offset:1d,decay:0。3},popularitygauss:{base:decaygauss,scale:100,offset:5,decay:0。5}},defaults:{popularity:1},score:simplegauss(time)popularity} Python和Go代码都需要执行以下操作来支持这种排名方法:解析分数的表达式。在这种情况下,我们希望将字符串simplegauss(time)popularity转换为一个函数,该函数将活动作为输入并返回分数作为输出。基于JSON配置创建部分函数。例如,我们希望simplegauss调用decaygauss,尺度为5天,偏移量为1天,衰减因子为0。3。解析默认配置,以便在未在活动上定义某个字段时进行备用。使用步骤1中的函数对提要中的所有活动进行评分。 开发Python版本的排名代码大约花了3天时间。这包括编写代码、单元测试和文档。接下来,我们花了大约2周的时间优化代码。其中一项优化是将分数表达式(simplegauss(time)popularity)转换为抽象语法树。我们还实现了缓存逻辑,可以在未来的特定时间预先计算分数。相比之下,开发此代码的Go版本大约需要4天时间。性能不需要任何进一步的优化。因此,虽然Python的最初开发速度更快,但基于Go的版本最终需要我们团队的工作量大大减少。另外一个好处是,Go代码的执行速度比我们高度优化的Python代码快大约40倍。现在,这只是我们通过切换到Go体验到的性能提升的一个示例。 与Python相比,我们系统的其他一些组件在Go中构建所需的时间要多得多。作为一个总体趋势,我们看到开发Go代码需要更多的努力。但是,我们花更少的时间优化代码以提高性能。 ElixirvsGo亚军ElixirvsGoTheRunnerUp 我们评估的另一种语言是Elixir。。Elixir建立在Erlang虚拟机之上。这是一种迷人的语言,我们之所以考虑它,是因为我们的一名团队成员在Erlang方面拥有丰富的经验。对于我们的用例,我们注意到Go的原始性能要好得多。Go和Elixir都可以很好地服务数千个并发请求。但是,如果您查看单个请求的性能,Go对于我们的用例来说要快得多。我们选择Go而不是Elixir的另一个原因是生态系统。对于我们需要的组件,Go有更成熟的库,而在许多情况下,Elixir库还没有准备好用于生产环境。培训寻找开发人员使用Elixir也更加困难。这些原因使天平向Go倾斜。Elixir的Phoenix框架看起来很棒,绝对值得一看。 结论 Go是一种非常高性能的语言,对并发有很好的支持。它几乎与C和Java等语言一样快。虽然与Python或Ruby相比,使用Go构建东西确实需要更多时间,但您将节省大量用于优化代码的时间。我们在Stream有一个小型开发团队,为超过5亿最终用户提供动力和聊天。Go结合了强大的生态系统、新开发人员的轻松入门、快速的性能、对并发的可靠支持和高效的编程环境,使其成为一个不错的选择。Stream仍然在我们的仪表板、站点和机器学习中利用Python来提供个性化的订阅源。我们不会很快与Python说再见,但今后所有性能密集型代码都将使用Go编写。我们新的聊天API也完全用Go编写。