Go 开发关键技术指南 | 为什么你要选择 GO?(内含超全知识大图)
导读:从问题本身出发,不局限于 Go 语言,探讨服务器中常常遇到的问题,最后回到 Go 如何解决这些问题,为大家提供 Go 开发的关键技术指南。我们将以系列文章的形式推出《Go 开发的关键技术指南》,共有 4 篇文章,本文为第 1 篇。
Go 开发指南大图
Overview
该指南主要讨论了服务器领域常见的并发问题,也涉及到了工程化相关的问题,还整理了 C 背景程序员对于 Go 的 GC 以及性能的疑问,探讨了 Go 的错误处理和类型系统最佳实践,以及依赖管理的难处、接口设计的正交性,当然也包含我们在服务器开发中对于 Go 实践的总结,有时候也会对一些有趣的问题做深度的挖掘,列出了 Go 重要的事件和资料集合,以及 Go2 的进展和思考。
以下是各个章节以及简介:
- About the Name:为何 Go 有时候也叫 Golang?
- Why Go:为何要选择 Go 作为服务器开发的语言?是冲动?还是骚动?
- Milestones:Go 的重要里程碑和事件,当年吹的那些牛逼,都实现了哪些?
- GC:Go 的 GC 靠谱吗?Twitter 说相当的靠谱,有图有真相。
- Could Not Recover:君可知,有什么 panic 是无法 recover 的?包括超过系统线程限制,以及 map 的竞争写。当然一般都能 recover,比如 Slice 越界、nil 指针、除零、写关闭的 chan 等。
- Declaration Syntax:为何 Go 语言的声明语法是那样的?C 语言的声明语法又是怎样的?是拍的大腿,还是拍的脑袋?
- Errors:为什么 Go2 的草稿 3 个中有 2 个是关于错误处理的?好的错误处理应该怎么做?错误和异常机制的差别是什么?错误处理和日志如何配合?
- Logger:为什么标准库的 Logger 是完全不够用的?怎么做日志切割和轮转?怎么在混成一坨的服务器日志中找到某个连接的日志?甚至连接中的流的日志?怎么做到简洁又够用?
- Type System:什么是面向对象的 SOLID 原则?为何 Go 更符合 SOLID?为何接口组合比继承多态更具有正交性?Go 类型系统如何做到 looser、organic、decoupled、independent and therefore scalable?
- Orthogonal:一般软件中如果出现数学,要么真的牛逼,要么就是装逼。正交性这个数学概念在 Go 中频繁出现,是神仙还是妖怪?为何接口设计要考虑正交性?
- Modules:如何避免依赖地狱(Dependency Hell)?小小的版本号为何会带来大灾难?Go 为什么推出了 GOPATH、Vendor 还要搞 module 和 vgo?新建了 16 个仓库做测试,碰到了 9 个坑,搞清楚了 gopath 和 vendor 如何迁移?以及 vgo with vendor 如何使用(毕竟生产环境不能每次都去外网下载)?
- Concurrency:服务器中的并发处理难在哪里?为什么说 Go 并发处理优势占领了云计算开发语言市场?什么是 C10K、C10M 问题?
- Context:如何管理 goroutine 的取消、超时和关联取消?为何 Go1.7 专门将 context 放到了标准库?context 如何使用以及问题在哪里?
- Engineering:Go 在工程化上的优势是什么?为什么说 Go 是一门面向工程的语言?覆盖率要到多少比较合适?什么叫代码可测性?为什么良好的库必须先写 Example?
- Go2 Transition:Go2 会像 Python3 不兼容 Python2 那样作吗?C 和 C++ 的语言演进可以有什么不同的收获?Go2 怎么思考语言升级的问题?
- Documents:Go 官网的重要文档分类,本屌丝读了四遍了,推荐阅读。
- SRS:Go 在流媒体服务器中的使用。
About the Name
The Go Programming Language 到底是该叫 GO 还是 GOLANG?Google 搜 Why Go is called Golang
能搜到几篇经典帖子。
Rob Pike 在 Twitter 上特意说明是 Go,可以看这个 The language is called Go:
Neither. The language is called Go, not Golang. http://golang.org is just the the web site address, not the name of the language.
在另外一个地方也说明了是 Go,可以看这个 The name of our language is go:
这里举了各种例子说明为何不加 lang 的后缀,当然有个典型的语言是加的,就是 Erlang
。于是就有回复说“Erlang Erlang, Let's just call it Er.”
那么为什么大多时候 Go 和 Golang 都很常用呢?在 Why is the Go programming language usually called Golang 中说的比较清楚:
讲个笑话先,用百度搜下为何 Go 叫做 Golang,一大片都是类似本文的鸡汤煲,告诉你为何 Go 才是天地间最合适你的语言,当然本文要成为鸡汤煲中的战斗煲,告诉你全家都应该选择 Go 语言。
为何 Go 语言名字是 Go,但是经常说成是 Golang 呢?有以下理由:
- go.org 被注册了,正在卖,也不贵才 1698 万。所以 Go 只能用 golang.org;
- 想要搜点啥信息时,如果搜 go 太宽泛了,特别是 go 还没有这么多用户时,搜 golang 能更精确的找到答案。
为什么在名字上要这么纠结呢?嗯嗯,不纠结,让我们开始干鸡汤吧。
Why Go?
考虑一个商用的快速发展的业务后端服务器,最重要的是什么?当然是稳定性了,如果崩溃可能会造成用户服务中断,崩溃的问题在 C/C++ 服务器中几乎是必然的:
- 稳定是一种假象;
想象一个 C 服务器,一般不会重头码所有的代码,会从一个开源版本开始,或者从一些网络和线程库开始,然后不断改进和完善。由于业务前期并不复杂,上线也没有发现问题,这时候可以说 C 服务器是稳定的吗?当然不是,只是 Bug 没有触发而已,所有崩溃的 Bug 几乎都不是本次发布导致的。野指针和越界是 C 服务器中最难搞定的狼人,这些狼人还喜欢玩潜伏。
- 稳定是短暂的,不稳定是必然和长期的;
一般业务会突飞猛进,特别是越偏上层的业务,需要后端处理的逻辑就越多,至于 UTest 和测试一般只存在于传说中,随着业务的发展,潜伏的狼人越来越多,甚至开源的库和服务器中的狼人也开始出来作妖。夜路走多了,总会碰到鬼,碰到鬼了怎么办?当然是遇鬼杀鬼了,还能被它吓尿不成,所以就反思解决 Bug,费了老劲、又白了几根头发,终于迎来短暂安宁,然后继续写 Bug。
- 最普遍的问题还是内存问题导致崩溃,一般就是野指针和越界;
空指针问题相对很容易查,除零之类的典型错误也容易处理。最完善的解决办法,就是实现 GC,让指针总是有效,无效后再释放,越界时能检测到,这样容易解决问题;其实 Go 早期的版本就和这个很类似了,要实现带 GC 的 C 的同学,可以参考下 Go 的实现。
- 线上的 CPU 和内存的问题,一般不方便使用工具查看,而线上的问题有时候很难在本地重现。
如何能直接获取线上的 Profile 数据,需要程序本身支持。比如提供 HTTP API 能获取到 Profile 数据,关键是如何采集这些数据。
Go 的使命愿景和价值观:
Go is an open source programming language that makes it easy to build simple, reliable, and efficient software.
Go is a concurrent open source programming language developed at Google. Combines native compilation and static types with a lightweight dynamic feel. Fast, fun, and productive.>
Go is an attempt to make programmers more productive. The first goal is to make a better language to meet the challenges of scalable concurrency. The larger goal is to make a better environment to meet the challenges of scalable software development, software worked on and used by many people, with limited coordination between them, and maintained for years.
Go 语言的关键字:
- 运行性能高: Statically typed. Native code generation (compiled). Efficiency. Fast development cycle.
- 码农不苦逼: Memory safe. Garbage collected. Safety.
- 云计算专享: Native concurrency support. Concurrency. Scalability.
- 工程师思维: Composition via interfaces. Excellent standard library. Great tools.
参考 The Path to Go1: What is Go? 和 Another Go at Language Design。
参考 > Go: a simple programming environment。
Go 是面向软件工程的语言,Go 在工程上的思考可以读 Go at Google: Language Design in the Service of Software Engineering 和 Less is exponentially more。Go 最初是解决 Google 遇到的大规模系统和计算的问题,这些问题如今被称为云计算,参考 Go, Open Source, Community。
GITHUT上显示 Go 的项目和 PR 一直在上升,如下图所示。
2014 云计算行业中使用 Go 的有:Docker, Kubernetes, Packer, Serf, InfluxDB, Cloud Foundry’s gorouter and CLI, CoreOS’s etcd and fleet, Vitess | YouTube’s tooling for MySQL scaling, Canonical’s Juju (rewritten in Go), Mozilla’s Heka, A Go interface to OpenStack Swift, Heroku’s Force.com and hk CLIs, Apcera’s NATS and gnatsd。
2018 年全球使用 Go 的公司数目有:US(329), Japan(79), Brazil(52), India(49), Indonesia(45), China(32), UK(32), Germany(28), Israel(24), France(17), Netherlands(16), Canada (15), Thailand(14), Turkey(14), Spain(12), Poland(11), Australia(9), Russia(9), Iran(8), Sweden(7), Korea(South)(6), Switzerland(6), Ukraine(5)。
参考 Go as the emerging language of cloud infrastructure、The RedMonk Programming Language Rankings: June 2018,还有 GoUsers 以及 Success Stories。
参考 > "Go: 90% Perfect, 100% of the time" -bradfitz, 2014。参考 > Nine years of Go: Go Contributors,社区贡献的代码比例。
我们一起看看这些 Go 牛逼的特性,详细分析每个点,虽然不能涵盖所有的点,对于常用的 Go 的特性我们做一次探讨和分析。
Milestones
接下来看一下有关 Go 的重要事件:
- 2019 年 9 月,Go1.13 发布。增强了 modules,新增了环境变量 GOPRIVATE 和 GOSUMDB,GOPROXY 支持多个,支持了 ErrorWraping;
- 2019 年 2 月,Go1.12 发布,支持了 TLS1.3,改进了 modules,优化运行时和标准库;
- 2018 年 8 月,Go1.11 发布,实验性支持 modules,实验性支持 WebAssembly;
- 2018 年 2 月,Go1.10 发布,go tool 缓存编译,编译加速,很多细微的改进;
- 2018 年 1 月,Hello, 中国! 及 中国站镜像上线,大陆可以访问官网资源;
- 2017 年 8 月,Go1.9 发布,支持 Type Alias、sync.Map,使用场景参考 slides,time 保持单增避免时间测量问题;
- 2017 年 2 月,Go1.8 发布,显著的性能提升,GC 延迟降低到了 10us 到 100us,支持 HTTP/2 Push,HTTP Server 支持 Shutdown,
sort.Slice
使排序使用更简单; - 2016 年 8 月,Go1.7 发布,支持了 Context,Context 在 K8s 和 Docker 中都有应用,新的编译算法减少 20%-30% 的二进制尺寸;
- 2016 年 2 月,Go1.6 发布,支持 HTTP/2,HTTPS 时会默认开启 HTTP/2,正式支持 vendor;
- 2015 年 8 月,Go1.5 发布,完全用 Go 代替了 C 代码,完全重新设计和重新实现 GC,支持 internal 的 package,实验性支持 vendor,GOMAXPROCS 默认为 CPU 个数;
- 2014 年 12 月,Go1.4 发布,支持 Android,从 Mecurial 迁移到了 Git,从 GoogleCode 迁移到了 Github: golang/go,大部分 runtime 的代码从 C 改成了 Go,
for
支持三种迭代写法; - 2014 年 6 月,Go1.3 发布,支持了 FreeBSD、Plan9、Solaris 等系统;
- 2013 年 12 月,Go1.2 发布,新增收集覆盖率工具 coverage,限制了最高线程数 ThreadLimit;
- 2013 年 5 月,Go1.1 发布,主要是包含性能优化,新增
Data Race Detector
等; - 2012 年 3 月,Go1.0 发布,包含了基本的语言元素比如 rune、error、map,标准库包括 bufio、crypto、flag、http、net、os、regexp、runtime、unsafe、url、encoding 等;
- 2009 年 11 月, Google 宣布要开发一门新语言,既要开源,又有 Python 的好处,还要有 C/C++ 的性能。GO 是 BSD 的 License,大部分 GO 的项目都是 BSD 或 MIT 或 Apache 等商业友好的协议。
本文作者:杨成立(忘篱)
本文为阿里云内容,未经允许不得转载。