Author:华章程序员书库
Go圈知名架构师和布道者撰写,3大Go社区力荐,哲学、思维、技巧等66个主题快速帮你写出高质量代码) (华章程序员书库
Tags
Support Statistics
¥.00 ·
0times
Text Preview (First 20 pages)
Registered users can read the full content for free
Register as a Gaohf Library member to read the complete e-book online for free and enjoy a better reading experience.
Page
1
(This page has no text content)
Page
2
目 录 作者简介 推荐语 推荐序 前言 第八部分 测试、性能剖析与调试 第40条 理解包内测试与包外测试的差别 40.1 官方文档的“自相矛盾” 40.2 包内测试与包外测试 第41条 有层次地组织测试代码 41.1 经典模式——平铺 41.2 xUnit家族模式 41.3 测试固件 第42条 优先编写表驱动的测试 42.1 Go测试代码的一般逻辑 42.2 表驱动的测试实践 42.3 表驱动测试的优点 42.4 表驱动测试实践中的注意事项 第43条 使用testdata管理测试依赖的外部数据文件 43.1 testdata目录 43.2 golden文件惯用法 第44条 正确运用fake、stub和mock等辅助单元测试 44.1 fake:真实组件或服务的简化实现版替身 44.2 stub:对返回结果有一定预设控制能力的替身 44.3 mock:专用于行为观察和验证的替身 第45条 使用模糊测试让潜在bug无处遁形 45.1 模糊测试在挖掘Go代码的潜在bug中的作用 45.2 go-fuzz的初步工作原理 45.3 go-fuzz使用方法 45.4 使用go-fuzz建立模糊测试的示例 45.5 让模糊测试成为“一等公民” 第46条 为被测对象建立性能基准 46.1 性能基准测试在Go语言中是“一等公民” 46.2 顺序执行和并行执行的性能基准测试 46.3 使用性能基准比较工具
Page
3
46.4 排除额外干扰,让基准测试更精确 第47条 使用pprof对程序进行性能剖析 47.1 pprof的工作原理 47.2 使用pprof进行性能剖析的实例 第48条 使用expvar输出度量数据,辅助定位性能瓶颈点 48.1 expvar包的工作原理 48.2 自定义应用通过expvar输出的度量数据 48.3 输出数据的展示 第49条 使用Delve调试Go代码 49.1 关于调试,你首先应该知道的几件事 49.2 Go调试工具的选择 49.3 Delve调试基础、原理与架构 49.4 并发、Coredump文件与挂接进程调试 第九部分 标准库、反射与cgo 第50条 理解Go TCP Socket网络编程模型 50.1 TCP Socket网络编程模型 50.2 TCP连接的建立 50.3 Socket读写 50.4 Socket属性 50.5 关闭连接 第51条 使用net/http包实现安全通信 51.1 HTTPS:在安全传输层上运行的HTTP协议 51.2 HTTPS安全传输层的工作机制 51.3 非对称加密和公钥证书 51.4 对服务端公钥证书的校验 51.5 对客户端公钥证书的校验 第52条 掌握字符集的原理和字符编码方案间的转换 52.1 字符与字符集 52.2 Unicode字符集的诞生与UTF-8编码方案 52.3 字符编码方案间的转换 第53条 掌握使用time包的正确方式 53.1 时间的基础操作 53.2 时间的格式化输出 53.3 定时器的使用 第54条 不要忽略对系统信号的处理 54.1 为什么不能忽略对系统信号的处理 54.2 Go语言对系统信号处理的支持
Page
4
54.3 使用系统信号实现程序的优雅退出 第55条 使用crypto下的密码学包构建安全应用 55.1 Go密码学包概览与设计原则 55.2 分组密码算法 55.3 公钥密码 55.4 单向散列函数 55.5 消息认证码 55.6 数字签名 55.7 随机数生成 第56条 掌握bytes包和strings包的基本操作 56.1 查找与替换 56.2 比较 56.3 分割 56.4 拼接 56.5 修剪与变换 56.6 快速对接I/O模型 第57条 理解标准库的读写模型 57.1 直接读写字节序列 57.2 直接读写抽象数据类型实例 57.3 通过包裹类型读写数据 第58条 掌握unsafe包的安全使用模式 58.1 简洁的unsafe包 58.2 unsafe包的典型应用 58.3 正确理解unsafe.Pointer与uintptr 58.4 unsafe.Pointer的安全使用模式 第59条 谨慎使用reflect包提供的反射能力 59.1 Go反射的三大法则 59.2 反射世界的入口 59.3 反射世界的出口 59.4 输出参数、interface{}类型变量及反射对象的可设置性 第60条 了解cgo的原理和使用开销 60.1 Go调用C代码的原理 60.2 在Go中使用C语言的类型 60.3 在Go中链接外部C库 60.4 在C中使用Go函数 60.5 使用cgo的开销 60.6 使用cgo代码的静态构建
Page
5
第十部分 工具链与工程实践 第61条 使用module管理包依赖 61.1 Go语言包管理演进回顾 61.2 Go module:Go包依赖管理的生产标准 61.3 Go module代理 61.4 升级module的主版本号 第62条 构建最小Go程序容器镜像 62.1 镜像:继承中的创新 62.2 镜像是个筐:初学者的认知 62.3 理性回归:builder模式的崛起 62.4 “像赛车那样减重”:追求最小镜像 62.5 “要有光”:对多阶段构建的支持 第63条 自定义Go包的导入路径 63.1 govanityurls 63.2 使用govanityurls 第64条 熟练掌握Go常用工具 64.1 获取与安装 64.2 包或module检视 64.3 构建 64.4 运行与诊断 64.5 格式化与静态代码检查 64.6 重构 64.7 查看文档 64.8 代码导航与洞察 第65条 使用go generate驱动代码生成 65.1 go generate:Go原生的代码生成“驱动器” 65.2 go generate的工作原理 65.3 go generate的应用场景 第66条 牢记Go的常见“陷阱” 66.1 语法规范类 66.2 标准库类
Page
6
作者简介 白明(Tony Bai) 资深Go技术专家和架构师,有超过10年的服务端架构设计和开发 经验,擅长服务器后端编程。曾任东软集团开发部技术总监和东网科技 高级架构师,现为东软睿驰智能网联汽车业务线车联网平台的高级架构 师。精通Go、C、Python、Shell、Linux、Rancher、Docker、 OpenStack、Kubernetes等技术栈。 《七周七语言》译者之一,GopherChina大会、开源中国源创会讲 师,麦思博客座培训师,tonybai.com技术博客博主。
Page
7
推荐语 2017年我曾邀请白明到GopherChina分享过如何用Go风格来写Go代 码,当时就很期待更多Go风格方面的技术干货。这次白老师给我们带 来了这本书,它系统讲解了如何运用Go思维、Go风格、Go最佳实践来 写好代码,鞭辟入里。 ——谢孟军 积梦智能CEO/GoCN社区和GopherChina发起人 这本书内容全面、翔实,涵盖Go基本语法、Go运行时底层实现、 常用标准库和第三方库的使用、代码设计模式,以及官方工具链和常见 第三方工具的使用。因此,本书既可以用来巩固Go知识,也可以用来 开拓技术视野。 ——老貘 《Go语言101》作者 Go语言是一门入门相对简单的编程语言,我的很多使用其他编程 语言的朋友,一天时间就可以学完Go语言的基础知识,但是当他们想 进一步学习Go语言时,却又感到茫然,无从下手。目前市面上有一些 Go入门的书,也有一些专门讲Go高级专题的书,但是鲜有帮助Go入门 者提高的书,白明老师的这本书正好填补了这一空白。本书从思维和实 践两个层面讲解如何写出地道的Go风格的代码,值得想进一步提高Go 编程水平的朋友拥有。 ——晁岳攀(鸟窝) 微服务框架rpcx的作者/资深工程师 白明是国内Go语言圈子里的资深开发者,这本书脱胎于他丰富的 编程经验,其中一条条的总结饱含着他对Go语言的热爱。对于广大的 Go语言爱好者来说,通过这本书充分吸取一位高手的经验实在是一个 难得的机会。相信本书能够帮助那些热爱Go语言编程的朋友们更上一 层楼。 ——郝林 《Go语言核心36讲》和《Go并发编程实战》作者 本书有两大特色:第一,通过条目的方式覆盖Go语言的方方面 面;第二,旨在道出如何写出符合Go思维和语言惯例的高质量代码。
Page
8
独特的组织方式让你在掌握Go语法的同时,更能写出Go风格的代码。 白明老师是早期的Go语言布道者,写的博文通俗易懂,近些年还一直 更新Gopher Daily,让大家第一时间掌握Go的动态。这本不一样的Go 书,值得你拥有。 ——徐新华(polarisxu) Go语言中文网站长/《Go语言编程之 旅》作者
Page
9
推荐序 既然你翻开了这本关于Go语言的书,那就说明你对Go语言有兴 趣,打算学习一下Go语言。不过,在你继续翻阅之前,我先问你一个 问题:“你觉得Go语言简单吗?” 简单?不简单? 对于这个问题,有的人会说Go语言很简单,一个晚上就能学完所 有语法,也有人会说Go语言不简单,要做到运用自如需要大量磨炼。 为什么会有两个截然不同的答案?因为他们回答的其实并不是同一 个问题。真正的问题是: Go语言入门简单吗?简单! Go语言精通简单吗?不简单! 从入门到精通的路有多远呢?大概相当于从学会说话到写文章发表 的距离吧!能说话的人有的是,一般一岁的小朋友就可以做到。但能写 文章发表的人有多少呢?即便是在自媒体如此发达的今天,能够利用文 字表达自己的也只有少数人,大多数人只能作为信息的接收者。 同样,入门的程序员数量庞大,但真正精通的程序员却为数不多。 一个有追求的程序员绝不满足于会写Hello World,他会望向那座叫 作“精通”的高峰,但怎样攀上高峰是摆在很多人面前的现实问题。 要精通一门语言,最好的方式是跟着一个已经精通这门语言的人系 统学习。然而,并不是每个人都有这样的机会。不是每个人身边都有一 位精通这门语言的高手,即便有,高手也不见得有意愿、有能力把自己 的知识体系整理出来,倾囊相授。好在这个世界上还有很多高手乐于分 享,把知识系统地整理了出来,我们才有机会向他们学习。 从入门到进阶
Page
10
入门的书与进阶的书有哪些不同呢?入门的书一般讲的是语言本 身,按照一个合适的顺序介绍语法规范中的各种细节,最多再增加一些 标准库的使用方式就够了。各种语言的差异无非是语法和标准库数量的 多寡,基本结构大体类似。 对于编程新手而言,难的并不是掌握这些语法和标准库,而是建立 一种思维方式。你只要学会了任何一门程序设计语言,通过了建立思维 方式的关卡,再去学习一门新的程序设计语言,就只需要学习具体的语 法和一些这门语言特有的知识。所以,程序员通常是“一专多能”的,除 了自己最拿手的那门语言,还会使用很多门其他的语言,而更厉害的家 伙甚至是“多专多能”的。 那一本进阶的书能告诉我们什么呢?它会告诉我们一个生态。如果 说入门书是在练习场上模拟,那么进阶的书就是在真实战场上拼杀。在 真实世界中编写代码解决的不再是一个个简单的问题,而是随着需求不 断膨胀的复杂问题。我们编写的不是“写过即弃”的代码,所以必须面对 真实的问题,比如如何做设计,如何组织代码,如何管理第三方的程序 库,等等。这些在很多人眼中琐碎的问题其实是我们每日都要面对的问 题,很多技术团队正是因为没有遵循这些方面的最佳实践而陷入了无尽 的深渊。而这些内容显然超出了语言本身的范畴,属于生态的范围。 进阶的书很重要,然而,写好进阶的书却不是一件容易的事。一个 初出茅庐的程序员就可以写入门书,而只有经验丰富的程序员才能写出 进阶的书。这种经验不仅在于写了很多年代码,还在于能够向行业动态 看齐。只有这样,写出来的才不是个人偏见,而是行业共识。 如果你是一名Go程序员,而且不满足于在入门水平徘徊,那么这 就是为你准备的一本进阶书。 一个高手,一本进阶书 本书的作者白明是一位有超过十年系统编程经验的资深程序员。这 里说的程序员指的是那些真正热爱编程,把编程当作一门手艺不断打磨 的人。虽然他有着诸如架构师之类的头衔,但骨子里他依然是个不断精 进的程序员。
Page
11
他刚刚开始工作时我们就相识了,那时他就是一个热爱编程的人, 时隔多年依然如故。他刚开始用C语言写通信网关这种有着各种严苛要 求的软件,但一直在寻找更好的工具。Go的出现让他眼前一亮:一方 面,Go与C一脉相承,有着共同的创造者;另一方面,Go引入了一些更 加现代的特性,让它更适合大规模开发。于是,白明把自己更多的时间 献给了Go。一晃十多年过去,Go由他最初的个人爱好变成了他日常工 作中使用的语言。 除了在工作中使用Go,白明还是一位非常积极的分享者,经常在 Go社区分享内容。他不仅主导了一个叫Gopher部落的技术社群,还坚 持把自己收集到的资料整理成Gopher日报,此外,也在GopherChina这 样的技术大会上做过主题演讲。目前,中文社区内最好的Go语言入门 教程《Tony Bai·Go语言第一课》就出自白明之手。 如果由一个既有丰富实战经验又有丰富分享经验的人来写一本Go 语言的进阶书,这会是Go社区的幸运,而你手上的就是这样一本书。 这本书完全符合我对一本进阶书的定义,在这里你会看到Go语言 的生态,你会了解到关于写好Go项目的种种知识。如果说这本书有什 么缺点,那就是它太厚了,不过,这恰恰是白明经验丰富的体现。没办 法,真实世界就是这么复杂。 如果你能坚持把这本书读完,把其中的知识内化为自己的行动,你 的Go项目开发之路将由荆棘密布变成一片坦途,对路上的种种你都会 有似曾相识的感觉。 祝你阅读愉快,开发愉快! 郑晔 《10×程序员工作法》专栏作者/前火币网首席架构师/前Thoughtworks 首席咨询师
Page
12
前言 为什么要写本书 Go是Google三位大师级人物Robert Griesemer、Rob Pike及Ken Thompson共同设计的一种静态类型、编译型编程语言。它于2009年11 月正式开源,一经面世就凭借语法简单、原生支持并发、标准库强大、 工具链丰富等优点吸引了大量开发者。经过十余年演进和发展,Go如 今已成为主流云原生编程语言,很多云原生时代的杀手级平台、中间 件、协议和应用都是采用Go语言开发的,比如Docker、Kubernetes、以 太坊、Hyperledger Fabric超级账本、新一代互联网基础设施协议IPFS 等。 Go是一门特别容易入门的编程语言,无论是刚出校门的新手还是 从其他编程语言转过来的老手,都可以在短时间内快速掌握Go语法并 编写Go代码。但很多Go初学者的疑问是:Go入门容易,但精进难,怎 么才能像Go开发团队那样写出符合Go思维和语言惯例的高质量代码呢? 这个问题引发了我的思考。在2017年GopherChina大会上,我以演讲的 形式初次尝试回答这个问题,但鉴于演讲的时长有限,很多内容没能展 开,效果不甚理想。而本书正是我对解答这个问题所做出的第二次尝 试。 我这次解答的思路有两个。 思维层面:写出高质量Go代码的前提是思维方式的进阶,即用 Go语言的思维写Go代码。 实践技巧层面:Go标准库和优秀Go开源库是挖掘符合Go惯用法 的高质量Go代码的宝库,对其进行阅读、整理和归纳,可以得 到一些能够帮助我们快速进阶的有效实践。 本书正是基于以上思路为想实现Go精进但又不知从何入手的你而 写的。 首届图灵奖得主、著名计算机科学家Alan J. Perlis曾说过:“不能影
Page
13
响到你的编程思维方式的编程语言不值得学习和使用。”由此可见编程 思维对编程语言学习和应用的重要性。只有真正领悟了一门编程语言的 设计哲学和编程思维,并将其应用到日常编程当中,你才算真正精通了 这门编程语言。 因此,本书将首先带领大家回顾Go语言的演进历程,一起了解Go 语言设计者在设计Go语言时的所思所想,与他们产生思维上的共鸣, 深刻体会那些看似随意实则经过深思熟虑的设计。 接下来,本书将基于对Go开发团队、Go社区高质量代码的分析与 归纳,从项目结构和代码风格、基础语法、函数、方法、接口、并发、 错误处理、测试与性能优化、标准库、工具链等多个方面,给出改善 Go代码质量、写出符合Go思维和惯例的代码的箴言。 学习了本书中的这些箴言,你将拥有和Go专家一样的Go编程思 维,写出符合Go惯例风格的高质量Go代码,从众多Go初学者中脱颖而 出,快速实现从Go编程新手到专家的转变! 读者对象 本书主要适合以下人员阅读: 迫切希望在Go语言上精进并上升到新层次的Go语言初学者; 希望写出更符合Go惯用法的高质量代码的Go语言开发者; 有Go语言面试需求的在校生或Go语言求职者; 已掌握其他编程语言且希望深入学习Go语言的开发者。 本书特色 本书的特色可以概括为以下几点。 进阶必备:精心总结的编程箴言助你掌握高效Go程序设计之 道。 高屋建瓴:Go设计哲学与编程思想先行。 深入浅出:原理深入,例子简明,讲解透彻。
Page
14
图文并茂:大量图表辅助学习,重点、难点轻松掌控。 如何阅读本书 本书内容共分为十部分,限于篇幅,分为两册出版,即《Go语言 精进之路:从新手到高手的编程思想、方法和技巧1》和《Go语言精进 之路:从新手到高手的编程思想、方法和技巧2》。其中,第1册包含第 一~七部分,第2册包含第八~十部分。 第一部分 熟知Go语言的一切 本部分将带领读者穿越时空,回顾历史,详细了解Go语言的诞 生、演进以及发展现状。通过归纳总结Go语言的设计哲学和原生编程 思维,让读者站在语言设计者的高度理解Go语言与众不同的设计,认 同Go语言的设计理念。 第二部分 项目结构、代码风格与标识符命名 每种编程语言都有自己惯用的代码风格,而遵循语言惯用风格是编 写高质量Go代码的必要条件。本部分详细介绍了得到公认且广泛使用 的Go项目的结构布局、代码风格标准、标识符命名惯例等。 第三部分 声明、类型、语句与控制结构 本部分详述基础语法层面高质量Go代码的惯用法和有效实践,涵 盖无类型常量的作用、定义Go的枚举常量、零值可用类型的意义、切 片原理以及高效的原因、Go包导入路径的真正含义等。 第四部分 函数与方法 函数和方法是Go程序的基本组成单元。本部分聚焦于函数与方法 的设计和实现,涵盖init函数的使用、跻身“一等公民”行列的函数有何 不同、Go方法的本质等。 第五部分 接口 接口是Go语言中的“魔法师”。本部分聚焦于接口,涵盖接口的设计
Page
15
惯例、使用接口类型的注意事项以及接口类型对代码可测试性的影响 等。 第六部分 并发编程 Go以其轻量级的并发模型而闻名。本部分详细介绍Go基本执行单 元——goroutine的调度原理、Go并发模型以及常见并发模式、Go支持 并发的原生类型——channel的惯用模式等内容。 第七部分 错误处理 Go语言十分重视错误处理,它有着相对保守的设计和显式处理错 误的惯例。本部分涵盖Go错误处理的哲学以及在这套哲学下一些常见 错误处理问题的优秀实践。 第八部分 测试、性能剖析与调试 Go自带强大且为人所称道的工具链。本部分详细介绍Go在单元测 试、性能基准测试与性能剖析以及代码调试方面的最佳实践。 第九部分 标准库、反射与cgo Go拥有功能强大且质量上乘的标准库,在多数情况下仅使用标准 库即可实现应用的大部分功能,这大幅降低了学习成本以及代码依赖的 管理成本。本部分详细说明高频使用的标准库包(如net/http、strings、 bytes、time等)的正确使用方式,以及在使用reflect包、cgo时的注意事 项。 第十部分 工具链与工程实践 本部分涵盖在使用Go语言进行大型软件项目开发的过程中,我们 很有可能会遇到的一些工程问题的解决方法,包括使用go module进行 Go包依赖管理、Go程序容器镜像、Go相关工具使用以及Go语言的 避“坑”指南。 勘误和支持
Page
16
由于作者水平有限,写作时间仓促,以及技术的不断更新和迭代, 书中难免会存在一些错误或者不准确的地方,恳请读者批评指正。书中 的源文件可以从 https://github.com/bigwhite/GoProgrammingFromBeginnerToMaster下载。 如果你有更多的宝贵意见,欢迎发送邮件至邮箱 bigwhite.cn@aliyun.com,期待你的真挚反馈。 致谢 感谢机械工业出版社华章公司的编辑杨福川与罗词亮,在这一年多 的时间里,他们的支持与鼓励让我顺利完成全部书稿。 谨以此书献给Go语言社区的关注者和建设者! 白明 2021年12月
Page
17
第八部分 测试、性能剖析与调试 Go语言推崇“面向工程”的设计哲学并自带强大且为人所称道的工具 链,本部分将详细介绍Go在单元测试、性能测试以及代码调试方面的 最佳实践方案。
Page
18
第40条 理解包内测试与包外测试的差别 Go语言在工具链和标准库中提供对测试的原生支持,这算是Go语 言在工程实践方面的一个创新,也是Go相较于其他主流语言的一个突 出亮点。 在Go中我们针对包编写测试代码。测试代码与包代码放在同一个 包目录下,并且Go要求所有测试代码都存放在以*_test.go结尾的文件 中。这使Go开发人员一眼就能分辨出哪些文件存放的是包代码,哪些 文件存放的是针对该包的测试代码。 go test命令也是通过同样的方式将包代码和包测试代码区分开的。 go test将所有包目录下的*_test.go文件编译成一个临时二进制文件(可 以通过go test -c显式编译出该文件),并执行该文件,后者将执行各个 测试源文件中名字格式为TestXxx的函数所代表的测试用例并输出测试 执行结果。
Page
19
40.1 官方文档的“自相矛盾” Go原生支持测试的两大要素——go test命令和testing包,它们是 Gopher学习Go代码测试的必经之路。 下面是关于testing包的一段官方文档(Go 1.14版本)摘录: 要编写一个新的测试集(test suite),创建一个包含 TestXxx函数的以_test.go为文件名结尾的文件。将这个测试文 件放在与被测试包相同的包下面。编译被测试包时,该文件将被 排除在外;执行go test时,该文件将被包含在内。 同样是官方文档,在介绍go test命令行工具时,Go文档则如是说: 那些包名中带有_test后缀的测试文件将被编译成一个独立 的包,这个包之后会被链接到主测试二进制文件中并运行。 对比这两段官方文档,我们发现了一处“自相矛盾”[1]的地方: testing包文档告诉我们将测试代码放入与被测试包同名的包中,而go test命令行帮助文档则提到会将包名中带有_test后缀的测试文件编译成 一个独立的包。 我们用一个例子来直观说明一下这个“矛盾”:如果我们要测试的包 为foo,testing包的帮助文档告诉我们把对foo包的测试代码放在包名为 foo的测试文件中;而go test命令行帮助文档则告诉我们把foo包的测试 代码放在包名为foo_test的测试文件中。 我们把将测试代码放在与被测包同名的包中的测试方法称为“包内 测试”。可以通过下面的命令查看哪些测试源文件使用了包内测试: $go list -f={{.TestGoFiles}} . 我们把将测试代码放在名为被测包包名+"_test"的包中的测试方法 称为“包外测试”。可以通过下面的命令查看哪些测试源文件使用了包 外测试:
Page
20
$go list -f={{.XTestGoFiles}} . 那么我们究竟是选择包内测试还是包外测试呢?在给出结论之前, 我们将分别对这两种方法做一个详细分析。 [1]https://github.com/golang/go/issues/25223
The above is a preview of the first 20 pages. Register to read the complete e-book.
Comments 0
Loading comments...
Reply to Comment
Edit Comment