写在前头,菜鸟作者的一些碎碎念:
回想自己2022年研三第一次去实习的时候,是到了一个数据库小组,是一个做数据库内核的小组,leader分配的目标是先把read/write流程搞明白。第一次出校实习,一来就是直接读内核源码,而自己对数据库只有书本上的知识,可以说是对内核实现完全不了解,一片空白,所以当时真的是焦虑的不行,生怕自己无法完成任务,再加上马上就要毕业了工作无着落(虽然后面延毕了),又是第一次去公司实习,所以真的慌得要死,好在leader特别特别特别特别特别好,让我慢慢来,给了我充分的时间来,读了1个月,懵懵懂懂。后来岳麓区疫情爆发,12月初答辩完拿到offer就回学校了,2月的短暂实习就结束了,源码阅读就暂告一个段落了,后来23年6月后来公司,原先数据库小组打散了,leader带着我来到了云原生这个新部门(留在原小组挂靠到其他部门下面的两个应届生后面都被裁了,真的感叹自己超级幸运),是搞k8s,最开始接的任务是改造一个项目的组件自动部署的任务,刚来新部门,啥也不懂,很慌,对k8s只是以前简单看过两本书,但是都忘光了,也就是说对k8s也是一片空白,还好组里的人特好,各种问,然后各种答疑解惑(蛮幸运的)。因为对k8s不太了解,初期只能靠视频以及资料学习,但是看视频和看资料总是不够,还是不知道怎么写代码,另外,除了k8s之外,要维护修改组件部署项目就必须先了解这个项目的源码,所以开头一个月是一边看视频学k8s一边读项目源码一边尝试开发,逐步摸索前进。大概到了十月份,安排了新的任务:组件部署只是实现了自动化这个功能,但是各种中间件的单机部署、集群部署yaml还是需要自己准备,所以新任务就是完成这些yaml,如redis/pulsar/rocketmq等十多中,10月到12月左右都是一边百度组件原理,一边写yaml,一边测试,这个时期是对各种中间件原理的简单了解,这个时期也暗暗埋下了好奇的种子,等到这个搞完,又安排了一个调度器开发的任务,也是对调度器啥也不了解,也是一边看网上资料,一边debug源码,一边编写调度器插件。差不多23年8月~24年2月都是一边看资料学习k8s一边看各种源码,一边开发,差不多对k8s有了初步了解。从出学校到初入职场,这一段时间真的超级重要,这段时间内,组里给我的感觉就是:这个小组的氛围真的特别特别好,学习氛围也特别特别浓厚,反正遇到什么的不懂的有什么不会开发的,看完资料直接看源码吧,正是这种浓厚的氛围为后来培养自己对源码阅读的习惯打下了基础(自己读源码还有一个十分重要的原因就是:第一次实习,阅读数据库源码,成功大致缕清崩溃恢复、2pc流程的时候,那种云开雾散、豁然开朗的感觉真的超爽,自己对这个不了解,看视频只知道大概,看完源码后突然就理解视频里说的是什么意思,很爽)。新的一年,砍掉了许多开发任务,由于人事变动,我也分到了leader(这个leader和前一个leader一样,真的超级好,连续两次遇到两个非常棒的leader真的超级幸运,一个好leader有时候真的是可遇不可求)手下的另一个小组出口网关和消息平台小组,负责出口网关,出口网关比较成熟,所以比去年要空闲一点,也改了加班政策,可以调休更多,所以自觉加班比以前多了一点,一般加班的时候,一半时间摸鱼,一半时间阅读源码和做笔记,从大概24年3月份到今天(2024/11/03),陆陆续续读了:pulsar、etcd、netty、redis、nginx以及公司的出口网关、消息平台等框架的部分源代码,真的受益颇深,如前面所述,支撑我阅读源码的重要动力之一就是:我对这个不了解,我想要了解(自驱力特别重要因为特别害怕被裁(现在工作真不好找),当然,我也是半吊子水平)。从最开始编译数据库库内核,成功跑起来都要个把星期到如今差不多一周就能大致了解一个框架的read/write等主要流程源码(包括搜集资料1到2天,编译环境1到2天(基本半天搞完,pulsar这种复杂的搭建集群折腾了很久),一个流程差不多1到2天就可以大致明白怎么实现的),虽然还是很菜(有时,真的,自己同龄人焦虑真的好严重,当初的同学,我还在入门,他们都去bat等一线大厂工作好几年了,会有深深的打击感和无力感,不过人各有命,顺其自然吧),但是自己也能感觉到自己和初入职场时是有进步的,职场中(我是在基础平台)阅读源码十分常见,所以我想把我这一年来阅读源码的心得总结下来,分享给大家,这仅仅是一家之言,仅仅是菜鸟之言,当然网上类似的文章一大把,内容和我的也差不多,但不管怎样,我的这些心得都是我这一年自己总结的,有些东西看别人写的,似懂非懂,但是等到自己真的实操一遍,才能真的深刻理解。所以,一起加油,各位。
如何快速理解一个框架源码:
1:了解框架及其使用。我一般去b站找该框架的使用教程和百度,过一遍就行,不用都学会,比如视频我都是2倍速和不断快进。阅读源码之前一定要了解他是怎么用的。一般通过使用,就能对他怎么实现的有一点点自己的猜测。
2:搭建debug环境,以便可以debug源码。工欲善其事必先利其器,我都是用jetbrains全家桶,比如java用idea、c++用clion、go用goland、python用pycharm。(实习以及参加工作,一年半里,go、java、python、c++都用过,真的感觉语言只是工具,重要的是不是能完成任务。。。)。!!!一定要搭建调试环境,因为如果要阅读源码,就必定要debug,否则效率很低而且有些流程会理解出错。当然,搭建环境这一步有时候就把人劝退了。。。。
3:再次上b站找视频和百度找源码资料,以便了解大致实现。这次主要是找源码解析视频和解析文章。在自己尝试阅读之前就要大致了解源码是怎么实现的,这样自己读源码的时候才能事半功倍,才能读的更顺。
4:确定切入点。千万不要从main函数开始看起,如果没有切入点,而是直接从main开始,那么你读源码就是浪费时间,因为你本来就对源码不了解,没有切入点也就是没有带着疑问带着目的去阅读,那么你这样收益是极低。所以一定要找到一个切入点。个人建议一般是:write/read/持久化(写日志)/事务,个人建议第一个切入点一般最好是write,比如etcd就是put命令的实现流程、redis就是set命令的实现,因为写命令会涉及到命令的执行、主从同步、日志记录等各方面,也就是一旦弄明白write流程,那么整个框架差不多理解了,像nginx这种就是read流程。因为各个流程都是息息相关的,所以一旦读懂了一个流程,其他的流程也就会轻松很多,并且先读完流程a,然后读流程b,读b的时候往往又会灵光一下,原来流程a里的某处操作是为了流程b啊,这样前后映照,互相补充,效率就高了
5:打断点。确定切入点之后就是打断点了。怎么找断点?我的心得如下:比如我要etcd write流程,那么我就上网百度etcd write源码流程、etcd write源码入口等,然后复制文章里的函数名字,然后去ide里ctrl+shif+f全局搜索这个函数,然后找到了就打一个断点,一般如此一下就在该流程的主线路径上打下两三个断点,注意,不要求一定是入口点,只要是主线流程上的点就行,因为借助ide我们可以快速找到主线。打下断点后我们就是用工具或者直接自己写代码来触发源码的这段流程,比如ectd write源码流程中我打下了断点,然后我就用etcd-cli来发送put命令,如果源码流程执行到了我们的断点处,那么这一步就大功告成,因为只要找到一个断点,那么借助ide,我们就能一眼找到调用栈,通过单步调试,我们也能一步一步深究下去。总结下来就是在某条主线上打下两三个钉子,然后我们就能一下拎起整条主线
6:就是debug源码然后做笔记了。一定要做笔记,只看不做是不行的。同时我并不是一调试就直接做笔记,而是读三遍。第一步先大致捋一遍该流程主线,这个阶段主要是快速浏览主线经过的函数,抓住主线,也就是靠猜,靠函数名来猜主线会往哪里走,然后打断点;第二遍则是从流程起点,再一步一步的看函数,比之前要详细一点,这一阶段主要是大致了解各个函数的流程,以便能够从中拎出哪些是主线代码,哪些是无关流程(比如错误处理、条件检测等);第三遍就是一边debug,一边记录笔记了,比前两个阶段要详细的多,而且记笔记的时候往往记到某个点,突然就会发现他是和前面某个点互相呼应的,所以一定要记笔记。当然,不是每个细节都要弄懂,只要明白这段代码是干什么的,大致是完成什么功能,这个函数也是大致完成什么功能,就行了,不用去深究数据结构,因为目前我读源码主要是弄明白代码流程而不是数据结构,如果死扣细节和数据结构,很容易就被绕晕,半天不得其解,此时很容易就气馁,所以不要扣细节,只要明白大致是什么意思就行了。不懂得地方一定要学会百度,比如这个函数不明白什么意思,直接百度,因为源码阅读这件事很多人都做过笔记的,不要从零开始,要站在前人的肩膀上。
补充:阅读源码的时候一定要学会融会贯通,就是说要会思考,会总结,会一边猜测,然后一边读源码验证。比如我最开始读的是starrocks数据库的源码,然后就对一致性和持久化有一定了解,然后读redis的时候就会想redis肯定会持久化日志,但是到底是在哪里持久化日志的,按照我读starrocks的经验,应该是先写日志,再执行,然后在返回响应,但是实际却是先执行,后写日志,虽然和猜测的不符,但是在带着疑问验证猜测的过程中,我们就可以很好地缕清主线。
补充:!!!!一定要会用chatgpt、阿里通义千问等ai助手,有时候一段函数我们不太懂,直接复制整个函数然后丢给ai助手,让他解释代码,虽然不一定全对,但ai助手能回答个七七八八,我们从ai助手的回答里就能大致知道这个函数大致怎么个流程,有助于我们猜测主线和抓住主线。再次强调,阅读源码一定要用ai助手,不要怀疑ai助手对于阅读源码的能力。
最后的碎碎念:经常有时候有个地方看了很久看不明白,然后上网搜,然后发现别人怎么讲的那么详细,然后觉得别人好厉害,自己好渣,然后被打击,然后心情低落。。。。这是我自己的亲身经历,我的感悟是:没关系,该打击还是会打击的,因为不管怎么样,时间是自己的,成长是自己的,即使垂头丧气,时间也会过的,所以与其垂头丧气,不如收拾好心态,重拾信心,然后再慢慢看,再次强调:因为时间,是自己的,成长,是自己的,所以即使再灰心再沮丧,等心情恢复,又继续慢慢看吧
下面以debug redis为例:
1:学习redis使用,视频和百度。随便找两个教程过一遍就行了,注意,最好是整套整套的教程,不要两三分钟一个知识点片段的那种。
2:搭建debug环境,直到我们可以用工具正常访问,我们这里用的是redisinsight
3:找源码教程或者源码文章
4:打断点,我们一般第一个流程选择write流程,然后百度源码流程,找到对应函数,然后直接ctrl+shift+f在源码中查找,然后打下断点,然后用工具触发这个流程。
4.1:百度,然后找到断点,比如redis的set命令就是一个write流程,我们百度得知set流程会经过setCommand。
4.2:在源码里面ctrl+shif+f查找字符串 setCommand(,然后在函数代码中打下断点,这样我们就抓住了主线上的一个点
4.3:工具触发流程,然后借助ide,查看调用栈,我们一下就可以抓住整个主线流程了。主线程流程正好从main开始,所以说不要一开始就从main函数开始平推,而是要找准某个切入点,抓住某条主线,然后往前往后debug。备注:看什么流程就重点关注什么流程,与当前流程无关的,统统跳过
4.4:借助ai助手,大致了解函数流程,要与时俱进,不要怀疑ai助手的强大。
5:debug和记笔记了。记笔记我都是记录整个流程,然后用缩进代表调用关系,一般用两个空格,即a调用b,假设a缩进2个空格,那么b就是缩进四个空格,然后是每行只记录一个函数调用,然后在右侧做笔记,介绍函数用途,以及自己的一些心得。c,我一般用文件名.方法名记录方法,go/c++/java,一般用类名.方法名来记录一个函数
filea.func_a #这里就表示func_a
classb.func_b #这里表示func_a调用了func_b,然后func_b做了什么事xxxx
classc.func_c #然后这里表示func_b调用了func_a
至此,源码阅读的心得总结完毕