一、千呼万唤始出来
亲爱的各位读者,我的新书《C++ 服务器开发精髓》终于终于终于与大家见面了,图书如下:
图书的封面设计很精美,当然内容一定不负众望。因出版社老师要求提供一张照片放到封面上,今年的 6 月 1 日儿童节去拍了一张,照片拍出来放在下面大家自己看吧。人到中年,生活不易,没有少年时代那种懵懂与青涩,多了一份厚实与坚定了。一些想做的事情却一步步把它做成了,成为现实。昔日戏言身后事,明朝都到眼前来。所以,梦想还是要有的,还是要坚持的,即使这世界有那么多不完善,生活有那么多不如意,因为万一实现了呢?
二、写作背后的故事
关于这本书,我写了三年,故事太长了,容我细细道来。
2018 年的时候,我去携程旅行网工作,那个时候携程正在招一个具有 C/C++ 背景的 Java 资深开发,而我正好符合这样的要求。那会儿,我已经玩了许多年的知乎,在知乎上也回答了一些 C/C++ 与 Linux 后台开发相关的问题,有些回答竟然达到了几千赞和收藏,有一些出版社编辑老师在知乎上找到我,问我有没有写一本关于 C++ 或者服务器方面的图书,我考虑时机不成熟,就都一一婉拒了。
后来我抱着玩儿的心态尝试在公众号发表文章,没想到一下子打开了一个充满奇幻的未知大门。在公众号互推的过程中结识了艳鹏,彼时他已经出版了一本口碑很好的分布式服务相关的图书,另外一本书也在写作中。艳鹏告诉我,和他对接的出版社编辑老师非常靠谱,可以推荐给我。于是,我就接受了他的推荐,没想到没过多久,出版社老师就发来约稿合同,我那个时候也没想到会这么快,就“糊里糊涂”地签了合同。
人嘛,有时候不逼自己一把,你永远不知道自己能成事。既然签了合同,那就得认真对待,而且合同上也有交稿日期,虽然出版社老师说,延期一般提前沟通问题也不大,就算最终作者没写出来,出版社一般也不会追究责任和赔偿。
既然答应下来,书还是要好好写的,我写书分为几个阶段,第一个阶段是列书的大纲目录和章节,同时列举每一节要写的内容;第二阶段是实际的写作阶段,这个阶段时间花的最长;第三阶段是润色与优化阶段,我把书的内容发给了一些相关同行和读者,请他们提出一些修改建议和意见,同时,补充了一些后来工作和学习中一些新的想法与有用的技巧。2019 年年初又应朋友邀请一起创业,是早期团队三个主要开发之一。工作就更忙了,加上那个时候也是单休,只能利用早早晚晚或者节假日的时间去写作。
就这样断断续续地写了两年,期间有些章节反复的修改。大家都知道的,在如今讲究速成的年代,出一本关于 C/C++ 图书是很需要勇气的,企业要求快速化生产与 学习周期长、学习难度大的 C/C++ 技术栈之间的矛盾让很多同学望而却步,一些新人在尝试之后学不得法或者被浮躁的网文的宣传而最终放弃。
C/C++ 技术栈一旦学成,奇妙无穷。那么,C/C++ 技术栈到底该如何学好呢?笔者虽然经验有限,但仍恬以剖析之。
按照技术掌握的深度,一般分为两个层级,第一个层级是开发熟练工,第二个阶段是融汇贯通阶段。
三、无穷岁月增中减,有味诗书苦后甜
3.1 开发熟练工阶段
C/C++ 这门语言与其他高级语言不同,它是离操作系统较近的语言。所以学好 C/C++ 体系的技术栈必须结合操作系统的运行机制来学习。展开来说,就是你必须掌握操作系统层面的几大基础知识,他们是汇编、编译链接与运行时体系、狭义的操作系统原理、多线程、网络编程。
第一个基础知识是汇编,我们学习汇编不是一定要用汇编来写代码,就像我们学习 C/C++ 也不一定单纯为了面试和找工作。
对于 C/C++ 的同学来说,汇编是建议一定要掌握的,只有这样,你才能在书写 C++ 代码的时候,清楚地知道你的每一行C++代码背后对应着什么样的机器指令,if/for/while 等基本程序结构如何实现的,函数的返回值如何返回的,为什么整型变量的数学运算不是原子的,最终你知道如何书写代码才能做到效率最高。掌握了汇编,你可以明白,在 C++ 中,一个栈对象从构造到析构,其整个生命周期里,开发者的代码、编译器和操作系统分别做了什么。掌握了汇编,你可以理解函数调用是如何实现的,你可以理解函数的几种调用方法,为什么printf这样的函数其调用方式不能是 __stdcall,而必须是 __cdecl。掌握了汇编,你就能明白为什么一个类对象增加一个方法不会增加其实际占的内存空间。
第二个基础知识是编译、链接与运行时体系知识。作为一个开发者,要清楚地知道我们写的 C/C++ 程序是如何通过预处理、编译与链接等步骤最终变成可执行的二进制文件,操作系统如何识别一个文件为可执行文件,一个可执行文件包含什么内容,执行时如何加载到进程的地址空间,程序的每一个变量和数据位于进程地址空间的什么位置,如何引用到。一个进程的地址空间有些什么内容,各段地址分布着什么内容,为什么读写空指针或者野指针会有内存问题。一个进程如何装在各个 so 或 dll 文件的,这些文件被加载到进程地址空间的什么位置,如何被执行,数据如何被交换。
第三个基础知识是狭义的操作系统原理。这里加上“狭义”二字是因为从广义上来讲,以上所说的内容都是操作系统原理的范畴。狭义的操作系统原理这里包括操作系统如何管理进程与线程,虚拟内存与物理内存之间的对应关系,何为内存映射文件,进程之间如何通信等等。
第四个基础知识是多线程知识****。严格来说,这点已经包括在第三点之中了,我之所以将其单独列出来,是因为多线程编程是我们做应用服务最常用的技术之一。最近面试过几个学历非常好的同学,对于一个进程中如果某个线程因为内存问题而退出,是否会导致整个进程退出的问题答不好,实在不应该。多线程知识其实不难学,立足于理解与实践而不是应付面试,可以学的很好。无论是 Windows 还是 Linux 操作系统,操作系统提供的线程同步对象就那么几种,Windows 常用的有临界区(关键端)、Event、互斥体、信号量等,Linux 有互斥体、信号量、读写锁、条件变量,这些知识点学过则会,不学则不会。这些线程同步原语花上几天就能搞得清楚,大多数同学不是学不会,而是不愿意学,但是偏偏喜欢在简历上写上自己熟悉多线程编程。面试的时候,被问到条件变量的虚假唤醒机制都说不清楚,非要说自己用过条件变量。这是一些同学犯的很低级的错误,如果真用过条件变量,如果不知道虚假唤醒机制,那一定写的代码是不对的。市场上目前没有任何一本图书对以上知识形成体系的介绍,当然,我的本书填补了这一空缺,你将从本书中获得从进程与线程的关系,再到常用的线程同步原语的区别与使用场景,再到线程池以及基于生产者消费者模型的消息队列,以及对协程思想介绍的相关知识。
掌握了常见的多线程同步原语之后,接下来可以找一些带多线程的项目去学习一下,不管是否带 UI 的都行。我推荐的一种方式是,使用 gdb 或者 Visual Studio 调试器将你需要学习的多线程程序中断下来,在多线程面板,看看这个进程一共有多少个正在运行的线程,分析每个线程的作用,然后研究下这些线程在何时何地创建的,为什么需要创建新的线程。尝试爱过几个人,面对爱情你会诚实很多;尝试研究几个多线程项目,面对多线程你会熟练许多。
第五个是网络编程,直白地说就是 Socket 编程。操作系统层面提供的 API 会在相当长的时间内保持接口不变,一旦学成,终生受用。理解和掌握常用的基础 socket API 不仅可以最大化地去定制各种网络通信框架,更不用说使用市面上流行的网络通信库了,最重要的是,它会是你排查各种网络疑难杂症坚实的技术保障。操作系统层面提供的网络模型就那么几种,无论像 Java/Go/Python 等语言如何封装,作为技术的源头,我们有什么理由不去掌握它呢?市面上关于网络编程的书很多,我在书中结合我这些年的工作经验总结了二十几个网络编程中的重点和难点,现在全部交给你。
以上是基于 C++ 技术栈来说,并没有包括算法与数据结构、数据库等方面的基本功,但是这些额外的也是应该需要掌握的。掌握了如上所说的,你就达到了一个熟练工阶段。
3.3 融汇贯通阶段
掌握了这些基础知识,接下来,技术更进一步,就无关具体的编程语言了,互联网方向的高级技术有如下:
高可用与容灾容错
服务都是人开发的,既然是人开发的,必然有宕机的可能性,当然宕机的原因可能是程序自身 bug,也可能是物理故障(断电、磁盘损坏等等),作为开发人员,针对不同的业务场景,我们没法做到服务 100% 可能,至少让其尽量可用,八仙过海各显神通。在宕机时如何尽量不影响业务、如何尽量快速恢复、如何保证数据和业务状态不丢失等等。这当然有一定的固定套路,例如主从、主备,当然固定的套路不是万能的,尤其是对于一些有状态要求的服务,这需要不断的磨练与自我总结。
分布式
分布式你需要掌握基本的分布式理论和原理,常见的分布式算法,然后是分布式系统设计的初衷和技巧,在实际并发量高的业务中,如何利用分布式解决高可用和访问效率问题。
RPC
很多人都听说过这个词,在面试时也可以说出来个大概,但是当问到 RPC 技术解决的核心问题是什么就说不清楚了。当然,学习 RPC,我们还要考虑协议的设计(协议格式、序列化与反序列化、兼容性问题)、网络连接的重试与反馈、接口 stub 的设计等等。
消息中间件
目前除了自己公司自研的消息中间件,主流的有 Kafka、RabbitMQ、RocketMQ,如果想学习,建议选择其中一种深入学习一下,要掌握消息中间件的用途、选举策略、保序策略、重试策略、高可用策略等。
缓存
缓存的设计是一个很大的方面,个人觉得与其说这是一种设计思想而非单纯的某个缓存服务。当然,老生常谈的有缓存雪崩、缓存穿透、缓存击穿的解决思路。当然,以缓存为代表的服务是 Redis,Redis 的常用数据类型、适用场景、持久化、主从复制、哨兵与集群,这些建议你掌握,如果你从来没机会吃猪肉,那就看看猪跑吧,一些技术书籍和项目案例都有 Redis 的用途说明。
数据库高级知识
包括 SQL 调优、数据库调优、分表分库、主从同步等等。
四、天下还有不会武功的百姓
融会贯通阶段远非仅仅需要掌握这么多知识。说了这么多,我能用一张图来表达一下我的见解吧:
《C++ 服务器开发精髓》这本书正是一本帮你成为 C/C++ 技术栈的熟练工的书。当然,这本书除了详细讲解了 C++11/14/17 新标准常用的方法以外,只是以 C/C++ 为编程语言来介绍相关的后端开发技术栈,这不是一本讲 C/C++ 语言本身的书,讲 C/C++ 语言的书太多了,多如过江之鲫。
后来,2020 年年初因为一些原因,我离开了创业团队,加入了另外一家独角兽公司。此时这本书的全部章节已经基本写完了,当我把他交给出版社老师的时候,我们产生了激烈的“交锋”,最后,这版书稿一共改了 8 个版本:
我总以为那是 Final 版本,但 Final 版本在改了八次、历时一年多,才最终定型,有的时候,我觉得出版社老师的要求有点“非难”,好在我们最终熬过来了,如今它呈现在你的面前。 全书初稿有大概一百七十万字,如果这样出版,实在太厚了,在拒绝了编辑老师说分为上下两册发行的建议后,最终当你看到的时候,它被浓缩成一百万字,它的厚度仍然比肩《高性能 MySQL》,然而页面比这本书更大。
时至今日,我工作中写 C/C++ 的机会并不多,但是这是我接触 C/C++ 开发十年来,从客户端到服务器,从 Windows 到 Linux 的经验总结,如果它能助你职业生涯一臂之力,那我这一千多个日夜的付出就没有白费。
有读者给我留言说:
认真的人无论在哪条路上走,总会碰到相似的情况。偶然看到你开源的博客,作为吃瓜群众过来看看开发者是怎样的人。叹息一声:中国人才真多。可为啥普通用户就是找不到好软件用呢?可能大侠们都醉心于武学去了,小侠们醉心于赚钱去了,忘记了天下还有不会武功的百姓。
一路走来,感谢出版社张老师的支持与不抛弃不放弃,感谢媳妇的支持与鼓励,感谢王旭东等同学为本书的勘误与校稿提供的帮助,感谢高性能服务器开发群的所有群友,感谢高性能服务器开发的所有读者。
最后,我希望你,也祝愿你掌握 C/C++ 服务器开发这门武功。
图书已经在京东开始预售,预售链接(拷贝到浏览器中打开):
https://item.jd.com/13312460.html
本文首发于:《在 2021 年,写一本 C++ 图书是一种什么体验?》