大橙子网站建设,新征程启航
为企业提供网站建设、域名注册、服务器等服务
《Go语言编程》(许式伟)电子书网盘下载免费在线阅读
为五莲等地区用户提供了全套网页设计制作服务,及五莲网站建设行业解决方案。主营业务为做网站、成都网站设计、五莲网站设计,以传统方式定制建设网站,并提供域名空间备案等一条龙服务,秉承以专业、用心的态度为用户提供真诚的服务。我们深信只要达到每一位用户的要求,就会得到认可,从而选择与我们长期合作。这样,我们也可以走得更远!
链接:
提取码:j0if
书名:Go语言编程
作者:许式伟
豆瓣评分:7.1
出版社:人民邮电出版社
出版年份:2012-8
页数:300
内容简介:
这本书从整体的写作风格来说,会以介绍 Go 语言特性为主,示例则尽量采用作者平常的实践,而不是一个没有太大实际意义的语法示范样例。
本书作者背景极强,许式伟为原金山WPS首席架构师、曾是盛大创新院研究员,目前是国内Go语言实践圈子公认的Go语言专家。参与本书写作的几位作者都是实际用Go语言开发的项目的开发人员,有较强的实战经验。
本书以介绍Go语言特性为主,示例则尽量采用作者开发团队平常的实践,内容涉及内存管理(堆和栈)、错误处理、OOP、并发编程等关键话题。 这本书面向的读者是所有打算用Go语言的开发者,主要包括目前使用C、C++、Java、C#的开发人员,甚至一些Python、PHP开发人员也可能转为 Go 程序员。
作者简介:
许式伟
七牛云存储CEO,曾任盛大创新院资深研究员、金山软件技术总监、WPS Office 2005首席架构师。开源爱好者,发布过包括WINX、TPL等十余个C++开源项目,拥有超过15年的C/C++开发经验。在接触Go语言后即可被其大道至简、少即是多的设计哲学所倾倒。七牛云存储是国内第一个吃螃蟹的团队,核心服务完全采用Go语言实现。
吕桂华
七牛云存储联合创始人,曾在金山软件、盛大游戏等公司担任架构师和部门经理等职务,在企业级系统和大型网游平台领域有较多涉猎。拥有十余年的C/C++大型项目开发经验,也曾在Java和.NET平台上探索多年。同样被Go语言的魅力所吸引而不可自拔,希望能为推广这门优秀的语言尽自己的绵薄之力。
Go 语言较之 C 语言一个很大的优势就是自带 GC 功能,可 GC 并不是没有代价的。写 C 语言的时候,在一个函数内声明的变量,在函数退出后会自动释放掉,因为这些变量分配在栈上。如果你期望变量的数据可以在函数退出后仍然能被访问,就需要调用 malloc 方法在堆上申请内存,如果程序不再需要这块内存了,再调用 free 方法释放掉。Go 语言不需要你主动调用 malloc 来分配堆空间,编译器会自动分析,找出需要 malloc 的变量,使用堆内存。编译器的这个分析过程就叫做逃逸分析。
所以你在一个函数中通过 dict := make(map[string]int) 创建一个 map 变量,其背后的数据是放在栈空间上还是堆空间上,是不一定的。这要看编译器分析的结果。
可逃逸分析并不是百分百准确的,它有缺陷。有的时候你会发现有些变量其实在栈空间上分配完全没问题的,但编译后程序还是把这些数据放在了堆上。如果你了解 Go 语言编译器逃逸分析的机制,在写代码的时候就可以有意识地绕开这些缺陷,使你的程序更高效。
Go 语言虽然在内存管理方面降低了编程门槛,即使你不了解堆栈也能正常开发,但如果你要在性能上较真的话,还是要掌握这些基础知识。
这里不对堆内存和栈内存的区别做太多阐述。简单来说就是, 栈分配廉价,堆分配昂贵。 栈空间会随着一个函数的结束自动释放,堆空间需要时间 GC 模块不断地跟踪扫描回收。如果对这两个概念有些迷糊,建议阅读下面 2 个文章:
这里举一个小例子,来对比下堆栈的差别:
stack 函数中的变量 i 在函数退出会自动释放;而 heap 函数返回的是对变量 i 的引用,也就是说 heap() 退出后,表示变量 i 还要能被访问,它会自动被分配到堆空间上。
他们编译出来的代码如下:
逻辑的复杂度不言而喻,从上面的汇编中可看到, heap() 函数调用了 runtime.newobject() 方法,它会调用 mallocgc 方法从 mcache 上申请内存,申请的内部逻辑前面文章已经讲述过。堆内存分配不仅分配上逻辑比栈空间分配复杂,它最致命的是会带来很大的管理成本,Go 语言要消耗很多的计算资源对其进行标记回收(也就是 GC 成本)。
Go 编辑器会自动帮我们找出需要进行动态分配的变量,它是在编译时追踪一个变量的生命周期,如果能确认一个数据只在函数空间内访问,不会被外部使用,则使用栈空间,否则就要使用堆空间。
我们在 go build 编译代码时,可使用 -gcflags '-m' 参数来查看逃逸分析日志。
以上面的两个函数为例,编译的日志输出是:
日志中的 i escapes to heap 表示该变量数据逃逸到了堆上。
需要使用堆空间,所以逃逸,这没什么可争议的。但编译器有时会将 不需要 使用堆空间的变量,也逃逸掉。这里是容易出现性能问题的大坑。网上有很多相关文章,列举了一些导致逃逸情况,其实总结起来就一句话:
多级间接赋值容易导致逃逸 。
这里的多级间接指的是,对某个引用类对象中的引用类成员进行赋值。Go 语言中的引用类数据类型有 func , interface , slice , map , chan , *Type(指针) 。
记住公式 Data.Field = Value ,如果 Data , Field 都是引用类的数据类型,则会导致 Value 逃逸。这里的等号 = 不单单只赋值,也表示参数传递。
根据公式,我们假设一个变量 data 是以下几种类型,相应的可以得出结论:
下面给出一些实际的例子:
如果变量值是一个函数,函数的参数又是引用类型,则传递给它的参数都会逃逸。
上例中 te 的类型是 func(*int) ,属于引用类型,参数 *int 也是引用类型,则调用 te(j) 形成了为 te 的参数(成员) *int 赋值的现象,即 te.i = j 会导致逃逸。代码中其他几种调用都没有形成 多级间接赋值 情况。
同理,如果函数的参数类型是 slice , map 或 interface{} 都会导致参数逃逸。
匿名函数的调用也是一样的,它本质上也是一个函数变量。有兴趣的可以自己测试一下。
只要使用了 Interface 类型(不是 interafce{} ),那么赋值给它的变量一定会逃逸。因为 interfaceVariable.Method() 先是间接的定位到它的实际值,再调用实际值的同名方法,执行时实际值作为参数传递给方法。相当于 interfaceVariable.Method.this = realValue
向 channel 中发送数据,本质上就是为 channel 内部的成员赋值,就像给一个 slice 中的某一项赋值一样。所以 chan *Type , chan map[Type]Type , chan []Type , chan interface{} 类型都会导致发送到 channel 中的数据逃逸。
这本来也是情理之中的,发送给 channel 的数据是要与其他函数分享的,为了保证发送过去的指针依然可用,只能使用堆分配。
可变参数如 func(arg ...string) 实际与 func(arg []string) 是一样的,会增加一层访问路径。这也是 fmt.Sprintf 总是会使参数逃逸的原因。
例子非常多,这里不能一一列举,我们只需要记住分析方法就好,即,2 级或更多级的访问赋值会 容易 导致数据逃逸。这里加上 容易 二字是因为随着语言的发展,相信这些问题会被慢慢解决,但现阶段,这个可以作为我们分析逃逸现象的依据。
下面代码中包含 2 种很常规的写法,但他们却有着很大的性能差距,建议自己想下为什么。
Benchmark 和 pprof 给出的结果:
熟悉堆栈概念可以让我们更容易看透 Go 程序的性能问题,并进行优化。
多级间接赋值会导致 Go 编译器出现不必要的逃逸,在一些情况下可能我们只需要修改一下数据结构就会使性能有大幅提升。这也是很多人不推荐在 Go 中使用指针的原因,因为它会增加一级访问路径,而 map , slice , interface{} 等类型是不可避免要用到的,为了减少不必要的逃逸,只能拿指针开刀了。
大多数情况下,性能优化都会为程序带来一定的复杂度。建议实际项目中还是怎么方便怎么写,功能完成后通过性能分析找到瓶颈所在,再对局部进行优化。
云和安全管理服务专家新钛云服 张春翻译
这种方法有几个缺点。首先,它可以对程序员隐藏错误处理路径,特别是在捕获异常不是强制性的情况下,例如在 Python 中。即使在具有必须处理的 Java 风格的检查异常的语言中,如果在与原始调用不同的级别上处理错误,也并不总是很明显错误是从哪里引发的。
我们都见过长长的代码块包装在一个 try-catch 块中。在这种情况下,catch 块实际上充当 goto 语句,这通常被认为是有害的(奇怪的是,C 中的关键字被认为可以接受的少数用例之一是错误后清理,因为该语言没有 Golang- 样式延迟语句)。
如果你确实从源头捕获异常,你会得到一个不太优雅的 Go 错误模式版本。这可能会解决混淆代码的问题,但会遇到另一个问题:性能。在诸如 Java 之类的语言中,抛出异常可能比函数的常规返回慢数百倍。
Java 中最大的性能成本是由打印异常的堆栈跟踪造成的,这是昂贵的,因为运行的程序必须检查编译它的源代码 。仅仅进入一个 try 块也不是空闲的,因为需要保存 CPU 内存寄存器的先前状态,因为它们可能需要在抛出异常的情况下恢复。
如果您将异常视为通常不会发生的异常情况,那么异常的缺点并不重要。这可能是传统的单体应用程序的情况,其中大部分代码库不必进行网络调用——一个操作格式良好的数据的函数不太可能遇到错误(除了错误的情况)。一旦您在代码中添加 I/O,无错误代码的梦想就会破灭:您可以忽略错误,但不能假装它们不存在!
try {
doSometing()
} catch (IOException e) {
// ignore it
}
与大多数其他编程语言不同,Golang 接受错误是不可避免的。 如果在单体架构时代还不是这样,那么在今天的模块化后端服务中,服务通常和外部 API 调用、数据库读取和写入以及与其他服务通信 。
以上所有方法都可能失败,解析或验证从它们接收到的数据(通常在无模式 JSON 中)也可能失败。Golang 使可以从这些调用返回的错误显式化,与普通返回值的等级相同。从函数调用返回多个值的能力支持这一点,这在大多数语言中通常是不可能的。Golang 的错误处理系统不仅仅是一种语言怪癖,它是一种将错误视为替代返回值的完全不同的方式!
重复 if err != nil
对 Go 错误处理的一个常见批评是被迫重复以下代码块:
res, err := doSomething()
if err != nil {
// Handle error
}
对于新用户来说,这可能会觉得没用而且浪费行数:在其他语言中需要 3 行的函数很可能会增长到 12 行 :
这么多行代码!这么低效!如果您认为上述内容不优雅或浪费代码,您可能忽略了我们检查代码中的错误的全部原因:我们需要能够以不同的方式处理它们!对 API 或数据库的调用可能会被重试。
有时事件的顺序很重要:调用外部 API 之前发生的错误可能不是什么大问题(因为数据从未通过发送),而 API 调用和写入本地数据库之间的错误可能需要立即注意,因为 这可能意味着系统最终处于不一致的状态。即使我们只想将错误传播给调用者,我们也可能希望用失败的解释来包装它们,或者为每个错误返回一个自定义错误类型。
并非所有错误都是相同的,并且向调用者返回适当的错误是 API 设计的重要部分,无论是对于内部包还是 REST API 。
不必担心在你的代码中重复 if err != nil ——这就是 Go 中的代码应该看起来的样子。
自定义错误类型和错误包装
从导出的方法返回错误时,请考虑指定自定义错误类型,而不是单独使用错误字符串。字符串在意外代码中是可以的,但在导出的函数中,它们成为函数公共 API 的一部分。更改错误字符串将是一项重大更改——如果没有明确的错误类型,需要检查返回错误类型的单元测试将不得不依赖原始字符串值!事实上,基于字符串的错误也使得在私有方法中测试不同的错误案例变得困难,因此您也应该考虑在包中使用它们。回到错误与异常的争论,返回错误也使代码比抛出异常更容易测试,因为错误只是要检查的返回值。不需要测试框架或在测试中捕获异常 。
可以在 database/sql 包中找到简单自定义错误类型的一个很好的示例。它定义了一个导出常量列表,表示包可以返回的错误类型,最著名的是 sql.ErrNoRows。虽然从 API 设计的角度来看,这种特定的错误类型有点问题(您可能会争辩说 API 应该返回一个空结构而不是错误),但任何需要检查空行的应用程序都可以导入该常量并在代码中使用它不必担心错误消息本身会改变和破坏代码。
对于更复杂的错误处理,您可以通过实现返回错误字符串的 Error() 方法来定义自定义错误类型。自定义错误可以包括元数据,例如错误代码或原始请求参数。如果您想表示错误类别,它们很有用。DigitalOcean 的本教程展示了如何使用自定义错误类型来表示可以重试的一类临时错误。
通常,错误会通过将低级错误与更高级别的解释包装起来,从而在程序的调用堆栈中传播。例如,数据库错误可能会以下列格式记录在 API 调用处理程序中:调用 CreateUser 端点时出错:查询数据库时出错:pq:检测到死锁。这很有用,因为它可以帮助我们跟踪错误在系统中传播的过程,向我们展示根本原因(数据库事务引擎中的死锁)以及它对更广泛系统的影响(调用者无法创建新用户)。
自 Go 1.13 以来,此模式具有特殊的语言支持,并带有错误包装。通过在创建字符串错误时使用 %w 动词,可以使用 Unwrap() 方法访问底层错误。除了比较错误相等性的函数 errors.Is() 和 errors.As() 外,程序还可以获取包装错误的原始类型或标识。这在某些情况下可能很有用,尽管我认为在确定如何处理所述错误时最好使用顶级错误的类型。
Panics
不要 panic()!长时间运行的应用程序应该优雅地处理错误而不是panic。即使在无法恢复的情况下(例如在启动时验证配置),最好记录一个错误并优雅地退出。panic比错误消息更难诊断,并且可能会跳过被推迟的重要关闭代码。
Logging
我还想简要介绍一下日志记录,因为它是处理错误的关键部分。通常你能做的最好的事情就是记录收到的错误并继续下一个请求。
除非您正在构建简单的命令行工具或个人项目,否则您的应用程序应该使用结构化的日志库,该库可以为日志添加时间戳,并提供对日志级别的控制。最后一部分特别重要,因为它将允许您突出显示应用程序记录的所有错误和警告。通过帮助将它们与信息级日志分开,这将为您节省无数时间。
微服务架构还应该在日志行中包含服务的名称以及机器实例的名称。默认情况下记录这些时,程序代码不必担心包含它们。您也可以在日志的结构化部分中记录其他字段,例如收到的错误(如果您不想将其嵌入日志消息本身)或有问题的请求或响应。只需确保您的日志没有泄露任何敏感数据,例如密码、API 密钥或用户的个人数据!
对于日志库,我过去使用过 logrus 和 zerolog,但您也可以选择其他结构化日志库。如果您想了解更多信息,互联网上有许多关于如何使用这些的指南。如果您将应用程序部署到云中,您可能需要日志库上的适配器来根据您的云平台的日志 API 格式化日志 - 没有它,云平台可能无法检测到日志级别等某些功能。
如果您在应用程序中使用调试级别日志(默认情况下通常不记录),请确保您的应用程序可以轻松更改日志级别,而无需更改代码。更改日志级别还可以暂时使信息级别甚至警告级别的日志静音,以防它们突然变得过于嘈杂并开始淹没错误。您可以使用在启动时检查以设置日志级别的环境变量来实现这一点。
原文:
Golang 的创建是为了实现最大的用户效率和编码效率。已经熟悉 Java 或 PHP 的程序员可以在几周内接受 Go 的培训(许多人最终会更喜欢它)。在本文中,Dewet Diener 探讨了 Golang 的优缺点,以及它的测试驱动开发 (TDD) 如何完美契合。
Golang 由 Google 开发和设计,于 2009 年作为一种综合性编程语言首次出现,旨在最大限度地提高编码效率。创建该语言的目的是修正其他已建立语言的缺陷。尽管 Golang(或简称为“Go”)是一门年轻的语言,但已经积累了大量的开发人员,因此我们想分享为什么在 Curve 我们喜欢 Golang,以及我们如何采用它来实现我们移动银行业务的目标到云端。
Go 是一种精致的编程语言:它支持“所见即所得”的原则,这意味着清晰易读的代码和更少的复杂抽象。该语言本身易于使用且易于训练。尽管如此,作为一个相对较新的生态系统,要找到对 Go 具有广泛预先知识的工程师可能会很棘手。
然而,与其他编程语言不同,Go 的创建是为了最大限度地提高用户效率。因此,具有 Java 或 PHP 背景的开发人员和工程师可以在几周内获得使用 Go 的技能和培训——根据我们的经验,他们中的许多人最终更喜欢它。
在 Curve,我们大力提倡测试驱动开发 (TDD),Go 的框架与这种方法保持一致。通过简单地命名一个文件 foo_test.go 并在该文件中添加结构化测试函数,Go 将快速有效地运行您的单元测试。这一创新功能提高了生产力,因为它可以更加专注于测试驱动的开发和改进的同行评审机会。
Golang 具有出色的生产优化品质,例如内存占用小,这支持其在大型项目中作为构建块的能力,以及开箱即用的与其他架构的轻松交叉编译。由于 Go 代码被编译为单个静态二进制文件,因此它可以轻松进行容器化,并且通过扩展,将 Go 部署到任何高可用性环境(例如 Kubernetes)中几乎是微不足道的。
它提供了一种机制来保护工作负载,通过拥有非常纤薄的生产容器而没有任何无关的依赖项。这使得构建、部署和维护基于 Go 的资产更加直接和安全,并为希望建立或发展其微服务战略的公司提供了可靠的选择。
Go 是专门为满足我们快速发展的技术生态系统的需求而创建的。例如,Go 可以满足您构建 API 所需的一切,并将其作为其标准库的一部分。它使用简单,高性能的 http 服务器消除了团队设计新项目时经常发生的一些常见的 探索 和设计瘫痪问题——这对于一些其他流行语言(如 Java 和 Node.js)来说太常见了。
Golang 还通过其内置于语言本身的自动格式化程序巧妙地解决了代码格式化分歧。这完全消除了格式争议,进而提高了团队的生产力和注意力。
尽管我是 Go 的拥护者,但它显然也不是没有缺陷。一个争论不休的特性是 Go 没有显式接口,这是许多开发人员习惯的概念。虽然不是有害的,但它可以使选择最适合您的结构的接口成为一项任务。这是因为您不会像在其他流行的编程语言中那样编写 X 实现 Y,但您很快就会接受。
依赖管理也是另一个不属于 Google Golang 开发团队原始设计的功能。开源社区介入并创建了 Glide 和 Dep,最初的努力并没有完全解决问题。从 Go 1.11 开始,添加了对模块的支持,这似乎已成为官方的依赖管理工具。这些挑战并没有削弱 Go 作为一种高效编程语言的独创性,并且它继续为我们提供优于其他编程语言的显着优势。
Golang 吸引了全球敏锐的开发人员的注意,并且围绕它的兴奋继续增长。开源社区因有趣的项目而蓬勃发展;最著名的是 Docker 和 Kubernetes。
正是这种新鲜、有创意但又简单的包装吸引了我们去Go:它是一种令人兴奋的编码语言,可以帮助我们在 Curve 中快速开发以构建更好的产品。
切换到新语言始终是一大步,尤其是当您的团队成员只有一个时有该语言的先前经验。现在,Stream 的主要编程语言从 Python 切换到了 Go。这篇文章将解释stream决定放弃 Python 并转向 Go 的一些原因。
Go 非常快。性能类似于 Java 或 C++。对于用例,Go 通常比 Python 快 40 倍。
对于许多应用程序来说,编程语言只是应用程序和数据库之间的粘合剂。语言本身的性能通常并不重要。然而,Stream 是一个API 提供商,为 700 家公司和超过 5 亿最终用户提供提要和聊天平台。多年来,我们一直在优化 Cassandra、PostgreSQL、Redis 等,但最终,您会达到所使用语言的极限。Python 是一门很棒的语言,但对于序列化/反序列化、排名和聚合等用例,它的性能相当缓慢。我们经常遇到性能问题,Cassandra 需要 1 毫秒来检索数据,而 Python 会花费接下来的 10 毫秒将其转换为对象。
看看我如何开始 Go 教程中的一小段 Go 代码。(这是一个很棒的教程,也是学习 Go 的一个很好的起点。)
如果您是 Go 新手,那么在阅读那个小代码片段时不会有太多让您感到惊讶的事情。它展示了多个赋值、数据结构、指针、格式和一个内置的 HTTP 库。当我第一次开始编程时,我一直喜欢使用 Python 更高级的功能。Python 允许您在编写代码时获得相当的创意。例如,您可以:
这些功能玩起来很有趣,但是,正如大多数程序员会同意的那样,在阅读别人的作品时,它们通常会使代码更难理解。Go 迫使你坚持基础。这使得阅读任何人的代码并立即了解发生了什么变得非常容易。 注意:当然,它实际上有多“容易”取决于您的用例。如果你想创建一个基本的 CRUD API,我仍然推荐 Django + DRF或 Rails。
作为一门语言,Go 试图让事情变得简单。它没有引入许多新概念。重点是创建一种非常快速且易于使用的简单语言。它唯一具有创新性的领域是 goroutine 和通道。(100% 正确CSP的概念始于 1977 年,所以这项创新更多是对旧思想的一种新方法。)Goroutines 是 Go 的轻量级线程方法,通道是 goroutines 之间通信的首选方式。Goroutines 的创建非常便宜,并且只需要几 KB 的额外内存。因为 Goroutine 非常轻量,所以有可能同时运行数百甚至数千个。您可以使用通道在 goroutine 之间进行通信。Go 运行时处理所有复杂性。goroutines 和基于通道的并发方法使得使用所有可用的 CPU 内核和处理并发 IO 变得非常容易——所有这些都不会使开发复杂化。与 Python/Java 相比,在 goroutine 上运行函数需要最少的样板代码。您只需在函数调用前加上关键字“go”:
Go 的并发方法很容易使用。与 Node 相比,这是一种有趣的方法,开发人员必须密切关注异步代码的处理方式。Go 中并发的另一个重要方面是竞争检测器。这样可以很容易地确定异步代码中是否存在任何竞争条件。
我们目前用 Go 编写的最大的微服务编译需要 4 秒。与以编译速度慢而闻名的 Java 和 C++ 等语言相比,Go 的快速编译时间是一项重大的生产力胜利。我喜欢在程序编译的时候摸鱼,但在我还记得代码应该做什么的同时完成事情会更好。
首先,让我们从显而易见的开始:与 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 开发人员团队更容易。如果您在博尔德和阿姆斯特丹等竞争激烈的生态系统中招聘人员,这是一项重要的优势。
对于我们这样规模的团队(约 20 人)来说,生态系统很重要。如果您必须重新发明每一个小功能,您根本无法为您的客户创造价值。Go 对我们使用的工具有很好的支持。实体库已经可用于 Redis、RabbitMQ、PostgreSQL、模板解析、任务调度、表达式解析和 RocksDB。与 Rust 或 Elixir 等其他较新的语言相比,Go 的生态系统是一个重大胜利。它当然不如 Java、Python 或 Node 之类的语言好,但它很可靠,而且对于许多基本需求,你会发现已经有高质量的包可用。
Gofmt 是一个很棒的命令行实用程序,内置在 Go 编译器中,用于格式化代码。就功能而言,它与 Python 的 autopep8 非常相似。我们大多数人并不真正喜欢争论制表符与空格。格式的一致性很重要,但实际的格式标准并不那么重要。Gofmt 通过使用一种正式的方式来格式化您的代码来避免所有这些讨论。
Go 对协议缓冲区和 gRPC 具有一流的支持。这两个工具非常适合构建需要通过 RPC 通信的微服务。您只需要编写一个清单,在其中定义可以进行的 RPC 调用以及它们采用的参数。然后从这个清单中自动生成服务器和客户端代码。生成的代码既快速又具有非常小的网络占用空间并且易于使用。从同一个清单中,您甚至可以为许多不同的语言生成客户端代码,例如 C++、Java、Python 和 Ruby。因此,内部流量不再有模棱两可的 REST 端点,您每次都必须编写几乎相同的客户端和服务器代码。.
Go 没有像 Rails 用于 Ruby、Django 用于 Python 或 Laravel 用于 PHP 那样的单一主导框架。这是 Go 社区内激烈争论的话题,因为许多人主张你不应该一开始就使用框架。我完全同意这对于某些用例是正确的。但是,如果有人想构建一个简单的 CRUD API,他们将更容易使用 Django/DJRF、Rails Laravel 或Phoenix。对于 Stream 的用例,我们更喜欢不使用框架。然而,对于许多希望提供简单 CRUD API 的新项目来说,缺乏主导框架将是一个严重的劣势。
Go 通过简单地从函数返回错误并期望调用代码来处理错误(或将其返回到调用堆栈)来处理错误。虽然这种方法有效,但很容易失去问题的范围,以确保您可以向用户提供有意义的错误。错误包通过允许您向错误添加上下文和堆栈跟踪来解决此问题。另一个问题是很容易忘记处理错误。像 errcheck 和 megacheck 这样的静态分析工具可以方便地避免犯这些错误。虽然这些变通办法效果很好,但感觉不太对劲。您希望该语言支持正确的错误处理。
Go 的包管理绝不是完美的。默认情况下,它无法指定特定版本的依赖项,也无法创建可重现的构建。Python、Node 和 Ruby 都有更好的包管理系统。但是,使用正确的工具,Go 的包管理工作得很好。您可以使用Dep来管理您的依赖项,以允许指定和固定版本。除此之外,我们还贡献了一个名为的开源工具VirtualGo,它可以更轻松地处理用 Go 编写的多个项目。
我们进行的一个有趣的实验是在 Python 中使用我们的排名提要功能并在 Go 中重写它。看看这个排名方法的例子:
Python 和 Go 代码都需要执行以下操作来支持这种排名方法:
开发 Python 版本的排名代码大约花了 3 天时间。这包括编写代码、单元测试和文档。接下来,我们花了大约 2 周的时间优化代码。其中一项优化是将分数表达式 (simple_gauss(time)*popularity) 转换为抽象语法树. 我们还实现了缓存逻辑,可以在未来的特定时间预先计算分数。相比之下,开发此代码的 Go 版本大约需要 4 天时间。性能不需要任何进一步的优化。因此,虽然 Python 的最初开发速度更快,但基于 Go 的版本最终需要我们团队的工作量大大减少。另外一个好处是,Go 代码的执行速度比我们高度优化的 Python 代码快大约 40 倍。现在,这只是我们通过切换到 Go 体验到的性能提升的一个示例。
与 Python 相比,我们系统的其他一些组件在 Go 中构建所需的时间要多得多。作为一个总体趋势,我们看到 开发 Go 代码需要更多的努力。但是,我们花更少的时间 优化 代码以提高性能。
我们评估的另一种语言是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 编写。
新建编译系统gcc
把大括号里的东西换成这个
"cmd": ["gcc","${file}", "-o", "${file_path}/${file_base_name}"],
"file_regex":"^(..[^:]*):([0-9]+):?([0-9]+)?:? (.*)$",
"working_dir":"${file_path}",
"selector": "source.c",
"variants":
[
{
"name": "Run",
"cmd": ["cmd","/c", "gcc", "${file}", "-o", "${file_path}/${file_base_name}","", "cmd", "/c","${file_path}/${file_base_name}"]
}
,保存为gcc.sublime-build
然后把编译系统换成你刚建立的gcc,然后就可以了,记得编译之前要先保存文件才可以