Overview
本文讨论了服务器领域常见的并发等问题,也涉及到了工程化相关的问题,也整理了C背景程序员对于Go的GC以及性能的疑问,探讨了Go的错误处理和类型系统最佳实践,以及依赖管理的难处,接口设计的正交性,当然也包含我们在服务器开发中对于Go实践的总结,有时候也对一些有趣的问题做深度的挖掘,也列出了Go重要的事件和资料集合,还有Go2的进展和思考。我更想从问题本身出发,不局限于Go语言,探讨服务器中常常遇到的问题,最后回到Go如何解决这些问题,提供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:
The name of our language is Go
Ruby is called Ruby, not Rubylang.
Python is called Python, not Pythonlang.
C is called C, not Clang. No. Wait. That was a bad example.
Go is called Go, not Golang.
Yes, yes, I know all about the searching and meta tags. Sure, whatever,
but that doesn't change the fact that the name of the language is Go.
Thank you for your consideration.
这里举了各种例子说明为何不加lang的后缀,当然有个典型的语言是加的,就是Erlang
。于是就有回复说“Erlang Erlang, Let's just call it Er.”
那么为何很多时候Go和Golang都很常用呢?在Why is the Go programming language usually called Golang中说的比较清楚:
It’s because “go domain” has been registered by Walt Disney and so Go creators couldn’t use it.
So, they have decided to use golang for the domain name. Then the rest came.
Also, it’s harder to search things on search engines just using the word Go. Although, Rob Pike is
against this idea but I disagree. Most of the time, for the correct results you need to search for
golang.
It’s just Go, not golang but it sticked to it.
讲个笑话先,用百度搜下为何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做到了哪些,Go的重要事件:
- 2019.09, Go1.13发布。增强了modules,新增了环境变量GOPRIVATE和GOSUMDB,GOPROXY支持多个。支持了ErrorWraping。
- 2019.02, Go1.12发布,支持了TLS1.3,改进了modules,优化运行时和标准库。
- 2018.08, Go1.11发布,实验性支持modules,实验性支持WebAssembly
- 2018.02, Go1.10发布,go tool缓存编译,编译加速,很多细微的改进。
- 2018.01, Hello, 中国!, 中国站镜像上线,大陆可以访问官网资源。
- 2017.08, Go1.9发布,支持Type Alias,支持sync.Map,使用场景参考slides,time保持单增避免时间测量问题。
- 2017.02, Go1.8发布,显著的性能提升,GC延迟降低到了10us到100us,支持HTTP/2 Push,HTTP Server支持Shutdown,
sort.Slice
使排序使用更简单。 - 2016.08, Go1.7发布,支持了Context,Context在K8S和Docker中都有应用,新的编译算法减少20-30%的二进制尺寸。
- 2016.02, Go1.6发布,支持HTTP/2,HTTPS时会默认开启HTTP/2,正式支持vendor。
- 2015.08, 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.06, Go1.3发布,支持了FreeBSD,Plan9,Solaris等系统。
- 2013.12, Go1.2发布,新增收集覆盖率工具coverage,限制了最高线程数ThreadLimit。
- 2013.05, Go1.1发布,主要是包含性能优化,新增
Data Race Detector
等。 - 2012.03, 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等商业友好的协议。
Links
由于限制了文章字数,只好分成不同章节:
- Overview 为何Go有时候也叫Golang?为何要选择Go作为服务器开发的语言?是冲动?还是骚动?Go的重要里程碑和事件,当年吹的那些牛逼,都实现了哪些?
- Could Not Recover 君可知,有什么panic是无法recover的?包括超过系统线程限制,以及map的竞争写。当然一般都能recover,比如Slice越界、nil指针、除零、写关闭的chan等。
- Errors 为什么Go2的草稿3个有2个是关于错误处理的?好的错误处理应该怎么做?错误和异常机制的差别是什么?错误处理和日志如何配合?
- Logger 为什么标准库的Logger是完全不够用的?怎么做日志切割和轮转?怎么在混成一坨的服务器日志中找到某个连接的日志?甚至连接中的流的日志?怎么做到简洁又够用?
- Interfaces 什么是面向对象的SOLID原则?为何Go更符合SOLID?为何接口组合比继承多态更具有正交性?Go类型系统如何做到looser, organic, decoupled, independent, and therefore scalable?一般软件中如果出现数学,要么真的牛逼要么装逼。正交性这个数学概念在Go中频繁出现,是神仙还是妖怪?为何接口设计要考虑正交性?
- Modules 如何避免依赖地狱(Dependency Hell)?小小的版本号为何会带来大灾难?Go为什么推出了GOPATH、Vendor还要搞module和vgo?新建了16个仓库做测试,碰到了9个坑,搞清楚了gopath和vendor如何迁移,以及vgo with vendor如何使用(毕竟生产环境不能每次都去外网下载)。
- Concurrency & Control 服务器中的并发处理难在哪里?为什么说Go并发处理优势占领了云计算开发语言市场?什么是C10K、C10M问题?如何管理goroutine的取消、超时和关联取消?为何Go1.7专门将context放到了标准库?context如何使用,以及问题在哪里?
- Engineering Go在工程化上的优势是什么?为什么说Go是一门面向工程的语言?覆盖率要到多少比较合适?什么叫代码可测性?为什么良好的库必须先写Example?
- Go2 Transition Go2会像Python3不兼容Python2那样作吗?C和C++的语言演进可以有什么不同的收获?Go2怎么思考语言升级的问题?
- SRS & Others Go在流媒体服务器中的使用。Go的GC靠谱吗?Twitter说相当的靠谱,有图有真相。为何Go的声明语法是那样?C的又是怎样?是拍的大腿,还是拍的脑袋?