3

如何高效阅读源码?

 3 years ago
source link: https://my.oschina.net/u/4613350/blog/4877501
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

“我能熟练使用这个框架/软件/技术就行了, 为什么要看源码?”

“平时不用看源码, 看源码太费时间,还容易忘记,工作中出现问题再针对性地阅读,效率更高。”

“为了面试才需要看源码!”

。。。。。。

如果你也有类似的疑问,不妨接着往下看

1、为什么要阅读源码?


1.1 在通用型基础技术中提高技术能力

在 JAVA 领域中包含 JAVA 集合、Java并发(JUC)等, 它们是项目中使用的高频技术,在各种复杂的场景中选用合适的数据结构、线程并发模型,合理控制锁粒度等都能显著提高应用程序的可用性、健壮性,非常容易凸显出自己的技术实力,更容易受到领导的认可,助力职场。

当然通过阅读源码并不是知晓原理的唯一方法,但作为一个名程序员、直面代码,亲自感受代码的魅力或许会显得的更加直接。

1.2 在重点领域打造自己的亮点

我所在公司使用了 Dubbo、RocketMQ,我也有幸参与到这些技术栈的运用与运维,积累了丰富的使用经验,为了突出在这两个领域的优势,我详细阅读了它们的源码,在CSDN和公众号等知识分享平台发布了大量的技术文章,成体系的剖析其实现原理、架构设计理念,理论与实战相结合,让我成为在Dubbo、RocketMQ领域当仁不让的技术专家,团队中的核心骨干。

同时由于文章是成体系的,被出版社相中,邀请出书,《RocketMQ技术内幕》一书应运而生,从而成为我职业技能列表中非常亮眼的名片,形成公认的技术影响力,具备一定的“品牌溢价”能力。

当然, 也可以等到出了问题再看源码,“投入产出比”更高,但这是个被动过程,如果生产环境由于并发不高,那可能一年、两年你都遇不到真正的问题,经验积累非常慢, 工作了4、5年,有可能还比不过工作2、3年的人。

所以如果是你想快速打造亮点的话,还是需要主动阅读源码,成体系掌握其设计理念、实现原理。

1.3 从优秀的源码中学习设计和编码

学习编程的过程其实就是一个模仿的过程, 优秀的源码都是大师级作品,极具营养,可以看到大师们是如何抽象接口的,如何应用SOLID原则的,还有很多非常有用的编程技巧。

例如JUnit是从模式开始构建系统, 其中你可以看到大量的设计模式的应用,这些都是活生生的案例,比干巴巴地看那些设计模式理论,或者简单的例子不知道好到哪里去了。

2、如何阅读源码

我根据多年的阅读经验,整理了这么一套方法:

  1. 了解这款软件的使用场景、以及架构设计中将承担的责任。
  2. 寻找官方文档,从整体上把握这款软件的设计理念。
  3. 搭建自己的开发调试环境,运行官方提供Demo示例,为后续深入研究打下基础。
  4. 先主干流程再分支流程,注意切割,逐个击破。

接下来分享一下我在阅读 RocketMQ 源码时的一些经历,尽量让上述理论具有画面感。

2.1 了解 RocketMQ的应用场景

MQ的使用场景是比较清晰的,它的两大基本职责是解耦与削峰填谷。

举一个最简单的场景:新用户注册送积分、送优惠券场景,其原始架构设计通常如下:

可以看出用户注册和发优惠券,送积分是紧耦合的, 随着业务不断发展,活动部门提出在春节期间用户注册不送积分,发优惠券,而是赠送一个新春礼包,如果基于上述架构的话,需要去改动用户注册的主流程,违背了设计模式中的对修改关闭、对扩展开放的设计理念。

MQ的出现,可以很好地解决上面的问题:

通过引入MQ,用户注册主流程只需要完成注册逻辑,并向MQ发送一条消息,然后活动模块(送积分、送优惠券、送礼包)只需要订阅MQ中的消息,进行对应的处理。

这样消息注册主流程会非常的简单,不管活动种类如何变化,注册流程无需更改,这样就实现了解耦。

2.2  通读官方文档,从全局把握其设计理念

了解使用场景以后,接下我们可以去查阅官方文档,主要包括用户设计文档(架构设计),用户使用手册等,从全局了解其设计理念。

通过通读官方文档,不仅可以得出MQ的整体脉络(例如NameServer路由发现、消息发送、消息存储、消息消费、消息过滤),也能对顺序消费,零拷贝、同步刷盘、异步刷盘等“高端大气上档次”的高级特性产生兴趣与好奇,驱动我们去阅读其源码,探究其实现细节,使得我们在阅读源码中进行一定的自我思考成为了可能。

2.3 搭建开发调试环境

不同的系统搭建方式也不同,我这里有一篇手把手教你安装RocketMQ与IDEA Debug环境搭建。

2.4 先主干,再分支

在搭建好本地开发环境后,切忌直接用Debug去跟踪消息发送的整体流程,因为这个流程实在是太长了,从比较粗粒度来看其流程如下图所示:

如果大家想一次性将上述流程的源码全部看一遍,几乎是不可能的。 因为消息发送高可用设计、消息存储、刷盘、同步等机制,每个点详细展开的工作都是海量的,我们没有这么多连续的时间,所以适当的拆分非常有必要。

经过这样一分解,就能专注理解其某一块的设计原理,所需要的连续时间也能大大减少,一口一口“吃”,最终完成整个体系的理解。

这还带来了一个额外的好处:分批次输出多篇文章,提高了文章产出,提高了成就感。

3、阅读源码很容易放弃,怎么办?


阅读源码是枯燥的,一个人孤军奋战很容易放弃,尤其是遇到问题的时候,  如何才能坚持下去,把它读完呢?

我的答案是坚持,但允许短暂的休整、停留,然后反复发起冲刺,拼抢,直到攻克这个“山头”。

因为一旦放弃就将前功尽弃,一旦突破,自身能力能得到一个质的飞跃。

我在阅读Netty源码时就遇到了难题,当时刚刚写完Netty的内存泄露检测,准备开始研究内存分配机制, 这一块儿非常抽象,涉及的数据结构复杂,需要掌握二叉树与数组之间如何映射、牵扯到大量的位运算等等,让我在探究其原理时寸步难行,怎么办呢?放弃?

当一周、两周都无法取得突破时,人很容易想到:这样持续投入时间,又没有回报, 是不是在浪费时间?

其实我想过放弃,但转念一想:放弃后,我会做什么呢?玩游戏?看电视?

既然是玩游戏、看电视,不更是浪费时间吗?想清楚这一层后,继续攻关,继续突破就成了唯一的选择。

当然,遇到难题了,偶尔放松一两天,重新调整状态也是非常必要的。短暂的休整可以让我们变得不那么焦躁,有利于我们重新整理思路,重新发起冲刺。

当时遇到难题时,采取哪些措施有利于我们攻克难题呢?

1、向“度娘”求救

在阅读源码时可以将看不懂的代码直接COPY一小段到百度上去搜索,可能会有大牛已经对这些代码做过解读,能起到指点作用。

当时经过我的搜索,发现Netty的内存分配算法并不是首创,而是对 jemalloc 算法的实现,通过查阅相关的技术文档,可以从整体上理解Netty的内存分配算法。

2、Debug
Netty追求极致的性能,采用了大量的位运算,由于平时工作中很少会使用位运算,看起来比较吃力,为了弥补这方面的不足,使用DEBUG模式,结合运行时数据,去理解位运算,更容易开窍。

经过上面的努力,花了10天时间,Netty的内存分配机制慢慢被我解开,经过分解,我写了一系列文章来描述Netty内存分配:

迈过了这道坎,后面的源码阅读效率变得非常高效。

通过攻克一个个难题,最终从量变引发质变,逐渐形成一条自己的源码阅读理论,后续的源码分析RocketMQ、Dubbo、ElasticJob、Sentinel、Kafka等专栏就变得非常简单了。

4、源码阅读的三层境界


4.1 初级:记流水账

我初期的源码阅读文章基本上是记流水账,例如对源码一样一行加注释,只关注底层实现细节,但并未形成更高层次认知,对其设计理念没有提炼与深度领悟。

4.2 中级:能提问、思考、提炼

随着技术类文章的持续分享, 我认识了很多大牛,发现和他们交流的时候,发现他们一开始并不会说细节,而是讲设计理念。

这就要求我们在阅读源码的时候多思考,并反问自己如果自己实现的话该如何着手,如何设计,带着疑问去研究源码。通过对比,思考,会对其背后的理念有了更深刻的理解。

4.3 高级:思考、质疑、验证

不管是哪个开源框架,都会存在BUG或者实现并不合理的地方,如果大家在阅读源码的时候能够深入思考, 合理质疑,并能通过验证证明自己的观点,然后与官方取得联系,交流,共同促进社区的发展,说明我们的能力、思考得到了极大的提升。

思考与质疑是源码阅读的一个升华,比如我在看Sentinel 熔断时对其提出的质疑。

一个“用户信息查找”服务,被部署到了三个机器上。

其中192.168.1.3这个机器有一段时间负载过高,响应时间过长,发往它的请求有30%都失败了。

而30%恰恰是Sentinel设置的熔断错误率, 于是Sentinel认为整个“用户信息查找”服务不可用了,熔断了。

这明显是不合理的,因为192.168.1.4和192.168.1.5这两个机器还活着呢!

本质上这是因为熔断错误率被定义到了服务级别 :服务 -> 熔断错误率

在对这个问题进行思考的时候,我认为在配置熔断规则的时候,需要细化,应该把熔断错误率定义到资源组级别:[服务 , 机器] ->  熔断错误率

这样,当一个机器上的服务提供者被熔断后,不影响其他机器,保证了真正的高可用。

通过与官方联系,我这个想法得到了其作者的肯定,反向促进了社区的发展。

15年教学经验的Java架构师用140个实战案例完美演示微服务+丰富架构图,轻松掌握微架构设计与开发

为什么阿里巴巴的程序员成长速度这么快

【手撕Spring源码合集】带你从入门到精通;

看完三件事❤️

如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:

点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。

关注公众号 『 Java斗帝 』,不定期分享原创知识。

同时可以期待后续文章ing🚀


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK