第1章 语言基础 1
1.1 Go语言创世纪 1
1.1.1 来自贝尔实验室特有基因 3
1.1.2 你好,世界 4
1.2 “Hello,World”的革命 5
1.2.1 B语言——Ken Thompson,1969 5
1.2.2 C语言——Dennis Ritchie,1972—1989 5
1.2.3 Newsqueak——Rob Pike,1989 7
1.2.4 Alef——Phil Winterbottom,1993 9
1.2.5 Limbo——Sean Dorward,Phil Winterbottom,Rob Pike,1995 10
1.2.6 Go语言——2007—2009 11
1.2.7 你好,世界!——V2.0 13
1.3 数组、字符串和切片 13
1.3.1 数组 14
1.3.2 字符串 17
1.3.3 切片 21
1.4 函数、方法和接口 27
1.4.1 函数 27
1.4.2 方法 31
1.4.3 接口 35
1.5 面向并发的内存模型 39
1.5.1 Goroutine和系统线程 40
1.5.2 原子操作 40
1.5.3 顺序一致性内存模型 44
1.5.4 初始化顺序 45
1.5.5 Goroutine的创建 46
1.5.6 基于通道的通信 46
1.5.7 不靠谱的同步 48
1.6 常见的并发模式 49
1.6.1 并发版本的“Hello,World” 50
1.6.2 生产者卐消费者模型 52
1.6.3 发布卐订阅模型 53
1.6.4 控制并发数 56
1.6.5 赢者为王 57
1.6.6 素数筛 58
1.6.7 并发的安全退出 59
1.6.8 context包 62
1.7 错误和异常 64
1.7.1 错误处理策略 65
1.7.2 获取错误的上下文 67
1.7.3 错误的错误返回 69
1.7.4 剖析异常 70
1.8 补充说明 73
第2章 CGO编程 74
2.1 快速入门 74
2.1.1 最简CGO程序 74
2.1.2 基于C标准库函数输出字符串 75
2.1.3 使用自己的C函数 75
2.1.4 C代码的模块化 76
2.1.5 用Go重新实现C函数 77
2.1.6 面向C接口的Go编程 78
2.2 CGO基础 79
2.2.1 import“C”语句 79
2.2.2 #cgo语句 81
2.2.3 build标志条件编译 82
2.3 类型转换 83
2.3.1 数值类型 83
2.3.2 Go字符串和切片 85
2.3.3 结构体、联合和枚举类型 86
2.3.4 数组、字符串和切片 89
2.3.5 指针间的转换 91
2.3.6 数值和指针的转换 92
2.3.7 切片间的转换 93
2.4 函数调用 94
2.4.1 Go调用C函数 94
2.4.2 C函数的返回值 94
2.4.3 void函数的返回值 95
2.4.4 C调用Go导出函数 96
2.5 内部机制 97
2.5.1 CGO生成的中间文件 97
2.5.2 Go调用C函数 98
2.5.3 C调用Go函数 101
2.6 实战:封装qsort 103
2.6.1 认识qsort()函数 103
2.6.2 将qsort()函数从Go包导出 104
2.6.3 改进:闭包函数作为比较函数 106
2.6.4 改进:消除用户对unsafe包的依赖 108
2.7 CGO内存模型 110
2.7.1 Go访问C内存 110
2.7.2 C临时访问传入的Go内存 111
2.7.3 C长期持有Go指针对象 113
2.7.4 导出C函数不能返回Go内存 115
2.8 C++类包装 117
2.8.1 C+++类到Go语言对象 117
2.8.2 Go语言对象到C+++类 121
2.8.3 彻底解放C+++的this指针 125
2.9 静态库和动态库 126
2.9.1 使用C静态库 126
2.9.2 使用C动态库 128
2.9.3 导出C静态库 129
2.9.4 导出C动态库 131
2.9.5 导出非main包的函数 131
2.10 编译和链接参数 133
2.10.1 编译参数:CFLAGS巭CPPFLAGS巭CXXFLAGS 133
2.10.2 链接参数:LDFLAGS 133
2.10.3 pkg-config 133
2.10.4 go get链 134
2.10.5 多个非main包中导出C函数 135
2.11 补充说明 135
第3章 Go汇编语言 136
3.1 快速入门 136
3.1.1 实现和声明 136
3.1.2 定义整数变量 137
3.1.3 定义字符串变量 138
3.1.4 定义main()函数 141
3.1.5 特殊字符 141
3.1.6 没有分号 142
3.2 计算机结构 142
3.2.1 图灵机和BrainFuck语言 143
3.2.2 《人力资源机器》游戏 144
3.2.3 X86-64体系结构 145
3.2.4 Go汇编中的伪寄存器 146
3.2.5 X86-64指令集 147
3.3 常量和全局变量 150
3.3.1 常量 150
3.3.2 全局变量 150
3.3.3 变量的内存布局 156
3.3.4 标识符规则和特殊标志 157
3.3.5 小结 158
3.4 函数 158
3.4.1 基本语法 158
3.4.2 函数参数和返回值 160
3.4.3 参数和返回值的内存布局 161
3.4.4 函数中的局部变量 163
3.4.5 调用其他函数 165
3.4.6 宏函数 166
3.5 控制流 167
3.5.1 顺序执行 167
3.5.2 if巭goto跳转 169
3.5.3 for循环 171
3.6 再论函数 172
3.6.1 函数调用规范 172
3.6.2 高级汇编语言 173
3.6.3 PCDATA和FUNCDATA 176
3.6.4 方法函数 177
3.6.5 递归函数:1到n求和 178
3.6.6 闭包函数 180
3.7 汇编语言的威力 182
3.7.1 系统调用 182
3.7.2 直接调用C函数 184
3.7.3 AVX指令 185
3.8 例子:Goroutine ID 187
3.8.1 故意设计没有goid 187
3.8.2 纯Go方式获取goid 187
3.8.3 从g结构体获取goid 189
3.8.4 获取g结构体对应的接口对象 190
3.8.5 goid的应用:局部存储 192
3.9 Delve调试器 194
3.9.1 Delve入门 194
3.9.2 调试汇编程序 198
3.10 补充说明 201
第4章 RPC和Protobuf 203
4.1 RPC入门 203
4.1.1 RPC版“Hello,World” 203
4.1.2 更安全的RPC接口 205
4.1.3 跨语言的RPC 207
4.1.4 HTTP上的RPC 209
4.2 Protobuf 210
4.2.1 Protobuf入门 210
4.2.2 定制代码生成插件 212
4.2.3 自动生成完整的RPC代码 215
4.3 玩转RPC 218
4.3.1 客户端RPC的实现原理 218
4.3.2 基于RPC实现监视功能 220
4.3.3 反向RPC 222
4.3.4 上下文信息 223
4.4 gRPC入门 224
4.4.1 gRPC技术栈 225
4.4.2 gRPC入门 225
4.4.3 gRPC流 227
4.4.4 发布和订阅模式 229
4.5 gRPC进阶 233
4.5.1 证书认证 233
4.5.2 Token认证 236
4.5.3 截取器 238
4.5.4 和Web服务共存 240
4.6 gRPC和Protobuf扩展 241
4.6.1 验证器 241
4.6.2 REST接口 244
4.6.3 Nginx 246
4.7 pbgo:基于Protobuf的框架 246
4.7.1 Protobuf扩展语法 246
4.7.2 插件中读取扩展信息 248
4.7.3 生成REST代码 249
4.7.4 启动REST服务 250
4.8 grpcurl工具 251
4.8.1 启动反射服务 251
4.8.2 查看服务列表 252
4.8.3 服务的方法列表 253
4.8.4 获取类型信息 253
4.8.5 调用方法 254
4.9 补充说明 255
第5章 Go和Web 256
5.1 Web开发简介 256
5.2 请求路由 260
5.2.1 httprouter 260
5.2.2 原理 262
5.2.3 压缩检索树创建过程 263
5.3 中间件 267
5.3.1 代码泥潭 267
5.3.2 使用中间件剥离非业务逻辑 269
5.3.3 更优雅的中间件写法 272
5.3.4 哪些事情适合在中间件中做 273
5.4 请求校验 274
5.4.1 重构请求校验函数 275
5.4.2 用请求校验器解放体力劳动 276
5.4.3 原理 277
5.5 Database和数据库打交道 279
5.5.1 从database巭sql讲起 279
5.5.2 提高生产效率的ORM和SQL Builder 281
5.5.3 脆弱的数据库 283
5.6 服务流量限制 285
5.6.1 常见的流量限制手段 287
5.6.2 原理 289
5.6.3 服务瓶颈和QoS 291
5.7 常见大型Web项目分层 291
5.8 接口和表驱动开发 297
5.8.1 业务系统的发展过程 297
5.8.2 使用函数封装业务流程 298
5.8.3 使用接口来做抽象 298
5.8.4 接口的优缺点 301
5.8.5 表驱动开发 303
5.9 灰度发布和A巭B测试 303
5.9.1 通过分批次部署实现灰度发布 304
5.9.2 通过业务规则进行灰度发布 305
5.9.3 如何实现一套灰度发布系统 306
5.10 补充说明 310
第6章 分布式系统 311
6.1 分布式ID生成器 311
6.1.1 worker_id分配 312
6.1.2 开源实例 313
6.2 分布式锁 316
6.2.1 进程内加锁 317
6.2.2 尝试锁 317
6.2.3 基于Redis的setnx 319
6.2.4 基于ZooKeeper 321
6.2.5 基于etcd 321
6.2.6 如何选择合适的锁 322
6.3 延时任务系统 323
6.3.1 定时器的实现 323
6.3.2 任务分发 325
6.3.3 数据再平衡和幂等考量 326
6.4 分布式搜索引擎 327
6.4.1 搜索引擎 328
6.4.2 异构数据同步 336
6.5 负载均衡 337
6.5.1 常见的负载均衡思路 337
6.5.2 基于洗牌算法的负载均衡 338
6.5.3 ZooKeeper集群的随机节点挑选问题 340
6.5.4 负载均衡算法效果验证 340
6.6 分布式配置管理 341
6.6.1 场景举例 341
6.6.2 使用etcd实现配置更新 342
6.6.3 配置膨胀 345
6.6.4 配置版本管理 345
6.6.5 客户端容错 345
6.7 分布式爬虫 346
6.7.1 基于colly的单机爬虫 346
6.7.2 分布式爬虫 347
6.7.3 结合nats和colly的消息生产 350
6.7.4 结合colly的消息消费 352
6.8 补充说明 353
附录A 使用Go语言常遇到的问题 354
附录B 有趣的代码片段 363