背景
一直有在零散的时间用go写点代码,正好借着最近比较有时间写东西的契机,给这个看着年轻,实际也已经发展10几年,并在当下众多开发领域都有不可忽视作用的语言做个介绍吧
golang 的起点
golang 的诞生可以说是时代造就了它,它也成就了研发工具、云原生和高性能服务开发百花齐放的时代
本节主要参考文章:
Go at Google: Language Design in the Service of Software Engineering
翻译-Google 里的 Go 语言:服务于软件工程的语言设计
煎鱼-为什么要开发 Go 这门新语言?
开发痛点
- 当时谷歌使用的语言:C++、Java 和 Python ,已经不适用于计算层面的需求,对于多核处理、大规模计算 和 web应用编程层面,遇到的问题都只能绕过,而不是直接解决
ced by multicore processors, networked systems, massive computation clusters, and the web programming model
-
软件规模:千万行代码,数千程序员共同维护(言外之意就是效率太低了,语言和开发者两个层面)
-
编译时间太长,几分钟到几小时都有(特别讲了 C++ #include 在编译上的消耗: Dependencies in C and C++)
-
依赖混乱,跨语言构建麻烦
-
难以编写自动化工具
译者注: 结合原文之后的内容来看,最主要的两类问题就是 大规模的 C++ 项目编译时间长,以及多种语言维护成本高的问题
发明者
Robert Griesemer: 参与过谷歌 V8 引擎的开发,Sawzall 语言(谷歌内部用的日志分级打印库,之后被 go 的日志库替代)、JVM 和 strongtalk 系统(用于类型检查,现在不维护了)
Rob Pike:Unix 开发者之一,为 Unix 编写过终端,后续编写过 sam、acme 文本编辑器,再之后和 Ken Thompson 一起创造了 UTF-8
Ken Thompson: Unix 开发者之一,B语言(C语言的前身)的发明者,后面 Dennis Ritchie 基于 B 语言又开发了 C语言。1983 年 Ken 和 Dennis 一同获得了图灵奖
扩展: Russ Cox,你可以在 golang 的很多 issue 中看到他的身影,他也是go代码提交次数最多的
推荐阅读: 图灵奖历届得主、golang talk
吉祥物
名字: Gopher(囊地鼠)
由 Rob Pike 的妻子 Renee French 创作,官网甚至还有细节描述呢
go 是什么样的语言
为了解决前面说的问题,go 必须有下面的特点:
- 高效率、可扩展、满足生产力需求
- 必须有可扩展性,对于代码量多、依赖多、开发者多的大型项目 必须能很好运作
- 自带 GC,并在对象内存申请、释放空间上进行优化,保证GC尽量不影响业务运行
- 符合C的编程习惯,让程序员能丝滑切换(如: 静态类型对象)
- 必须足够现代化,在多核处理、网络、web 应用开发上需要更加方便,也要支持更多新特性,如内置的并发编程方案
时间和版本
golang 版本来源: 从 1.10 到最新
go 语言特点
本节中,笔者将结合自己平时开发过程中使用到的 go 比较核心的特性进行基本介绍,这些也是 go 和其他编程语言最不同的地方,包括 基本语法、基本指令、函数、关键字、基本类型、打包方式等
语法
- 通过大括号划分代码块,保证大项目下依旧结构清晰
- 不需要分号,虽然加了也不会报错
- 不需要小括号,比如在 if a < b 判断语句、for 循环中的判断,都不需要
- 未使用的 import / 局部变量将报错,保证编译不会引入多余的代码,提升编译效率
执行
- go build: 统一的打包语句
- 编译后生成一个可执行文件
debug
- pprof: 自带性能指标库,可通过火焰图、命令行查看堆栈情况、堆大小、各协程调用耗时等
函数
- 接口不需要显式说明继承,只要实现了接口的方法即算是继承
- 接口可以通过 指针接收器(pointer receiver)或 值接收器(value receiver)实现,分别适合 setter 和 getter 的实现,大对象的方法也适合用 指针接收器 避免频繁拷贝(扩展: 什么时候适合用什么接收器?)
- 方法可以作为对象传递,可以作为 map 的 value、声明匿名方法、定义闭包(闭包常用于定义配置方法,可参考 trpc 的 server.Opts 及其具体实现方法)
- 不支持重载,用开发必须增加少量代码的代价换来了 代码可读性和编译性能(一些讨论)
- 建议使用组合(composition),而不是继承(inheritance)(对比: java 类在多次继承后,要找到只在基类实现的方法的源代码,需要跳很多次才能找到,相反,组合的结构就很清晰,只会有一层)
错误处理
- 没有异常机制,通过 error + 返回值的方式返回报错,在外部及时处理错误(也促使开发者封装好会出现 error 的代码块,减少主函数的层级)
- 通过 panic + recover 机制捕获协程中发生的严重错误(如空指针)
基本类型和关键字
- 切片(slice)和数组(array): 切片包含数组、长度(len)和容量(cap)
- map: 非协程安全的 map(slice 也是非协程安全的,go 设计上就是让 channel 成为唯一的协程间通信对象)
- defer: 常用于释放资源、panic 处理
- make: 申请切片、map 和 channel 的空间
- init 方法: 被引用的时候,包内的所有 init 方法会自动执行。单个包内的 init 方法执行顺序按 文件名字段序 -> 方法从上到下 的顺序执行,参考
- 声明对象的方式: 通过 var 指定对象名称和类型,或直接 a := 1,编译器自动填充字段类型
- const: 可声明包内的全局常量,但不能用于声明局部变量(从设计上,避免开发者在协程之间直接共享变量)
- iota: 枚举,只能是数值
- 空白标识符: 下划线(_),可用于忽略返回值,迭代 slice 或者 map 时忽略 下标/key,以及 init import(只执行包的 init 方法,不需要显式调用包的资源,如: import _ “net/http/pprof”)
- select case: 多个 Channel 同时读取方式
- 总共只有25个关键字,相比: C99 有 37 个,C++11 有 84 个
原生库和方法
- time: 时间,方法有 time.Parse, time.Now, time.DateTime(常量 2006-01-02 15:04:05)等
- io.ReadAll: 数据流
- bytes.Buffer: 字符串缓冲
- strings、maps、slices: 集合类型的工具方法,如 strings.ReplaceAll、maps.Clone、slices.Sort 等
- regexp: 正则
- net/http: http 核心库
- sort: 1.19 之前的数组排序库
- log/slog: 1.21 新增的日志库,支持等级和格式化打印
- Sync: 协程之间同步、状态共享相关方法,如锁: Mutex;仅执行一次: Once;等待n个协程执行: WaitGroup;协程间复用对象: Pool;等待和唤醒:Cond
反射
- reflect.TypeOf: 获取对象的具体类型
- reflect.Kind: 获取对象的类别(和类型的区别: 具体值或指针的类别为 interface / pointer,类型可以拿到具体的 struct或接口类型)
- reflect.ValueOf: 获取对象的具体值
- struct tag: 标注 struct 内属性的附加信息,一般用于 json序列化、yaml 内容解析、orm 字段映射等场景
并发
- 协程(Routine): 用户层的“线程”,通过 go func() 开启并发
- 管道(Channel): 协程间的通信方式
- 上下文(Context): 协程间传递数据、父协程控制子协程状态的媒介
运行时
- GPM 模型: Go 进程的核心
- GC: 三色标记法、混合写屏障等
包管理
- 从 git 路径引用,没有“中央仓库”的说法,去中心化的设计
- 以 git tag 或者 commit id 作为版本标识
- 对象和方法,通过首字母标识是否对外可见,简单明了
工具
- gofmt、goformat、staticcheck: 代码格式化
- test、bench、fuzz: 普通测试、并发测试、混沌测试
- doc: 文档,go 的文档可在源码中通过注释编写,并自动生成
以上就是自己常接触的原生库了,可以从 go源码的src目录 更多的实现细节
自己写的和go有关的博客
golang 编程规范查漏补缺
golang-使用 go test 输出单元测试覆盖率
golang-单元测试和mock框架的介绍和推荐
golang-使用 godoc 工具编写代码注释
golang-文章翻译-go高效编程
golang-文章翻译-go常见的10种错误
vscode 使用技巧(vscode+go插件的开发环境)
go 学习方法总结
官网
Go 的官网其实是最好的学习资料,教程、博客、社区动态都有,绝对适合作为入门资料的第一位
下面说明官网中右上角各个栏目的内容
为什么使用 Go(Why Go)
大体就是 Go 在一些通用领域的使用场景,以及在大厂的使用情况
Case Studies: 大厂的使用场景,如 Google、CloudFare、Meta、Netflix 等
Use Cases: 在云服务、网络通信、命令行工具、网页/客户端应用 和 运维和可持续开发(DevOps、SRE)领域的应用
每一个领域下都有比较流行的开源项目的推荐
Security: 如何提升自己工程的安全性,如通过 govulncheck 检查项目存在哪些已知漏洞
Learn
非常适合上手的 go tour,几乎覆盖了 go,可以说练习完所有示例就会写 go 代码了
Docs
所有官方文章: 可以挑讲原理的文章看,如 Using and understanding Go 中的 A Guide to the Go Garbage Collector 以及 References 中的 The Go Memory Model
Effective Go: 上手之后,进一步了解 go 编程风格的必读文章
std: 标准库文档
faq: 囊括了从其他语言迁到 go 语言的常见问题,也体现了 go 的设计思想
社区
blog: go 博客,社区新动态
开源项目和代码推荐
go
go 本身的源码就很好阅读,也和 go 本身语法比较简洁有关。如果你想了解 go 的基础库的原理,大可不用搜一堆博客,一点点地消化别人的总结,读go源码就是最好的学习go原理的方法
包括: sync, runtime, context, reflect,这些都是 go 的核心库
web 应用
开发后台必须要掌握的就是 web 框架,当然不同公司对它的定制化也会不同,如果想从简单的入手,使用官方推荐的 gin 就行。想参考大厂实际用的,可参考国内开发者开源的 web 框架(功能比较全),如字节的 hertz、go-zero、goframe 等
中间件
gorm: 数据库 orm 库
redis: redigo
kafka: sarama
云原生和监控
如 Kubernetes、Istio、Prometheus、Grafana 等,也能接触到当下比较流行的项目
命令行工具
以往我们需要用工具进行文档格式转换、爬虫、文件批量下载等场景,可能会主要以 python 项目作参考,因为它语法简单,执行方便,你需要的只是一个 python 环境
现在,拥有类似特性的 go 让我们多了一个选项,同样的需求,也可以看 go 社区有没有现成的方案。甚至 go 只需要编译好的可执行文件,更加方便
具体用哪个工具因需求而异,可以参考别人的整理,这里列几个可能比较常用的:
hugo: 博客网站生成,类似 hexo
buffalo: 快速生成 web 项目
vhs: 终端录制
pget: 并发下载
fsnotify: 系统文件监听
lux: 网站视频下载工具(不能下载需要登录才能下的超高画质)
migrate: 跨不同类型数据库迁移
博客推荐
golang 在国内大厂实践还是比较多的,因此国内开发者写的博客也不会缺少。本着技术的学习方法来说,这里推荐两个博主 分别对应前沿动态,和深度剖析原理
煎鱼: go 语言开发者,对 go 社区动态、新版本特性的跟踪比较及时,类似的博主还有 polarisxu
go语言设计与实现: go 原理和设计思想,这个博客对技术的思考方式值得应用到所有技术的学习上: 从为什么这么设计的问题基础上,去理解技术实现细节