You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

132 lines
13 KiB
Markdown

# Go
## 0. 目录
## 1. 背景
### 1.1 历史和现状
- Go 语言是怎样诞生的?
- Go 语言的创始人有三位分别是图灵奖获得者、C 语法联合发明人、Unix 之父**肯·汤普森 Ken Thompson**、Plan 9 操作系统领导者、UTF-8 编码的最初设计者**罗伯·派克Rob Pike**
,以及 Java 的 HotSpot 虚拟机和 Chrome 浏览器的 JavaScript V8 引擎的设计者之一**罗伯特·格瑞史莫Robert Griesemer**
- ![Go语言之父们](pic/Go语言之父们.png)
- Go语言第一版特性设计稿
- ![Go语言第一版特性设计稿](pic/Go语言第一版特性设计稿.png)
- 主要思路是,在 C 语言的基础上, **修正一些明显的缺陷,删除一些被诟病较多的特性,增加一些缺失的功能**
- 很多 Go 语言初学者经常称这门语言为 golang其实这是不对的“golang”仅应用于命名 Go 语言官方网站,而且当时没有用 go.com 纯粹是**这个域名被占用了**而已
- 在 Go 语言项目开源后Go 语言也迎来了自己的“吉祥物”,是一只由罗伯·派克夫人**芮妮·弗伦奇Renee French**设计的地鼠从此地鼠gopher也就成为了世界各地 Go
程序员的象征Go 程序员也被昵称为 **Gopher**
- ![Go语言的吉祥物](pic/Go语言的吉祥物.png)
- 2012 年 3 月 28 日Go 1.0 版本正式发布
- 只要符合 Go1 语言规范的源代码Go 编译器将保证向后兼容backwards compatible也就是说我们使用**新版编译器也可以正确编译用老版本语法编写的代码**。
- Go语言大事记
- ![Go语言大事记](pic/Go语言大事记.png)
- Go 是否值得我们学习?
- Go 语言已经逐渐成为了**云计算时代基础设施的编程语言**
- Docker、Kubernetes、Prometheus、Ethereum以太坊、Istio、CockroachDB、InfluxDB、Terraform、Etcd、Consul
- Gartner 的技术成熟度曲线The Hype Cycle
- Gartner 的技术成熟度曲线又叫技术循环曲线,是企业用来评估新科技是否要采用或采用时机的一种可视化方法,它利用时间轴与该技术在市面上的可见度(媒体曝光度)决定要
不要采用,以及什么时候采用这种新科技
- ![Gartner技术成熟度曲线](pic/Gartner技术成熟度曲线.png)
- ![Go语言的技术成熟度曲线](pic/Go语言的技术成熟度曲线.png)
- 预计在 2022 年初发布的支持 Go 泛型特性的 Go 1.18 版本,会是继 Go 1.5 版本之后又一“爆款”,很可能会快速推动 Go 迈入更高的发展阶段。
### 1.2 拒绝“Hello and Bye”Go语言的设计哲学是怎么一回事
- 根据“绝大多数主流编 程语言将在其 15 至 20 年间大步前进”这个依据我们给出了一个结论Go 语言即将进入自己的**黄金 5~10 年**。
- 所谓编程语言的设计哲学,就是指决定这门语言演化进程的高级原则和依据。
- Go 语言的设计哲学总结为五点:简单、显式、组合、并发和面向工程。
- **简单**
- Go 语言的设计者们在语言设计之初,就拒绝了走语言特性融合的道路,选择了“做减法”并致力于打造一门**简单的编程语言**
- 复杂性被 Go 语言的设计者们“隐藏”了,所以 Go 语法层面上呈现了这样的状态:
- 仅有 25 个关键字,主流编程语言最少;
- 内置垃圾收集,降低开发人员内存管理的心智负担;
- 首字母大小写决定可见性,无需通过额外关键字修饰;
- 变量初始为类型零值,避免以随机值作为初值的问题;
- 内置数组边界检查,极大减少越界访问带来的安全隐患;
- 内置并发支持,简化并发程序设计;
- 内置接口类型,为组合的设计哲学奠定基础;
- 原生提供完善的工具链,开箱即用;
- Go 设计者选择的“简单”,其实是站在巨人肩膀上,去除或优化了以往语言中,已经被开发者证明为体验不好或难以驾驭的语法元素和语言机制,并提出了自己的一些创新性的设
计。比如,首字母大小写决定可见性、变量初始为类型零值、内置以 go 关键字实现的并发支持等
- **显式**
- Go 不允许不同类型的整型变量进行混合计算,它同样也不会对其进行隐式的自动转换。
- 在 Go 语言中,不同类型变量是不能在一起进行混合计算的,这是因为 Go 希望**开发人员明确知道自己在做什么**,这与 C 语言的“信任程序员”原则完全不同,因此你需要以显式
的方式通过转型统一参与计算各个变量的类型。
- 除此之外Go 设计者所崇尚的显式哲学还直接决定了 Go 语言错误处理的形态Go 语言采用了**显式的基于值比较的错误处理方案**,函数 / 方法中的错误都会通过 return 语句显式
地返回,并且通常调用者不能忽略对返回的错误的处理。
- **组合**
- Go 语言不像 C++、Java 等主流面向对象语言,我们在 Go 中是找不到经典的面向对象语法元素、类型体系和继承机制的Go 推崇的是**组合**的设计哲学。
- 在 Go 语言设计层面Go 设计者为开发者们提供了正交的语法元素,以供后续组合使用,包括:
- Go 语言无类型层次体系,各类型之间是相互独立的,没有子类型的概念;
- 每个类型都可以有自己的方法集合,类型定义与方法实现是正交独立的;
- 实现某个接口时,无需像 Java 那样采用特定关键字修饰;
- 包之间是相对独立的,没有子包的概念。
- 无论是包、接口还是一个个具体的类型定义Go 语言其实是为我们呈现了这样的一幅图景:一座座没有关联的“孤岛”,但每个岛内又都很精彩。那么现在摆在面
前的工作,就是在这些孤岛之间以最适当的方式建立关联,并形成一个整体。而 **Go 选择采用的组合方式,也是最主要的方式**
- Go 语言为支撑组合的设计提供了类型嵌入Type Embedding。通过类型嵌入我们可以将**已经实现的功能嵌入到新类型中**,以快速满足新类型的功能需求,这种方式有些类似
经典面向对象语言中的“继承”机制,但在原理上却与面向对象中的继承完全不同,这是一种 Go 设计者们精心设计的“语法糖”。
- 被嵌入的类型和新类型两者之间没有任何关系甚至相互完全不知道对方的存在更没有经典面向对象语言中的那种父类、子类的关系以及向上、向下转型Type Casting
通过新类型实例调用方法时,方法的匹配主要取决于方法名字,而不是类型。这种组合方式,我称之为垂直组合,即**通过类型嵌入,快速让一个新类型“复用”其他类型已经实现**
**的能力**,实现功能的垂直扩展。
- 垂直组合本质上是一种“能力继承”采用嵌入方式定义的新类型继承了嵌入类型的能力。Go 还有一种常见的组合方式,叫水平组合。和垂直组合的能力继承不同,水平组合是
一种**能力委托Delegate**,我们通常使用**接口类型**来实现水平组合。
- Go 语言中的接口是一个创新设计,它只是方法集合,并且它与实现者之间的关系无需通过显式关键字修饰,它让程序内部各部分之间的耦合降至最低,同时它也是连接程序各个部
分之间“**纽带**”。
- 水平组合的模式有很多,比如一种常见方法就是,通过接受接口类型参数的普通函数进行组合
- func ReadAll(r io.Reader)([]byte, error) // $GOROOT/src/io/ioutil/ioutil.go
- func Copy(dst Writer, src Reader)(written int64, err error) // $GOROOT/src/io/io.go
- 还可以将 Go 语言内置的并发能力进行灵活组合以实现比如通过goroutine+channel 的组合,可以实现类似 Unix Pipe 的能力。
- 组合也让遵循“简单”原则的 Go 语言,在表现力上丝毫不逊色于其他复杂的主流编程语言。
- **并发**
- 并发”这个设计哲学的出现有它的背景,你也知道 CPU 都是靠**提高主频**来改进性能的,但是现在这个做法已经遇到了瓶颈。主频提高导致 CPU 的功耗和发热量剧增,反过来制约
了 CPU 性能的进一步提高。2007 年开始,处理器厂商的竞争焦点**从主频转向了多核**。
- 在这种大背景下Go 的设计者在决定去创建一门新语言的时候,果断将面向**多核、原生支持并发**作为了新语言的设计原则之一。并且Go 放弃了传统的基于操作系统线程的并发模
而采用了用户层轻量级线程Go 将之称为 **goroutine**
- goroutine 占用的资源非常小Go 运行时默认为每个 goroutine 分配的栈空间仅 **2KB**。goroutine 调度的切换也不用陷入trap操作系统内核层完成代价很低。因此一个
Go 程序中可以创建成千上万个并发的 goroutine。而且所有的 Go 代码都在 goroutine 中执行,哪怕是 go 运行时的代码也不例外。
- 在提供了开销较低的 goroutine 的同时Go 还在语言层面内置了辅助并发设计的原语:**channel 和 select**。开发者可以通过语言内置的 channel 传递消息或实现同步,并通过
select 实现多路 channel 的并发控制。相较于传统复杂的线程并发模型Go 对并发的原生支持将大大降低开发人员在开发并发程序时的心智负担。
- 此外,并发的设计哲学不仅仅让 Go 在语法层面提供了并发原语支持,其对 Go 应用程序设计的影响更为重要。并发是一种程序结构设计的方法,它使得并行成为可能
- 采用并发方案设计的程序在单核处理器上也是可以正常运行的,也许在单核上的处理性能可能不如非并发方案。但随着处理器核数的增多,并发方案可以自然地提高处理性能
- 而且并发与组合的哲学是一脉相承的并发是一个更大的组合的概念它在程序设计的全局层面对程序进行拆解组合再映射到程序执行层面上goroutines 各自执行特定的工
作,通过 channel+select 将 goroutines 组合连接起来。并发的存在鼓励程序员在程序设计时进行独立计算的分解,而对并发的原生支持让 Go 语言也更适应现代计算环境
- **面向工程**
- Go 语言设计的初衷,就是面向解决真实世界中 Google 内部大规模软件开发存在的各种问题,为这些问题提供答案,这些问题包括:程序构建慢、依赖管理失控、代码难于理
解、跨语言构建难等
- 在 Go 语言最初设计阶段就将解决工程问题作为 Go 的设计原则之一去考虑 Go 语法、工具链与标准库的设计,这也是 Go 与其他偏学院派、偏
研究型的编程语言在设计思路上的一个重大差异
- 在面向工程设计哲学的驱使下Go 在语法设计细节上做了精心的打磨。比如:
- 重新设计编译单元和目标文件格式,实现 Go 源码快速构建,让大工程的构建时间缩短到类似动态语言的交互式解释的编译速度;
- 如果源文件导入它不使用的包,则程序将无法编译。这可以充分保证任何 Go 程序的依赖树是精确的。这也可以保证在构建程序时不会编译额外的代码,从而最大限度地缩短编译时间;
- 去除包的循环依赖,循环依赖会在大规模的代码中引发问题,因为它们要求编译器同时处理更大的源文件集,这会减慢增量构建;
- 包路径是唯一的,而包名不必唯一的。导入路径必须唯一标识要导入的包,而名称只是包的使用者如何引用其内容的约定。“包名称不必是唯一的”这个约定,大大降低了开发人员给包起唯一名字的心智负担;
- 故意不支持默认函数参数。因为在规模工程中,很多开发者利用默认函数参数机制,向函数添加过多的参数以弥补函数 API 的设计缺陷,这会导致函数拥有太多的参数,降低清晰度和可读性;
- 增加类型别名type alias支持大规模代码库的重构
- 多数功能不需要依赖外部的第三方包或库
- Go 开发者可以直接基于标准库提供的这些包实现一个满足生产要求的 API 服务,从而减少对外部第三方包或库的依赖,降低工程代码依赖管理的复杂性
- Go 语言就提供了足以让所有其它主流语言开发人员羡慕的工具链,工具链涵盖了编译构建、代码格式化、包依赖管理、静态代码检查、测试、文档生成与查看、性能剖析、语言服务器、运行时程序跟踪等方方面面。
- gofmt它统一了 Go 语言的代码风格在其他语言开发者还在为代码风格争论不休的时候Go 开发者可以更加专注于领域业务中。
- 在提供丰富的工具链的同时Go 在标准库中提供了官方的词法分析器、语法解析器和类型检查器相关包,开发者可以基于这些包快速构建并扩展 Go 工具链。
## 2. 基础
### 2.1
## 并发
## 实战