Go语言高级编程
作者: 柴树杉,曹春晖 著
出版时间: 2019
本书从实践出发讲解Go语言的进阶知识。本书共6章,第1章简单回顾Go语言的发展历史;第2章和第3章系统地介绍CGO编程和Go汇编语言的用法;第4章对RPC和Protobuf技术进行深入介绍,并讲述如何打造一个自己的RPC系统;第5章介绍工业级环境的Web系统的设计和相关技术;第6章介绍Go语言在分布式领域的一些编程技术。书中还涉及CGO和汇编方面的知识,其中CGO能够帮助读者继承的软件遗产,而在深入学习Go运行时,汇编对于理解各种语法设计的底层实现是必不可少的知识。此外,本书还包含一些紧跟潮流的内容,介绍开源界流行的gRPC及其相关应用,讲述Go Web框架中的基本实现原理和大型Web项目中的技术要点,引导读者对Go语言进行更深入的应用。本书适合对Go语言的应用已经有一些心得,并希望能够深入理解底层实现原理或者是希望能够在Web开发方面结合Go语言来实现进阶学习的技术人员学习和参考。
目录
- 第1章 语言基础1
- 1.1Go语言创世纪1
- 1.1.1来自贝尔实验室特有基因3
- 1.1.2你好,世界4
- 1.2“Hello, World”的革命5
- 1.2.1B语言——Ken Thompson, 19695
- 1.2.2C语言——Dennis Ritchie,1972—19895
- 1.2.3Newsqueak——Rob Pike, 19897
- 1.2.4Alef——Phil Winterbottom, 19939
- 1.2.5Limbo——Sean Dorward, Phil Winterbottom, Rob Pike, 199510
- 1.2.6Go语言——2007—200911
- 1.2.7你好,世界!——V2.013
- 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.1Goroutine和系统线程40
- 1.5.2原子操作40
- 1.5.3顺序一致性内存模型44
- 1.5.4初始化顺序45
- 1.5.5Goroutine的创建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.8context包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.4C代码的模块化76
- 2.1.5用Go重新实现C函数77
- 2.1.6面向C接口的Go编程78
- 2.2CGO基础79
- 2.2.1import C语句79
- 2.2.2#cgo语句81
- 2.2.3build标志条件编译82
- 2.3类型转换83
- 2.3.1数值类型83
- 2.3.2Go字符串和切片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.1Go调用C函数94
- 2.4.2C函数的返回值94
- 2.4.3void函数的返回值95
- 2.4.4C调用Go导出函数96
- 2.5内部机制97
- 2.5.1CGO生成的中间文件97
- 2.5.2Go调用C函数98
- 2.5.3C调用Go函数101
- 2.6实战:封装qsort103
- 2.6.1认识qsort()函数103
- 2.6.2将qsort()函数从Go包导出104
- 2.6.3改进:闭包函数作为比较函数106
- 2.6.4改进:消除用户对unsafe包的依赖108
- 2.7CGO内存模型110
- 2.7.1Go访问C内存110
- 2.7.2C临时访问传入的Go内存111
- 2.7.3C长期持有Go指针对象113
- 2.7.4导出C函数不能返回Go内存115
- 2.8C++类包装117
- 2.8.1C++类到Go语言对象117
- 2.8.2Go语言对象到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/CXXFLAGS133
- 2.10.2链接参数:LDFLAGS133
- 2.10.3pkg-config133
- 2.10.4go 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.3X86-64体系结构145
- 3.2.4Go汇编中的伪寄存器146
- 3.2.5X86-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.2if/goto跳转169
- 3.5.3for循环171
- 3.6再论函数172
- 3.6.1函数调用规范172
- 3.6.2高级汇编语言173
- 3.6.3PCDATA和FUNCDATA176
- 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.3AVX指令185
- 3.8例子:Goroutine ID187
- 3.8.1故意设计没有goid187
- 3.8.2纯Go方式获取goid187
- 3.8.3从g结构体获取goid189
- 3.8.4获取g结构体对应的接口对象190
- 3.8.5goid的应用:局部存储192
- 3.9Delve调试器194
- 3.9.1Delve入门194
- 3.9.2调试汇编程序198
- 3.10补充说明201
- 第4章RPC和Protobuf203
- 4.1RPC入门203
- 4.1.1RPC版“Hello, World”203
- 4.1.2更安全的RPC接口205
- 4.1.3跨语言的RPC207
- 4.1.4HTTP上的RPC209
- 4.2Protobuf210
- 4.2.1Protobuf入门210
- 4.2.2定制代码生成插件212
- 4.2.3自动生成完整的RPC代码215
- 4.3玩转RPC218
- 4.3.1客户端RPC的实现原理218
- 4.3.2基于RPC实现监视功能220
- 4.3.3反向RPC222
- 4.3.4上下文信息223
- 4.4gRPC入门224
- 4.4.1gRPC技术栈225
- 4.4.2gRPC入门225
- 4.4.3gRPC流227
- 4.4.4发布和订阅模式229
- 4.5gRPC进阶233
- 4.5.1证书认证233
- 4.5.2Token认证236
- 4.5.3截取器238
- 4.5.4和Web服务共存240
- 4.6gRPC和Protobuf扩展241
- 4.6.1验证器241
- 4.6.2REST接口244
- 4.6.3Nginx246
- 4.7pbgo:基于Protobuf的框架246
- 4.7.1Protobuf扩展语法246
- 4.7.2插件中读取扩展信息248
- 4.7.3生成REST代码249
- 4.7.4启动REST服务250
- 4.8grpcurl工具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和Web256
- 5.1Web开发简介256
- 5.2请求路由260
- 5.2.1httprouter260
- 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.5Database 和数据库打交道279
- 5.5.1从database/sql讲起279
- 5.5.2提高生产效率的ORM和
- SQLBuilder281
- 5.5.3脆弱的数据库283
- 5.6服务流量限制285
- 5.6.1常见的流量限制手段287
- 5.6.2原理289
- 5.6.3服务瓶颈和 QoS291
- 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.1worker_id分配312
- 6.1.2开源实例313
- 6.2分布式锁316
- 6.2.1进程内加锁317
- 6.2.2尝试锁317
- 6.2.3基于Redis的setnx319
- 6.2.4基于ZooKeeper321
- 6.2.5基于etcd321
- 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.3ZooKeeper集群的随机节点挑选问题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