描述
开 本: 16开纸 张: 胶版纸包 装: 平装是否套装: 否国际标准书号ISBN: 9787115619785
(2)本书结构清晰全面。本书以普通应用开发者会涉及的知识点为纲要,深入剖析了这些知识点的底层机制,全面而透彻。
(3)内容由浅入深,便于理解。顺序阅读,会起到很好效果。当然,也可用于查询式阅读。
(4)作者资历深厚,拥有十多年的开发经验,拥有丰富的开发经验。
作。书中首先介绍 Go 语言的基本概念,并通过“hello world”程序引导读者熟悉 Go 的工具链。接下来逐步深
入,介绍面向包的设计、测试框架、错误与异常处理等内容。第 8 章开始探讨指针和内存逃逸分析,这对于理
解 Go 语言的内存模型至关重要。随后的章节涉及数据结构、面向对象和接口编程等核心知识。从第 15 章开始,
重点转向并发编程,从基本的并发模式到复杂的并发原理,再到内存管理和垃圾回收等高级主题。最后几
章关注实际开发中的问题,如使用标准库和第三方库、性能问题分析与追踪,以及重构“hello world”示
例代码。
本书适合想要掌握 Go 语言的基本使用方法,以及了解其底层工作原理和设计实现的初、中级读者阅读。
1.1 Go 语言的发展里程碑 1
1.2 云时代 Go 语言的发展趋势 2
1.3 Go 语言优秀的语言特性 3
1.3.1 “少即是多”的设计哲学 3
1.3.2 强大的 runtime 4
1.3.3 面向接口编程 5
1.3.4 为工程服务的语言 6
1.3.5 自带标准化的测试框架 7
1.3.6 丰富的标准库和第三方库 7
1.4 强大的生态圈和成功案例 8
1.5 Go 程序是如何运行的 8
1.6 plan9 与 Go 语言 10
1.6.1 寄存器 10
1.6.2 Go 语言的反汇编方法 11
1.6.3 反汇编的查看示例 13
第 2 章 “hello world”与工具链 15
2.1 Go 语言的安装和配置 15
2.1.1 下载和安装 15
2.1.2 配置 Go 语言的环境变量 15
2.1.3 查看配置信息 16
2.2 第 一个程序“hello world” 16
2.2.1 “hello world”程序的代码说明 17
2.2.2 代码的编译与运行 21
2.2.3 “hello world”示例总结 21
2.3 Go 语言的工具链命令 22
2.3.1 与编译执行有关的工具链命令 22
2.3.2 获取与安装第三方包 22
2.3.3 工具包组合命令 go tool 23
2.3.4 跨平台交叉编译 23
2.3.5 网络代理 GOPROXY 24
第 3 章 Go 语言的基础知识 26
3.1 Go 语言的常用规范 26
3.1.1 命名与注释 26
3.1.2 声明 27
3.1.3 对变量赋值 28
3.1.4 包和文件 28
3.2 数据类型 29
3.2.1 基本类型 29
3.2.2 非引用类型和引用类型 31
3.2.3 用户自定义类型 32
3.2.4 类型别名 32
3.2.5 传参方式 33
3.3 变量的本质 33
3.3.1 类型的两个要素 33
3.3.2 变量的声明 34
3.3.3 零值机制 35
3.3.4 短变量声明与类型转换 35
3.4 常量 36
3.4.1 常量 iota 37
3.4.2 常量的类型提升机制 38
3.5 运算符 38
3.5.1 算术运算符 39
3.5.2 比较运算符 39
3.5.3 逻辑运算符 39
3.5.4 位运算符 40
3.5.5 赋值运算符 40
3.5.6 指针运算符 41
3.6 结构化语法 41
3.6.1 循环结构 41
3.6.2 条件语句 42
3.6.3 switch-case 语句 42
3.6.4 控制或跳出循环语句的关键字 43
3.7 类型转换 43
3.7.1 转换的语法 44
3.7.2 类型断言 44
3.8 Go 语言的语法糖 44
3.8.1 短变量声明和new 函数 44
3.8.2 符号“”与切片 44
3.8.3 for range 45
第 4 章 面向包的设计与依赖管理 46
4.1 包的使用 46
4.1.1 包的概述 46
4.1.2 包的查找方式 47
4.1.3 包加载的顺序 48
4.1.4 包中 init 函数的加载 49
4.1.5 包加载顺序的示例 49
4.1.6 包的使用总结 50
4.2 面向包的设计 50
4.3 包管理工具 Go Module 51
4.3.1 包管理的方式 51
4.3.2 Go Module 简介 52
4.3.3 开启 Go Module 52
4.3.4 Go Module 的优点 52
4.3.5 使用 Go Module 53
4.3.6 go.mod 文件中的命令 57
4.3.7 升级依赖包的方法 58
4.3.8 依赖包版本的选择 58
4.3.9 语义版本的导入路径语法 58
4.3.10 Go Module 的使用总结 59
第 5 章 测试框架 60
5.1 Go 语言中的测试框架 60
5.1.1 测试使用的约定 60
5.1.2 标准库 testing 的辅助功能函数 61
5.1.3 测试框架示例 61
5.1.4 使用测试命令 62
5.2 单元测试 63
5.2.1 指定测试用例 63
5.2.2 单元测试之子测试 64
5.2.3 帮助函数 64
5.3 测试代码的覆盖率 65
5.4 断言 66
5.5 基准测试 67
5.5.1 基准测试场景 67
5.5.2 基准测试的方法 67
5.5.3 基准测试之子测试 68
5.5.4 基准测试示例 68
5.6 与网络有关的模拟测试 70
5.7 与测试有关的第三方工具 71
5.7.1 gomock 71
5.7.2 BDD 71
第 6 章 错误与异常处理 73
6.1 error 的引入 73
6.1.1 预定义的错误类型 74
6.1.2 快速创建错误类型 74
6.1.3 自定义错误 75
6.1.4 接口在错误处理上的妙用 76
6.1.5 自定义错误的陷阱 77
6.1.6 获取和处理错误 78
6.1.7 Go 语言作者关于错误处理的观点 78
6.2 异常处理 79
6.2.1 panic 的使用 79
6.2.2 defer 函数的设计与使用陷阱 79
6.2.3 recover 函数的使用 81
6.3 面向错误和恢复的设计 82
6.4 带堆栈信息的 error 83
6.5 标准库 errors 的改进 84
6.6 errGroup 对象 86
6.7 日志系统的引入 87
6.7.1 日志概述 88
6.7.2 第三方日志框架 88
第 7 章 编码与字符串 89
7.1 字符编码 89
7.1.1 字符的编码方式 89
7.1.2 使用字符类型的注意事项 90
7.2 字符串 90
7.2.1 字符串的声明和初始化 90
7.2.2 字符串的数据结构 90
7.2.3 遍历字符串 91
7.2.4 字符串的长度问题 92
7.2.5 字符串的备份 92
7.2.6 字符串拼接 92
7.3 字符串与基本类型互转 94
第 8 章 指针与内存逃逸分析 96
8.1 活动帧的作用 96
8.2 值语义的本质 98
8.3 指针 99
8.3.1 指针的由来 99
8.3.2 指针和指针类型 100
8.3.3 使用指针运算符的注意事项 101
8.3.4 nil 指针 101
8.3.5 指针数组与数组指针 102
8.3.6 关于指针的补充说明 102
8.4 内存逃逸分析 103
8.4.1 内存逃逸分析的由来 103
8.4.2 内存逃逸分析的作用 103
8.4.3 两种情况会引起内存逃逸分析 103
8.4.4 内存逃逸分析示例 104
8.4.5 函数内联 106
8.4.6 手动控制内存逃逸分析 107
8.5 引用类型与深、浅拷贝 108
第 9 章 数据结构 109
9.1 面向数据的设计 109
9.1.1 编码和硬件 109
9.1.2 可预测的内存访问模式 111
9.2 数组 111
9.2.1 数组的声明及初始化 112
9.2.2 数组在内存中的形式 112
9.2.3 遍历数组 113
9.2.4 数组的截取 113
9.2.5 数组的反转 114
9.3 切片 114
9.3.1 切片的设计 114
9.3.2 切片的创建与初始化 116
9.3.3 切片的长度与容量 117
9.3.4 nil 切片和空切片 117
9.3.5 切片的共享底层数组 118
9.3.6 append 函数与切片的扩容 119
9.3.7 append 函数引发的内存泄漏 121
9.3.8 三下标切片 122
9.3.9 切片的复制 122
9.3.10 切片的比较 122
9.3.11 删除切片中的元素 123
9.3.12 特殊的切片:字符串 125
9.3.13 数组与切片的对比 125
9.4 映射 125
9.4.1 选择合适的键值类型 126
9.4.2 映射的声明和初始化 127
9.4.3 映射的使用 128
9.4.4 映射的排序 129
9.4.5 映射的扩容 130
9.4.6 映射的并发安全性 130
9.4.7 映射的删除机制 132
9.4.8 映射的设计 133
9.5 数据结构中的常见问题 138
9.5.1 make 与 new 的差异 138
9.5.2 使用引用类型前先分配空间 139
9.5.3 可能发生内存泄漏的情况 139
第 10 章 结构体与内存对齐 140
10.1 结构体 140
10.1.1 结构体的定义 140
10.1.2 结构体的初始化 142
10.1.3 结构体的类型转换 143
10.1.4 结构体比较 143
10.1.5 结构体的值 144
10.2 序列化与反序列化 145
10.2.1 序列化 145
10.2.2 反序列化 145
10.2.3 使用 tag 146
10.3 unsafe 包 147
10.3.1 unsafe.Pointer 类型 147
10.3.2 unsafe 包简介 147
10.3.3 unsafe 包中的函数 148
10.3.4 unsafe 包的使用方式 150
10.4 内存对齐 152
10.4.1 内存对齐的概念 152
10.4.2 数据类型的尺寸 153
10.4.3 内存自动对齐 153
10.4.4 内存对齐的示例 154
第 11 章 函数 155
11.1 认识函数 155
11.1.1 函数的定义 155
11.1.2 函数的种类 156
11.2 defer 函数 158
11.2.1 defer 函数的使用场景 158
11.2.2 当 panic 遇到defer 函数 159
11.2.3 defer 函数与for 循环语句 160
11.3 作为数据类型的函数 161
11.4 函数类型的使用场景 161
11.4.1 匿名函数 161
11.4.2 回调函数 162
11.4.3 闭包 163
11.5 函数的别名 165
第 12 章 面向“对象”编程 166
12.1 封装 166
12.1.1 方法 166
12.1.2 方法的声明方式 167
12.1.3 接收者类型与接收者基础类型 167
12.1.4 接收者使用的语义 168
12.1.5 两种语义本质上的区别 169
12.1.6 解耦带来的问题 170
12.1.7 更为复杂的调用方式 170
12.1.8 隐式转换 171
12.1.9 关于封装的总结 173
12.2 继承 173
12.2.1 Go 语言不支持继承 173
12.2.2 用“内嵌+组合”替代继承 174
12.2.3 扩展已有的包 175
12.3 多态 176
12.3.1 接口的定义 176
12.3.2 鸭子类型 176
12.3.3 接口与协议 178
12.3.4 接口如何实现多态 178
第 13 章 面向接口编程 180
13.1 接口编程哲学 180
13.2 接口与组合 181
13.2.1 接口的设计准则 181
13.2.2 接口与组合示例 182
13.2.3 组合的多样化 183
13.3 接口的剖析 183
13.3.1 与接口相关的说明 183
13.3.2 空接口与包裹 184
13.3.3 实现接口类型 185
13.3.4 接口包裹非接口值 185
13.3.5 接口与多态 185
13.3.6 接口类型断言 186
13.3.7 强制转换接口类型 187
13.3.8 接口类型与隐式声明 187
13.3.9 类型转换的时间复杂度 188
13.4 接口的设计原则 188
13.4.1 错误的接口设计 189
13.4.2 基于数据驱动的接口设计 189
13.4.3 类型断言在 API 设计中的应用 189
13.4.4 接口设计的建议 190
13.5 检查接口的实现 190
13.6 空接口与类型断言 193
13.7 接口值的比较 194
13.8 检查运行阶段的接口类型 195
第 14 章 反射 196
14.1 反射的概念 196
14.2 接口与反射 198
14.2.1 静态类型与动态类型 198
14.2.2 空接口 199
14.2.3 类型的底层分析 199
14.3 反射包介绍 201
14.3.1 理解反射对象的转换机制 201
14.3.2 reflect.Type 接口的转换方式 202
14.3.3 reflect.Value 结构体类型的使用方法 204
14.4 反射包的使用示例 207
14.4.1 获取变量的类型和值 208
14.4.2 获取结构体的属性和方法 209
14.4.3 动态调用方法和传值 209
14.4.4 修改接口值 210
14.4.5 判断结构体实现了哪个接口 211
14.5 反射的三个定律 212
14.6 反射的应用场景 212
14.7 反射的性能 213
第 15 章 并发编程 214
15.1 感受并发的魅力 214
15.1.1 并发和并行 214
15.1.2 并发带来的好处 215
15.1.3 “hello goroutine” 215
15.1.4 协程的执行顺序 216
15.1.5 控制协程的几种方式 216
15.2 sync.WaitGroup 217
15.2.1 sync.WaitGroup 的三个方法 217
15.2.2 使用 sync.WaitGroup 的模板 217
15.2.3 使用 sync.WaitGroup 时的注意事项 218
15.2.4 为 sync.WaitGroup 增加额外的功能 218
15.3 数据竞争问题 219
15.3.1 临界区 219
15.3.2 数据竞争的检测方法 219
15.3.3 解决临界区的数据安全问题 220
15.4 传统的锁 221
15.4.1 锁的概念 221
15.4.2 互斥锁 Mutex 222
15.4.3 Mutex 的工作模式 224
15.4.4 读写锁 RWMutex 224
15.4.5 重入与 TryLock 226
15.5 原子操作介绍 227
15.5.1 Go 语言中的原子操作 228
15.5.2 atomic 包的使用 228
第 16 章 并发与通道 230
16.1 通道的行为 230
16.2 创建通道 231
16.3 通道的特性 231
16.3.1 通道的成对性 231
16.3.2 通道的阻塞性 232
16.3.3 通道与死锁 232
16.3.4 让出当前协程的执行权 233
16.3.5 关闭通道 234
16.3.6 遍历通道 236
16.4 通道的其他特性 237
16.4.1 带缓冲的通道 237
16.4.2 缓冲区与延迟保障 238
16.4.3 通道的方向 239
16.4.4 通道的状态 239
16.5 通道的使用建议 239
16.6 select 机制 240
16.6.1 select 机制的介绍与示例 241
16.6.2 select 与超时控制 242
16.7 通道的模式 243
16.7.1 等待任务模式 243
16.7.2 等待结果模式 244
16.7.3 等待完成模式 246
16.7.4 Pooling 模式 247
16.7.5 流水线模式 248
16.7.6 FanOut/FanIn 模式 249
16.7.7 Drop 模式 251
第 17 章 其他并发技术 252
17.1 context 包 252
17.1.1 context 包的使用场景 253
17.1.2 context 包中的接口和函数 253
17.1.3 context 包的使用流程 254
17.1.4 context.Context 接口 255
17.1.5 生成 Context 的方法 255
17.1.6 Context 与请求超时 258
17.1.7 Context 的使用总结 260
17.2 sync.Cond 261
17.3 sync.Once 262
17.4 sync.Map 263
17.5 sync.Pool 265
17.5.1 sync.Pool 的介绍 265
17.5.2 缓存对象的生命周期 266
17.5.3 sync.Pool 的使用场景及存在的问题 267
17.6 实现对象池 268
17.7 常用连接池 269
17.8 并发技术选型 270
第 18 章 并发原理 271
18.1 怎样让程序跑得更快 271
18.1.1 从单进程到多线程 271
18.1.2 工作任务的种类 272
18.2 Go 语言中的协程 273
18.2.1 内核态线程与用户态线程 273
18.2.2 轻量级的协程 274
18.2.3 改造后的 Go 语言协程 275
18.2.4 简说 Go 语言协程的调度 275
18.2.5 协作式与抢占式调度器 277
18.2.6 协程与 I/O 多路复用 277
18.3 GPM 调度流程 278
18.3.1 GPM 调度模型 278
18.3.2 G 的调度 280
18.3.3 P 的调度 282
18.3.4 M 的调度 285
18.3.5 探索调度器的调度流程 286
18.3.6 循环调度 290
18.3.7 任务执行函数 execute 294
18.4 监控线程 sysmon 297
18.5 main 函数与协程的执行顺序 299
18.6 可视化分析 GPM 调度 300
18.6.1 使用 trace 分析GPM 调度 300
18.6.2 使用 GODEBUG调试 GPM 调度 302
18.7 深入探索通道 303
18.7.1 通道的底层数据结构hchan 303
18.7.2 发生阻塞的条件 304
18.7.3 select 多路复用的底层逻辑 305
第 19 章 内存管理 307
19.1 runtime 307
19.2 内存分配模型 307
19.2.1 内存模型 308
19.2.2 内存分配过程 308
19.2.3 span 与预设的内存大小和规格 309
19.3 内存管理单元 310
19.3.1 mspan 311
19.3.2 mheap 312
19.3.3 heapArena 313
19.3.4 mcentral 313
19.3.5 mcache 315
19.3.6 内存的多级分配管理 316
19.4 对象分类及分配策略 317
19.4.1 微小对象 317
19.4.2 小对象和大对象 318
19.5 堆内存分配总结 318
第 20 章 垃圾回收 319
20.1 垃圾回收算法 320
20.2 Go 语言的垃圾回收算法 321
20.2.1 标记清扫算法 322
20.2.2 三色标记法 323
20.2.3 三色标记与并发问题 325
20.2.4 三色不变式与屏障技术 326
20.2.5 插入写屏障 327
20.2.6 删除写屏障 330
20.2.7 混合写屏障 333
20.2.8 并发增量式垃圾回收 334
20.3 触发垃圾回收的时机 334
20.4 查看运行时的垃圾回收信息 335
20.5 垃圾回收优化示例 337
20.5.1 传递复杂对象时建议使用指针 337
20.5.2 自动扩容的代价 339
第 21 章 使用标准库和第三方库 341
21.1 I/O 操作 342
21.1.1 io 包 342
21.1.2 os 包 343
21.1.3 bufio 包 344
21.1.4 bytes 包 345
21.1.5 ioutil 包与替换方案 346
21.1.6 读取文件的示例 346
21.1.7 大文件读取方案 348
21.1.8 文件的复制 350
21.1.9 断点续传 351
21.2 网络操作 352
21.2.1 Socket 编程 352
21.2.2 net/http 包 355
21.2.3 与网络编程相关的其他包 359
21.3 与时间有关的标准库 359
21.3.1 时间函数 360
21.3.2 时间戳 360
21.3.3 时间的格式化与解析 361
21.4 随机数 362
21.5 正则表达式 362
21.6 flag 包的使用 363
21.6.1 简单标记的声明方式 363
21.6.2 其他使用方式 364
21.7 os 包的使用 365
21.8 crypto 包 367
21.9 base64 编码 367
21.10 fmt 包 368
21.11 使用第三方库 369
第 22 章 性能问题分析与追踪 370
22.1 性能优化概述 370
22.2 性能优化的步骤 371
22.3 硬件与软件的性能指标 373
22.4 优化工具概述 374
22.4.1 runtime.MemStats 374
22.4.2 Benchmark 374
22.4.3 go tool pprof 工具 375
22.4.4 runtime/pprof 包 375
22.4.5 net/http/pprof 包 376
22.4.6 go tool trace 工具 377
22.4.7 fgprof 包 377
22.4.8 coredump 377
22.4.9 gcflags 379
22.4.10 GODEBUG 379
22.4.11 使用场景总结 380
22.5 性能优化总结 380
22.6 使用 go tool pprof 工具进行性能分析的示例 380
22.7 pprof 包结合 HTTP 服务使用的示例 387
22.8 pprof 包和 fgprof 包的使用对比 390
22.9 go tool trace 工具的使用示例 391
22.10 持续性能分析 392
22.11 性能问题的定位及处理建议 393
22.11.1 CPU 占用率高的定位及处理建议 393
22.11.2 内存使用率高的定位及处理建议 394
22.11.3 I/O 高的定位及处理建议 395
22.11.4 阻塞问题的定位及处理建议 395
22.11.5 协程泄露的定位及处理建议 396
第 23 章 重构“hello world” 397
23.1 搭建业务处理框架 397
23.2 设计解耦的读写接口 398
23.2.1 用结构体代替读写方法 398
23.2.2 使用组合接口 399
23.3 业务实现 401
23.3.1 读日志数据 401
23.3.2 Nginx 日志数据的说明及处理 402
23.3.3 处理日志数据的关键代码 403
23.3.4 实现数据归档 404
23.4 构建 HTTP 服务发布数据 405
23.4.1 埋点处理 405
23.4.2 构建 HTTP 服务发布数据的步骤 406
23.5 整合 Prometheus 发布数据 408
23.5.1 引用第三方prometheus 包 408
23.5.2 实现自定义的exporter 409
23.6 代码细节的提升 412
23.7 总结 412
评论
还没有评论。