第六课 大数据技术之Hadoop3.x的源码解析
文章目录
- 第六课 大数据技术之Hadoop3.x的源码解析
-
- 第一节 RPC通信原理解析
- 第二节 NameNode启动源码解析
- 第三节 DataNode启动源码解析
- 第四节 HDFS上传源码解析
-
- 4.1 create创建过程
- 4.2 write上传过程
- 第五节 HDFS上传源码解析
- 第六节 MapReduce源码解析
-
- 6.1 Job提交流程源码和切片源码详解
- 6.2 MapTask & ReduceTask源码解析
- 第七节 Hadoop源码编译
-
- 6.1 前期准备工作
- 6.2 工具包安装
- 6.3 编译源码
第一节 RPC通信原理解析
- RPC实例需求。模拟RPC的客户端、服务端、通信协议三者如何工作的
- 代码编写:
- 在HDFSClient项目基础上创建包名com.atguigu.rpc
- 创建RPC协议
package com.atguigu.rpc;
public interface RPCProtocol {
long versionID = 666;
void mkdirs(String path);
}
- 创建RPC服务端
package com.atguigu.rpc;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.ipc.RPC;
import org.apache.hadoop.ipc.Server;
import java.io.IOException;
public class NNServer implements RPCProtocol{
@Override
public void mkdirs(String path) {
System.out.println("服务端,创建路径" + path);
}
public static void main(String[] args) throws IOException {
Server server = new RPC.Builder(new Configuration())
.setBindAddress("localhost")
.setPort(8888)
.setProtocol(RPCProtocol.class)
.setInstance(new NNServer())
.build();
System.out.println("服务器开始工作");
server.start();
}
}
- 创建RPC客户端
package com.atguigu.rpc;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.ipc.RPC;
import java.io.IOException;
import java.net.InetSocketAddress;
public class HDFSClient {
public static void main(String[] args) throws IOException {
RPCProtocol client = RPC.getProxy(
RPCProtocol.class,
RPCProtocol.versionID,
new InetSocketAddress("localhost", 8888),
new Configuration());
System.out.println("我是客户端");
client.mkdirs("/input");
}
}
- 测试, 启动服务端 观察控制台打印:服务器开始工作
- 在控制台Terminal窗口输入,jps,查看到NNServer服务。启动客户端
- 观察客户端控制台打印:我是客户端
- 观察服务端控制台打印:服务端,创建路径/input
- 总结
- RPC的客户端调用通信协议方法,方法的执行在服务端;
- 通信协议就是接口规范。
第二节 NameNode启动源码解析
- 在pom.xml中增加如下依赖
hadoop-hdfs-client
<dependencies>
<dependency>
<groupId>org.apache.hadoopgroupId>
<artifactId>hadoop-clientartifactId>
<version>3.1.3version>
dependency>
<dependency>
<groupId>org.apache.hadoopgroupId>
<artifactId>hadoop-hdfsartifactId>
<version>3.1.3version>
dependency>
<dependency>
<groupId>org.apache.hadoopgroupId>
<artifactId>hadoop-hdfs-clientartifactId>
<version>3.1.3version>
<scope>providedscope>
dependency>
dependencies>
- ctrl + n 全局查找namenode,进入NameNode.java
- ctrl + f,查找main方法, 点击createNameNode,发现返回了一个NameNode对象点进去,查看initialize点击进去查看,
startHttpServer(conf);
点击startHttpServer。启动htt服务
- 点击startHttpServer方法中的
httpServer.start();
点击setupServlets
。这里设置了启动服务和端口结束。
- 回到initialize这里
loadNamesystem(conf);
加载镜像文件和编辑日志,点击进去看具体细节。
- 回到initialize这里
rpcServer = createRpcServer(conf);
,初始化NN的RPC服务端
- 回到initialize这里
startCommonServices
,startCommonServices
,NN启动资源检查。
- 追踪startCommonServices继续点击
NameNodeResourceChecker
, DFS_NAMENODE_DU_RESERVED_DEFAULT
默认100M的元数据储存空间。
- 从上面回到checkAvailableResources 真正检测磁盘空间逻辑
- 回到initialize这里
startCommonServices
,
- 追踪startCommonServices继续点击,
blockManager.activate(conf, completeBlocksTotal);
,继续追踪datanodeManager.activate(conf);
发现heartbeatManager.activate();
管理心跳信息。点击进去发现启动一个线程检测心跳,追踪run方法。// 10分钟 + 30秒
- 回到追踪startCommonServices继续点击
blockManager.activate(conf, completeBlocksTotal);
,继续追踪`bmSafeMode.activate(blockTotal);
计算是否满足块个数的阈值
setBlockTotal(total);`。有俩个块损毁不能启动了。
areThresholdsMet()
判断DataNode节点和块信息是否达到退出安全模式标准
第三节 DataNode启动源码解析
- 在pom.xml中增加如下依赖
<dependencies>
<dependency>
<groupId>org.apache.hadoopgroupId>
<artifactId>hadoop-clientartifactId>
<version>3.1.3version>
dependency>
<dependency>
<groupId>org.apache.hadoopgroupId>
<artifactId>hadoop-hdfsartifactId>
<version>3.1.3version>
dependency>
<dependency>
<groupId>org.apache.hadoopgroupId>
<artifactId>hadoop-hdfs-clientartifactId>
<version>3.1.3version>
<scope>providedscope>
dependency>
dependencies>
- ctrl + n 全局查找datanode,进入DataNode.java
- ctrl + f,查找main方法, secureMain -> createDataNode -> instantiateDataNode -> makeInstance -> DataNode -> startDataNode -> 这里就是对DN实例化 initDataXceiver -> dataXceiverServer(开启线程)
- ctrl + f,查找main方法, secureMain -> createDataNode -> instantiateDataNode -> makeInstance -> DataNode -> startDataNode -> startInfoServer 初始化http服务 -> DatanodeHttpServer
- ctrl + f,查找main方法, secureMain -> createDataNode -> instantiateDataNode -> makeInstance -> DataNode -> startDataNode -> initIpcServer(); 初始化RPC server
- ctrl + f,查找main方法, secureMain -> createDataNode -> instantiateDataNode -> makeInstance -> DataNode -> startDataNode -> refreshNamenodes DN向NN注册 -> doRefreshNamenodes -> createBPOS -> BPOfferService -> BPServiceActor
- ctrl + f,查找main方法, secureMain -> createDataNode -> instantiateDataNode -> makeInstance -> DataNode -> startDataNode -> refreshNamenodes DN向NN注册 -> doRefreshNamenodes -> startAll -> bpos.start() -> start -> bpThread.start(); 开启线程每个DN向NN注册, 查找它的run方法 -> connectToNNAndHandshake -> register -> rpcProxy.registerDatanode -> 接着就到了服务端NN的接口中了 ctr + N搜索NameNodeRpcServer -> 搜索函数registerDatanode -> 继续追踪
org.apache.hadoop.hdfs.server.blockmanagement.DatanodeManager#registerDatanode
addDatanode(nodeDescr);
blockManager.getBlockReportLeaseManager().register(nodeDescr);
heartbeatManager.addDatanode(nodeDescr);
heartbeatManager.updateDnStat(nodeDescr);
- 对着图追踪。
第四节 HDFS上传源码解析
4.1 create创建过程
- 添加依赖
<dependencies>
<dependency>
<groupId>org.apache.hadoopgroupId>
<artifactId>hadoop-clientartifactId>
<version>3.1.3version>
dependency>
<dependency>
<groupId>org.apache.hadoopgroupId>
<artifactId>hadoop-hdfsartifactId>
<version>3.1.3version>
dependency>
<dependency>
<groupId>org.apache.hadoopgroupId>
<artifactId>hadoop-hdfs-clientartifactId>
<version>3.1.3version>
<scope>providedscope>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-log4j12artifactId>
<version>1.7.30version>
dependency>
dependencies>
- DN向NN发起创建请求,自己写的代码。就追踪这个create即可
@Test
public void testPut2() throws IOException {
FSDataOutputStream fos = fs.create(new Path("/input"));
fos.write("hello world".getBytes());
}
- NN处理DN的创建请求 点击create ClientProtocol.java
HdfsFileStatus create(String src, FsPermission masked,
String clientName, EnumSetWritable<CreateFlag> flag,
boolean createParent, short replication, long blockSize,
CryptoProtocolVersion[] supportedVersions, String ecPolicyName)
throws IOException;
- Ctrl + h查找create实现类,点击NameNodeRpcServer,在NameNodeRpcServer.java中搜索creat
- DataStreamer启动流程 NN处理完DN请求后,再次回到DN端,启动对应的线程
4.2 write上传过程
- 向DataStreamer的队列里面写数据.用户写的代码 点击write
@Test
public void testPut2() throws IOException {
FSDataOutputStream fos = fs.create(new Path("/input"));
fos.write("hello world".getBytes());
}
- 建立管道之机架感知(块存储位置) Ctrl + n全局查找DataStreamer,搜索run方法
- 建立管道之Socket发送 点击nextBlockOutputStream
- 建立管道之Socket发送 点击nextBlockOutputStream
- 客户端接收DN写数据应答Response Ctrl + n全局查找DataStreamer,搜索run方法
第五节 HDFS上传源码解析
- Yarn客户端向RM提交作业 wordcount程序的驱动类中点击 Job.java 最后一行job.waitForCompletion 追踪
- RM启动MRAppMaster 在pom.xml中增加如下依赖 ctrl +n 查找MRAppMaster,搜索main方法
<dependency>
<groupId>org.apache.hadoopgroupId>
<artifactId>hadoop-mapreduce-client-appartifactId>
<version>3.1.3version>
dependency>
- 调度器任务执行(YarnChild) 启动MapTask ctrl +n 查找YarnChild,搜索main方法
第六节 MapReduce源码解析
- 说明:在讲MapReduce课程时,已经讲过源码,在这就不再赘述。
6.1 Job提交流程源码和切片源码详解
- Job提交流程源码详解
waitForCompletion()
submit();
connect();
new Cluster(getConfiguration());
initialize(jobTrackAddr, conf);
submitter.submitJobInternal(Job.this, cluster)
Path jobStagingArea = JobSubmissionFiles.getStagingDir(cluster, conf);
JobID jobId = submitClient.getNewJobID();
copyAndConfigureFiles(job, submitJobDir);
rUploader.uploadFiles(job, jobSubmitDir);
writeSplits(job, submitJobDir);
maps = writeNewSplits(job, jobSubmitDir);
input.getSplits(job);
writeConf(conf, submitJobFile);
conf.writeXml(out);
status = submitClient.submitJob(jobId, submitJobDir.toString(), job.getCredentials());
- FileInputFormat切片源码解析(input.getSplits(job))
6.2 MapTask & ReduceTask源码解析
- MapTask源码解析流程
=================== MapTask ===================
context.write(k, NullWritable.get());
output.write(key, value);
collector.collect(key, value,partitioner.getPartition(key, value, partitions));
HashPartitioner();
collect()
close()
collector.flush()
sortAndSpill()
sorter.sort() QuickSort
mergeParts();
collector.close();
- ReduceTask源码解析流程
=================== ReduceTask ===================
if (isMapOrReduce())
initialize()
init(shuffleContext);
totalMaps = job.getNumMapTasks();
merger = createMergeManager(context);
this.inMemoryMerger = createInMemoryMerger();
this.onDiskMerger = new OnDiskMerger(this);
rIter = shuffleConsumerPlugin.run();
eventFetcher.start();
eventFetcher.shutDown();
copyPhase.complete();
taskStatus.setPhase(TaskStatus.Phase.SORT);
sortPhase.complete();
reduce();
cleanup(context);
第七节 Hadoop源码编译
6.1 前期准备工作
- 官网下载源码
https://hadoop.apache.org/release/3.1.3.html
- 修改源码中的HDFS副本数的设置
- CentOS虚拟机准备
- 配置CentOS能连接外网。Linux虚拟机ping www.baidu.com 是畅通的
- 注意:采用root角色编译,减少文件夹权限出现问题
- Jar包准备(Hadoop源码、JDK8、Maven、Ant 、Protobuf)
- hadoop-3.1.3-src.tar.gz
- jdk-8u212-linux-x64.tar.gz
- apache-maven-3.6.3-bin.tar.gz
- protobuf-2.5.0.tar.gz(序列化的框架)
- cmake-3.17.0.tar.gz
6.2 工具包安装
- 注意:所有操作必须在root用户下完成
- 分别创建/opt/software/hadoop_source和/opt/module/hadoop_source路径
- 上传软件包到指定的目录,例如 /opt/software/hadoop_source
pwd
/opt/software/hadoop_source
ll
tar -zxvf apache-maven-3.6.3-bin.tar.gz -C /opt/module/hadoop_source/
tar -zxvf cmake-3.17.0.tar.gz -C /opt/module/hadoop_source/
tar -zxvf hadoop-3.1.3-src.tar.gz -C /opt/module/hadoop_source/
tar -zxvf protobuf-2.5.0.tar.gz -C /opt/module/hadoop_source/
pwd
/opt/module/hadoop_source
tar -zxvf jdk-8u212-linux-x64.tar.gz -C /opt/module/hadoop_source/
vim /etc/profile.d/my_env.sh
export JAVA_HOME=/opt/module/hadoop_source/jdk1.8.0_212
export PATH=$PATH:$JAVA_HOME/bin
source /etc/profile
java -version
vim /etc/profile.d/my_env.sh
MAVEN_HOME=/opt/module/hadoop_source/apache-maven-3.6.3
PATH=$PATH:$JAVA_HOME/bin:$MAVEN_HOME/bin
source /etc/profile
vi conf/settings.xml
<mirrors>
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>central</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
</mirrors>
mvn -version
yum install -y gcc* make
yum -y install snappy* bzip2* lzo* zlib* lz4* gzip*
yum -y install openssl* svn ncurses* autoconf automake libtool
yum -y install epel-release
yum -y install *zstd*
pwd
./bootstrap
make && make install
cmake -version
pwd
/opt/module/hadoop_source/protobuf-2.5.0
./configure --prefix=/opt/module/hadoop_source/protobuf-2.5.0
make && make install
vim /etc/profile.d/my_env.sh
PROTOC_HOME=/opt/module/hadoop_source/protobuf-2.5.0
PATH=$PATH:$JAVA_HOME/bin:$MAVEN_HOME/bin:$PROTOC_HOME/bin
source /etc/profile
protoc --version
libprotoc 2.5.0
6.3 编译源码
pwd
/opt/module/hadoop_source/hadoop-3.1.3-src
mvn clean package -DskipTests -Pdist,native -Dtar
pwd
/opt/module/hadoop_source/hadoop-3.1.3-src/hadoop-dist/target