ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Hadoop源码深度剖析:从核心模块到RPC与HDFS读写链路实战

Hadoop源码深度剖析:从核心模块到RPC与HDFS读写链路实战 我决定把这一年多啃Hadoop源码的笔记整理成一篇可以直接照着读的索引式分享。不是那种罗列类名的源码导读也不是贴一堆注释的代码复述而是从“我为什么会去读源码、读了哪些模块、怎么搭建调试环境、核心流程到底怎么跑通、踩了哪些坑”这几个角度把Hadoop源码剖析这件事讲透。如果你正在准备Hadoop面试题或者被伪分布式搭建、集群环境配置折腾得够呛又或者看完一堆apache官方文档仍然对NameNode、DataNode、RPC这些概念停留在“背定义”的阶段那这篇文章应该能帮你把最后一块拼图补上。Hadoop源码的难点不在于某个类有多难懂而在于它的模块多、依赖深、启动链路长。但只要找对切入点和调试方法它的代码风格其实非常工程化可读性比想象中好很多。1. 动手之前先把源码阅读策略定下来1.1 源码版本怎么选别一上来就追最新我见过不少人下载了最新的Hadoop 3.4.x然后编译半天跑起来之后发现网上大部分资料都对不上号怀疑人生。读源码这件事版本选型直接决定后续所有的体验。Hadoop的版本线主要是两条2.x系列和3.x系列。2.x是经典中的经典很多老项目、老面试题、老博客都基于2.x比如2.7.5、2.10.x。3.x引入了HDFS Federation的完善、YARN Timeline Service、基于Protobuf的RPC重构等大改动。我的建议是如果你是为了面试和工作中的系统设计直接看3.x的稳定版比如3.3.x如果你是想先找一个简单一点的版本把整体链路跑通2.10.x会更轻量。我当时选的是Hadoop 3.3.4原因有几个第一这个版本在社区使用面广依赖问题在Stack Overflow上基本都有答案第二3.x的代码把很多2.x里纠缠不清的逻辑拆得更清晰了比如RPC那部分Protobuf化之后接口定义和实现分的很开读起来不累。还有一个很现实的问题源码版本和你实际部署的集群版本要一致否则调试的时候断点打在错误的行号上那酸爽我不太想回忆。比如你用3.3.4的源码去调试3.1.3的集群很多类名一样但方法内部已经重写过了。所以动手第一步先在官网把对应版本的源码tar.gz下载下来别直接用git clone master分支。1.2 模块拆解Hadoop不是一个项目是四个项目Hadoop源码最劝退新人的地方就是它不是一个单一工程而是一个多模块的Maven项目集合。顶层目录就能看到hadoop-common、hadoop-hdfs、hadoop-mapreduce、hadoop-yarn四大核心模块下面又挂了几十个子模块。读Hadoop源码前要建立一个清晰的模块地图不然进去就是迷路。我个人推荐按以下顺序来先读Hadoop Common里的RPC模块hadoop-common项目的org.apache.hadoop.ipc包这是整个Hadoop的神经中枢NameNode和DataNode、客户端和集群之间的所有通信都走这套RPC。读懂了RPC后面再看HDFS和YARN的通信逻辑就是顺水推舟的事。再读HDFS的NameNode启动链路和文件读写链路这是整个Hadoop里最有故事性的部分涉及到内存元数据、日志、状态机、分布式协议设计。接着可以看YARN的资源调度ResourceManager、Scheduler这个模块和HDFS相对独立逻辑上更偏“管理平台”。MapReduce模块放到最后因为它的很多逻辑是建立在HDFS和YARN之上的先看它容易被各种TaskAttempt、JobStateChangeListener绕晕。从代码量来说HDFS和Common是重点YARN次之MapReduce反而没有那么难啃。很多面试题比如“NameNode宕机怎么办”“HDFS写数据流程”答案的核心逻辑全都集中在HDFS和Common两个模块里。1.3 读Hadoop源码需要哪些前置知识有些朋友Java基础还没打牢就冲进源码结果连续几周都在看各种类继承和抽象类最后放弃。我盘点一下实际需要的前置能力Java多线程与并发这块是硬门槛。NameNode里到处是读写锁ReentrantReadWriteLock、轻量级锁synchronized、线程池ExecutorService和状态机。如果你对AQS、Condition、FutureTask不熟至少要把《Java并发编程实战》里的前六章翻一遍。RPC的基本原理不要求你能手写Dubbo但要知道动态代理、序列化、网络通信的基础概念。Hadoop的RPC是自己实现的不是用的Netty它的IPC模型是基于Java NIO和阻塞IO混用的这部分读起来需要一定的网络编程底子。文件系统的基础概念块Block、元数据、流式读写InputStream/OutputStream、权限模型这些概念能帮你降低理解HDFS的门槛。Linux操作和Shell基本功虽然读源码主要在IDE里但编译、看日志、起进程都离不开Linux特别是看启动脚本bin/hdfs、bin/yarn时没有Shell基础会比较痛苦。前置知识不等于要全部精通才能开读而是边读边补。我在读RPC那段时硬生生把Java NIO的Selector机制重新学了一遍不然真的看不懂Server端怎么管理成千上万个连接。2. Hadoop源码地图四大模块和核心类2.1 Common模块配置系统、抽象文件系统和RPCHadoop Common是一切的基础。很多人忽略它直接去啃HDFS结果遇到Configuration、FileSystem、Path这些基础类时一知半解后面越看越累。重点说几个核心部分。org.apache.hadoop.conf.Configuration是贯穿全局的配置类它做的事情本质上是把core-site.xml、hdfs-site.xml、yarn-site.xml等配置文件里的键值对加载进内存并支持资源覆盖。读这个类能帮你理解Hadoop里各种fs.defaultFS、dfs.replication参数是怎么被解析和传递的。org.apache.hadoop.fs.FileSystem是分布式文件系统的抽象基类。它定义了open、create、mkdirs、rename、delete这些操作的标准接口HDFS的DistributedFileSystem继承了它LocalFileSystem也在本地模式下实现了它。理解FileSystem对后续读HDFS的入口代码特别重要因为DFSClient的很多上层API入口就是从FileSystem这个抽象类开始的。org.apache.hadoop.ipc这个包就是我自己花时间最多的RPC实现。核心类包括RPC、Client、Server、ProtobufRpcEngine、WritableRpcEngine。大概逻辑是客户端通过动态代理把方法调用变成RPC请求经过序列化后通过网络发送给服务端服务端的Server监听端口把请求反序列化后分发到具体的实现类上执行。2.2 HDFS模块NameNode、DataNode、JournalNodeHDFS是Hadoop里最有“体系感”的模块因为分布式文件系统要解决的问题实在太典型了——元数据管理、数据存储、副本策略、故障恢复。NameNode相关的核心类都在hadoop-hdfs模块的org.apache.hadoop.hdfs.server.namenode包下。FSNamesystem是NameNode内部所有名字空间操作的“总指挥”它持有Directory目录树、BlockManager块管理、LeaseManager租约管理等核心组件。FSImage和EditLog是元数据持久化的两个关键类FSImage对应的是元数据快照EditLog对应的是操作日志流水。NameNode启动时会加载FSImage然后回放EditLog把元数据恢复到最新状态。DataNode的核心类在org.apache.hadoop.hdfs.server.datanode包下。DataNode本身是一个常驻进程它的主要职责是存储Block和向NameNode上报状态。BlockReceiver负责接收外部写入的数据FsDatasetImpl负责管理本地磁盘上的数据块文件。理解DataNode的关键是理解它与NameNode之间的心跳和块汇报机制。JournalNode是HAHigh Availability模式下的核心角色对应org.apache.hadoop.hdfs.qjournal包。它的作用是在两个NameNode之间共享EditLog让Active和Standby NameNode能够保持元数据同步。如果你只搭过单机伪分布式这块可能接触不多但只要走到生产级HA它就非常重要。2.3 YARN模块调度器和应用管理YARN的设计目标是“资源调度与作业计算分离”。ResourceManager负责全局资源管理和调度NodeManager负责单个节点上的资源隔离和任务执行ApplicationMaster是每个作业的“经理”负责向ResourceManager申请资源并协调任务执行。ResourceManager里最值得关注的是Scheduler它是个接口实现类包括CapacityScheduler容量调度器、FairScheduler公平调度器和FifoScheduler先进先出调度器。调度器的核心逻辑就是为各个应用分配Container。代码层面SchedulerNode、SchedulerApplicationAttempt、SchedulerQueue这些类的名字光看类名就能猜出七八分职责。如果你只关心“作业怎么跑起来的”可以重点跟一遍ApplicationMaster的启动流程。从ResourceManager接收到一个提交的作业开始到启动一个Container运行MRAppMaster再到MRAppMaster向ResourceManager注册并申请后续任务容器这一套流程非常适合锻炼阅读长链路代码的能力。2.4 MapReduce模块分而治之的工程落地方案MapReduce模块本质上是一个编程模型规范加一个执行引擎实现。源码里你会看到mapreduce包和mapred包并存这是历史遗留问题。前者是新版APIorg.apache.hadoop.mapreduce后者是旧版APIorg.apache.hadoop.mapred新版API只是包名不同核心逻辑一致看新版就行。MapReduce执行的核心是MapTask和ReduceTask。MapTask里最值得读的是MapTask.run方法它包含读输入、执行Mapper、分区排序、溢写spill、合并merge的完整流程。ReduceTask.run方法则包含拉取Map输出、归并排序、执行Reducer、写输出。整个Shuffle过程包括分区、排序、溢写、抓取、合并是最经典的分布式计算“魔鬼细节”面试里被问烂的环形缓冲区CircularBuffer就在这里。我一开始以为MapReduce会是最难读的后来发现它反而是最好读的因为它的数据流太清晰了——输入分片、Map、Shuffle、Reduce、输出代码结构和这个流程一一对应不像NameNode那样有各种状态机嵌套。2.5 核心类速查表为了方便按图索骥我把最常见的核心类整理成一张速查表模块核心类/包职责简述Commonorg.apache.hadoop.conf.Configuration全局参数配置加载与覆盖Commonorg.apache.hadoop.ipc.RPC / Client / Server自研RPC通信框架HDFSorg.apache.hadoop.hdfs.DFSClientHDFS客户端入口HDFSorg.apache.hadoop.hdfs.server.namenode.FSNamesystemNameNode核心名字空间逻辑HDFSorg.apache.hadoop.hdfs.server.namenode.BlockManager数据块与副本管理HDFSorg.apache.hadoop.hdfs.server.datanode.DataNode数据节点存储与上报YARNorg.apache.hadoop.yarn.server.resourcemanager.ResourceManager集群资源调度中枢YARNorg.apache.hadoop.yarn.server.resourcemanager.scheduler.Scheduler资源调度算法实现MapReduceorg.apache.hadoop.mapreduce.Mapper / Reducer编程模型接口MapReduceorg.apache.hadoop.mapred.MapTask / ReduceTask任务执行主流程这张表不是让你背而是让你在读源码的过程中反复对照。Class名字在Hadoop源码里基本做到了“见名知义”这是它工程素养高的一个体现。3. 源码阅读环境搭建与构建实操3.1 为什么建议先从源码编译开始很多教程会让你直接下载预编译的Hadoop发行版然后把源码包导入IDEA当普通Java工程看。但我强烈建议至少亲自执行一次源码编译。原因很实际编译过程中你会遇到protoc版本不匹配、Maven依赖下载失败、native库缺失等一堆问题这个过程能逼你去理解Hadoop的构建系统、第三方依赖、版本兼容性。这些问题在以后自己写基于Hadoop的二次开发时一样会遇到。而且编译好的target目录里会有完整的jar包和依赖清单后面调试时用什么classpath、缺哪个依赖扫一眼target目录就心里有数了。比起对着源码干瞪眼有了一套能跑起来的二进制调试时才真正落地。3.2 编译环境准备JDK、Maven和Protocol BuffersHadoop 3.3.x的编译环境我实际用下来是这样的JDK 8必须是JDK 8JDK 11有概率遇到javadoc插件兼容性问题除非你愿意折腾额外参数Maven 3.6.3或3.8.x太高或太低都可能有插件不兼容Protocol Buffers编译器protoc版本必须恰好是2.5.0——这是Hadoop源码里proto文件资源严格锁定的版本装成3.x会直接编译报错而且错误信息特别难以理解另外还需要一些native库的依赖比如zlib、openssl如果你不需要native压缩解压可以跳过这一块后面用-Pnative参数时再补一边。在Ubuntu上安装这些依赖时的常见做法是sudo apt-get install -y maven openjdk-8-jdk # 下载protoc 2.5.0并放到/usr/local/bin wget https://github.com/protocolbuffers/protobuf/releases/download/v2.5.0/protoc-2.5.0-linux-x86_64.zip cd /usr/local sudo unzip protoc-2.5.0-linux-x86_64.zip export PATH/usr/local/bin:$PATH protoc --version # 确认输出libprotoc 2.5.0依赖的下载建议把Maven仓库源换成国内镜像否则hadoop-common一个模块就要拉几百MB的依赖等的过程中你会怀疑人生。另外Maven需要配置的堆内存至少给足1GBHadoop编译涉及的模块多内存不够经常OOM。3.3 源码构建全流程实测记录下面是我在干净的Ubuntu 20.04上完整编译Hadoop 3.3.4的过程每一步都实测过。先解压源码包进入根目录tar -zxvf hadoop-3.3.4-src.tar.gz cd hadoop-3.3.4-src先做一些跳过检查的快速编译验证环境没问题mvn clean install -DskipTests -Dmaven.javadoc.skiptrue -Dcheckstyle.skiptrue -Dfindbugs.skiptrue -Ddocker.skiptrue这个命令会编译并安装所有模块到本地Maven仓库。等第一次跑通后再考虑编译native库和dist包mvn package -Pdist,native -DskipTests -Dmaven.javadoc.skiptrue -Drequire.opensslfalse -Dtar这里-Pdist表示生成标准的hadoop发布包-Pnative是在enable native本地库支持。实际编译时间取决于机器性能16G内存8核CPU大概需要10到20分钟CPU弱的话半小时以上也正常。编译完成后在hadoop-dist/target目录下会生成hadoop-3.3.4.tar.gz。这个包就可以直接拿来当伪分布式集群和调试集群用和官方预编译发行版几乎没有差别。3.4 IDEA导入与本地调试配置源码编译好之后用IntelliJ IDEA直接导入根目录的pom.xmlMaven会自动识别所有子模块。因为模块非常多IDEA第一次索引会卡一会儿建议给IDEA增加-Xmx2048m以上的堆内存不然卡到怀疑人生。调试Hadoop进程的方式我试过三种按推荐程度排序方式一写JUnit测试类直接调用MiniDFSCluster。这是最推荐的调试方式。MiniDFSCluster是HDFS自带的测试集群工具可以在本地JVM里同时启动NameNode、DataNode和客户端你不用额外起进程直接在测试类里打断电所有线程都在同一个JVM里变量值、调用栈一目了然。方式二在IDEA里配置远程调试连接一个已经启动的伪分布式集群。先用bin/hdfs namenode启动NameNode然后配置JVM参数-agentlib:jdwptransportdt_socket,servery,suspendy,address5005在IDEA里建一个Remote Debug配置连接上去。方式三直接用IDEA运行NameNode、DataNode的main方法但要手动把conf目录、日志目录都配好坑非常多新手不建议。MiniDFSCluster的具体用法在hadoop-hdfs项目的src/test/java目录下非常多测试用例可以参考。最少启动一个集群的代码大致是这样Configuration conf new Configuration(); conf.set(dfs.datanode.data.dir.perm, 755); try (MiniDFSCluster cluster new MiniDFSCluster.Builder(conf) .numDataNodes(1) .build()) { FileSystem fs FileSystem.get(conf); // 在这里打断点调试 cluster.shutdown(); }这种方式能让你在几分钟内进入源码调试而不是花两小时搭环境。4. 核心流程源码级解析4.1 RPC调用链路从动态代理到Server处理RPC是Hadoop分布式协作的地基先把这条链路拿出来解剖。客户端调用一个NameNode接口方法比如创建文件实际执行的过程远比一个普通方法调用复杂。整体链路是DFSClient拿到的是一个动态代理生成的ClientProtocol实现它内部通过RPC.getProxy创建代理对象。代理对象的方法调用会被转入ProtobufRpcEngine的Invoker。Invoker把方法名和参数封装成一个RPC请求交给IPC的Client。Client负责建立到NameNode的TCP连接把请求发送出去然后等待响应。从Server端来看NameNodeRpcServer监听8020端口。它启动的时候会初始化一个IPC Server这个Server有一个Listener线程负责accept新连接每个连接对应一个Connection。请求到达后Server会从线程池里分配一个Handler线程来处理。Handler线程做的事情是反序列化请求、找到对应的Protocol实现类、调用具体方法、把结果序列化后交给Responder线程返回给客户端。这条链路的地方在于它的并发模型不是简单的请求-线程模型而是多路复用加线程池。所以读org.apache.hadoop.ipc.Server源码时你会看到Listener、Connector、Handler、Responder这四类线程协同工作。搞懂这个模型之后再去读RPC相关的性能优化和超时配置就很容易理解了。4.2 NameNode启动流程一个文件系统的“开机过程”NameNode是HDFS的元数据大脑它的启动过程本质上是一次“元数据重建”。我建议把NameNode的启动拆成三个阶段来读。第一个阶段是解析启动参数。NameNode.main方法会调用createNameNode根据启动参数判断是格式化、滚动升级、启动Active还是Standby。实际启动走的是startNameNode方法。第二个阶段是加载元数据。initialize方法里会调用loadFromDisk这一步最关键。它会读取FSImage文件这是整个名字空间的快照然后读取EditLog文件把自上次快照以来的所有操作日志重放一遍把目录树、文件属性、块信息恢复到最新状态。这个过程对应的是FSNamesystem的loadFSImage和EditLog的回放逻辑。3.x版本里加载完成之后会做saveNamespace生成一个新的checkpoint来缩短下次重启的回放时间。第三个阶段是启动对外服务。startCommonServices会启动RPC Server、启动BlockManager的block报告处理线程、启动LeaseManager的租约管理线程。如果配了HA还有StartupProgress和EditLogTailer线程去跟踪Standby的日志同步进度。读NameNode启动流程最有效的方式是在NameNode.initialize()方法里打一个断点用单步调试走一遍整个启动过程。我当初这么干的时候很多抽象的概念比如“FSImage和EditLog合并”“checkpoint是什么”立刻就具象了。4.3 一次写文件操作客户端到数据管道HDFS写数据的完整链路是面试题里最常考的也是源码剖析的必修课。从DFSClient.create()开始客户端会向NameNode发起一个RPC请求对应ClientProtocol.create()。NameNode在FSNamesystem里执行startFileInternal它要做的事情包括检查权限、在目录树里新增一个INodeFile、为文件分配一个租约Lease、记录一条EditLog日志、返回一个HdfsFileStatus对象给客户端。拿到响应后客户端会创建DFSOutputStream和DataStreamer。DataStreamer是写数据管道的核心线程它的run方法会循环从队列里取数据包然后向NameNode申请数据块的位置addBlock接着按照pipeline顺序DataNode列表建立连接把数据包依次发送出去。每个DataNode收到数据后会先写入本地磁盘再转发给下一个DataNode形成一条流水线。当所有数据写完后客户端调用complete方法通知NameNode。NameNode在FSNamesystem里完成最后的收尾工作包括确认所有数据块的副本已经满足要求、关闭租约、再记录一条EditLog。这个时候文件才真正“落袋为安”。读这段源码时重点要关注的是数据流和元数据流这两个流是分离且异步的数据流是DataStreamer和DataNode之间的网络传输元数据流是客户端和NameNode之间的RPC调用。这种设计是HDFS高吞吐的一个关键。4.4 心跳和块汇报分布式系统里的“心跳协议”心跳是HDFS里最基础也最直观的分布式协调机制。DataNode每隔一段时间会向NameNode发送一个心跳报告自身状态。对应代码是DataNode的offerService方法它会循环调用HeartbeatManager的sendHeartbeatNameNode收到后返回一个DatanodeCommand数组里面可能包含“需要你删掉哪些块”“需要你传输哪些块给其他节点”等指令。块汇报BlockReport是另一种更重的心跳。DataNode启动时会做全量块汇报之后周期做增量块汇报。NameNode收到块汇报后会更新BlockManager里的数据块映射关系判断哪些块处于安全状态、哪些块副本不足需要复制。读BlockManager的processReport方法可以看到很多关于副本状态机的状态转换逻辑。这部分源码非常适合补一补“分布式系统怎么保证最终一致性”的感觉。DataNode上报的状态是滞后的NameNode不能假设上报数据一定正确它要做各种对比、补偿、调度最终让集群达到期望的状态。5. 源码调试的常见问题与排查技巧5.1 编译期问题protoc版本和Maven依赖很多人卡在源码编译第一个坎。最常见的就是Protocol Buffers编译报错提示的信息类似“protoc: error while loading shared libraries: libprotoc.so.9”。这是因为系统装了Protobuf 3.x而不是2.5.0。解决方法就是严格按照源码根目录README里的版本要求安装protoc 2.5.0并确保protoc --version输出版本正确。另一个高频问题是Maven依赖下载慢。我建议在Maven的settings.xml里配置阿里云镜像同时在~/.m2目录预留至少10GB空间。Hadoop全家桶的依赖树非常深依赖下载失败时会提示某个artifact找不到不用慌多半是网络问题多试几次或者换镜像源就能解决。还有一个容易被忽略的问题是构建时内存不足Maven编译hadoop-mapreduce-client模块经常OOM。要记得在MAVEN_OPTS环境变量里设置export MAVEN_OPTS-Xms1g -Xmx2g -XX:MaxPermSize512m5.2 运行期问题权限、目录和配置我调试时踩过最大的坑是权限问题。NameNode在本地需要写namenode目录DataNode需要写datanode目录如果目录的所有者和当前启动用户不一致启动要么报Permission Denied要么直接格式化失败。这个问题在伪分布式里特别常见因为很多教程让你用root操作而HDFS内部的DFSAdmin和shell命令对权限判断又很严格。还有一个坑是core-site.xml里的fs.defaultFS和hdfs-site.xml里的dfs.namenode.http-address配置不一致导致Status页面能访问但shell命令连不上集群。读源码时你可能觉得这些参数只是简单键值对但实际上它们被Configuration类在多个地方读取一处配置错误会引起连锁反应。5.3 调试断点停太久导致超时用MiniDFSCluster或远程调试时在NameNode的RPC服务方法里打断点很容易把调试搞挂。因为客户端DFSClient默认有IPC超时重试一旦断点停留时间太长客户端会抛出SocketTimeoutException然后重试又触发了另一个断点最后整个集群状态错乱。这种情况的解决办法是在调试前把下面几个参数调大conf.setInt(ipc.client.connect.timeout, 60000); conf.setInt(ipc.ping.interval, 60000); conf.setInt(dfs.namenode.handler.count, 128);虽然不是优雅的解法但实测下来能让断点调试变得可用。记住调试分布式代码和调试单机程序完全是两种思路要把“超时重试”“心跳”这些分布式理念时刻放在脑子里。5.4 常见问题速查表我把这一年多来遇到的典型问题整理成一个速查表按经验排序问题现象可能原因解决/规避方法protoc编译报错protoc版本不是2.5.0安装protoc 2.5.0并确认PATHMaven依赖下载失败网络问题或镜像源不稳定配置阿里云镜像尽量用完整源码包编译OOMMaven内存不足设置MAVEN_OPTS增加堆内存NameNode启动报Permission Denied数据目录权限不对检查dfs.namenode.name.dir目录的属主客户端连接NameNode超时断点停留时间太长调大ipc.client.connect.timeout租约过期文件被删除LeaseManager回收调试时注意控制断点时间避免长时间暂停DataNode日志出现Too many open files文件句柄耗尽提高ulimit -n上限MiniDFSCluster启动失败端口冲突或目录未清理换个测试端口清理test.dir这张表不能涵盖所有场景但大部分新人遇到的坑基本都集中在这几类里。5.5 我的调试工作流最后分享我现在比较顺手的源码调试工作流给想快速入坑的朋友一个参考。第一步是精读核心类不是读所有代码是带着问题读。比如想知道“NameNode怎么处理心跳”就打开HeartbeatManager搜索sendHeartbeat方法顺着调用链往下走。遇到不认识的类先看类注释再搜使用入口不要陷入到每个细节。第二步是改代码加日志。读源码的时候我经常在关键方法里临时加System.out.println或者用log.info输出关键变量然后重新编译模块跑一个MiniDFSCluster测试。这种方式看代码运行时的真实值比脑补变量值要踏实得多。第三步是画调用链时序图。我不推荐用复杂画图工具拿纸笔简单画一下“客户端create - NameNode RPC server - FSNamesystem - EditLog - DataNode”这个流程就足够清晰了。画完之后再去看具体实现类效率翻倍。我个人在实际操作中的体会是读Hadoop源码最大的障碍不是代码本身而是缺乏一个完整的“运行上下文”。所以一定要想办法让代码跑起来借助调试器、测试集群和日志让源码词句和真实执行过程一一对应。这个习惯养成之后读其他开源项目也会快的多。这个内容后续还可以这样扩展读完HDFS和RPC之后可以尝试自己动手写一个迷你分布式文件系统的核心逻辑用几百行代码模拟NameNode的元数据管理、DataNode的存储和客户端读写。你会在写的过程中发现读源码时忽略掉的设计细节全都会自己蹦出来。
返回列表