hadoop datanode源码分析

[java] view plaincopy
  1. DataNode源代码分析:  
  2. 1.简介:DataNode是文件存储的基本单元,它将Block存储在本地文件系统中,保存了Block的Meta-data,  
  3.        同时周期性地将所有存在的Block信息发送给NameNode  
  4.   
  5. 2.main启动DataNode  
  6.   
  7. 2.1:shell脚本启动DataNode  
  8. |-->hadoop/bin/start-all.sh  
  9. |-->start-dfs.sh  
  10. |-->"$bin"/hadoop-daemons.sh --config $HADOOP_CONF_DIR start datanode $dataStartOpt  
  11.   
  12. 2.2:main()函数启动分析  
  13. |-->StringUtils.startupShutdownMessage(DataNode.class, args, LOG); |设置启动和关闭日志信息  
  14.     |-->toStartupShutdownString()  
  15.     |-->Runtime.getRuntime().addShutdownHook() |通过设置钩子,完成日志结束标志  
  16. |-->DataNode datanode = createDataNode(args, null);  |见2.3  
  17. |-->datanode.join();  |主线程等待datanode线程执行完成  
  18.   
  19. 2.3 createDataNode(args, null) |用于创建Datanode实例,并启动Datanode线程  
  20. |-->DataNode dn = instantiateDataNode(args, conf);   
  21. |-->runDatanodeDaemon(dn);   
  22.   
  23. 2.3.1 instantiateDataNode(args, conf) |实例化DataNode结点  
  24. |-->parseArguments(args, conf)  |根据args解析加载conf的参数值  
  25. |-->String[] dataDirs = conf.getStrings("dfs.data.dir");  |获取datanode的本地存储路径  
  26. |-->makeInstance(dataDirs, conf);    
  27.   
  28. 2.3.2 makeInstance(dataDirs, conf); |创建Datanode实例  
  29. |-->for (int i = 0; i < dataDirs.length; i++)    
  30.     |-->dirs.add(data);  
  31. |-->return new DataNode(conf, dirs);  |返回DataNode实例  
  32.   
  33. 2.3.3 runDatanodeDaemon(dn);  |运行DataNode结点  
  34. |-->dn.register();  |向namenode发送注册信息,namenode会通过心跳机制传递命令给datanode  
  35. |-->dn.dataNodeThread = new Thread(dn, dnThreadName);  
  36. |-->dn.dataNodeThread.setDaemon(true);  
  37. |-->dn.dataNodeThread.start();  
  38.   
  39.   
  40. 3.DataNode实例化,通过startDataNode(conf, dataDirs)进行实例化  
  41. |-->setMachineName  |设置machineName  
  42.     |-->machineName = conf.get("slave.host.name");   
  43.     |-->machineName = DNS.getDefaultHost()  
  44. |-->nameNodeAddr = NameNode.getAddress(conf);  |获取nameNode的地址信息  
  45. |-->setSocketout时间  
  46.     |-->his.socketTimeout =  conf.getInt("dfs.socket.timeout",HdfsConstants.READ_TIMEOUT);  
  47.     |-->this.socketWriteTimeout = conf.getInt("dfs.datanode.socket.write.timeout",  
  48.                                           HdfsConstants.WRITE_TIMEOUT);  
  49. |-->this.writePacketSize = conf.getInt("dfs.write.packet.size"64*1024); |写包的大小,默认64K  
  50. |-->String address = NetUtils.getServerAddress(   |设置地址  
  51.                  conf,  
  52.                                 "dfs.datanode.bindAddress",   
  53.                                 "dfs.datanode.port",  
  54.                                 "dfs.datanode.address");)    
  55. |-->InetSocketAddress socAddr = NetUtils.createSocketAddr(address);  |创建本地socketaddress地址  
  56. |-->int tmpPort = socAddr.getPort();  |端口号  
  57. |-->storage = new DataStorage();      |DataStorage保存了存储相关的信息  
  58. |-->this.dnRegistration = new DatanodeRegistration(machineName + ":" + tmpPort); |构造一个注册器  
  59. |-->this.namenode = (DatanodeProtocol)  RPC.waitForProxy();  |通过动态代理生成namenode实例  
  60.     |-->RPC.class中的getProxy()  
  61.     |-->VersionedProtocol proxy = (VersionedProtocol) Proxy.newProxyInstance(  
  62.             protocol.getClassLoader(), new Class[] { protocol },  
  63.             new Invoker(addr, ticket, conf, factory));  
  64. |-->NamespaceInfo nsInfo = handshake();  |主要包含buildVersin和distributeUpgradeVersion,用于版本检验  
  65.     |-->nsInfo = namenode.versionRequest();  
  66.         |-->return namesystem.getNamespaceInfo();   
  67. |-->boolean simulatedFSDataset =          
  68.         conf.getBoolean("dfs.datanode.simulateddatastorage"false);  
  69. |-->if (simulatedFSDataset) |判断一下是否是伪分布式,否则走正常判断,此处分析正常逻辑  
  70. |-->else   
  71.     |-->storage.recoverTransitionRead(nsInfo, dataDirs, startOpt);  
  72.     |-->this.dnRegistration.setStorageInfo(storage); |将storage进行信息注册  
  73.     |-->this.data = new FSDataset(storage, conf);    |根据storage和conf信息,生成FSDataset,用于数据块操作  
  74. |-->ServerSocket ss = (socketWriteTimeout > 0) ?   |初始化Socket服务器端,区分NIO和IO  
  75.           ServerSocketChannel.open().socket() : new ServerSocket();  
  76. |-->Server.bind(ss, socAddr, 0);   
  77. |-->ss.setReceiveBufferSize(DEFAULT_DATA_SOCKET_SIZE);  |设置接收的buffer缓存大小,默认64K  
  78. |-->selfAddr = new InetSocketAddress(ss.getInetAddress().getHostAddress(),  
  79.                                      tmpPort);  
  80. |-->this.dataXceiverServer = new Daemon(threadGroup, new DataXceiverServer |初始化处理类dataXceiverServer  
  81.                         (ss, conf, this));   
  82. |-->setInterval  |分别设置块状态信息间隔时间和心跳间隔时间  
  83.     |-->blockReportInterval  
  84.     |-->heartBeatInterval  
  85. |-->blockScanner = new DataBlockScanner(this, (FSDataset)data, conf);   |blockScanner用于定时对文件块进行扫描  
  86. |-->this.infoServer = new HttpServer("datanode", infoHost, tmpInfoPort, |创建HttpServer,内部用jetty实现,用于页面监控  
  87.         tmpInfoPort == 0, conf);  
  88. |-->ipcServer = RPC.getServer(this, ipcAddr.getHostName(), ipcAddr.getPort(),  |开启本地ipc服务,监听来自client和其它  
  89.         conf.getInt("dfs.datanode.handler.count"3), false, conf);              datanode结点的指令信息  
  90.   
  91. 4.Datanode线程运行 run()方法  
  92. |-->dataXceiverServer.start();  |启动dataXceiverServer服务器  
  93.     |-->new Daemon(datanode.threadGroup,      |根据socket接送状态,启动DataXceiver,见4.1  
  94.             new DataXceiver(s, datanode, this)).start();  
  95. |-->startDistributedUpgradeIfNeeded();      
  96. |-->offerService();    |与namenode完成心跳机制,并接受来自namenode的命令 ,见4.2  
  97.   
  98. 4.1 DataXceiver的run()方法  
  99. |-->in = new DataInputStream(      |获取来自namenode结点的流信息  
  100.           new BufferedInputStream(NetUtils.getInputStream(s),   
  101.                                   SMALL_BUFFER_SIZE));  
  102. |-->short version = in.readShort();  |读取版本信息  
  103. |-->boolean local = s.getInetAddress().equals(s.getLocalAddress()) |判断是否本地地址  
  104. |-->byte op = in.readByte(); |获取命令指令,主要有以下几种   
  105.     |-->DataTransferProtocol.OP_READ_BLOCK     |读取block信息  
  106.     |-->DataTransferProtocol.OP_WRITE_BLOCK:   |写block信息  
  107.     |-->DataTransferProtocol.OP_READ_METADATA: |读取元数据信息  
  108.     |-->DataTransferProtocol.OP_REPLACE_BLOCK  |替换块信息  
  109.     |-->DataTransferProtocol.OP_COPY_BLOCK      |复制块信息  
  110.     |-->DataTransferProtocol.OP_BLOCK_CHECKSUM |较验block信息  
  111.   
  112. 4.1.1 .OP_READ_BLOCK -->readBlock(DataInputStream in)  |读取数据块信息  
  113. |-->首先读取block描述信息  
  114.     |-->long blockId = in.readLong();            
  115.     |-->Block block = new Block( blockId, 0 , in.readLong());  
  116.     |-->long startOffset = in.readLong();  
  117.     |-->long length = in.readLong();  
  118.     |-->String clientName = Text.readString(in); |Utf-9转码读取clientName信息  
  119. |-->创建输出流  
  120.     |-->OutputStream baseStream = NetUtils.getOutputStream(s,   
  121.         datanode.socketWriteTimeout);  
  122.     |-->DataOutputStream out = new DataOutputStream(  
  123.                  new BufferedOutputStream(baseStream, SMALL_BUFFER_SIZE));  
  124. |-->blockSender = new BlockSender(block, startOffset, length,  
  125.             truetruefalse, datanode, clientTraceFmt);  
  126. |-->out.writeShort(DataTransferProtocol.OP_STATUS_SUCCESS);   
  127. |-->long read = blockSender.sendBlock(out, baseStream, null);  |发送block数据  
  128. |-->if (blockSender.isBlockReadFully())  |如果读取的整个块信息,则需要校验块信息  
  129.     |-->datanode.blockScanner.verifiedByClient(block);  
  130. |-->datanode.myMetrics.bytesRead.inc((int) read);  
  131. |-->datanode.myMetrics.blocksRead.inc();  
  132. |-->关闭相应流信息  
  133.     |-->IOUtils.closeStream(out);  
  134.     |-->IOUtils.closeStream(blockSender);      
  135.         
  136. 4.1.1.1 sendBlock(out, baseStream, null) |读取Block信息时,发送block数据流  
  137. |-->this.throttler = throttler;  |设置调节器,用于调节流速度与带宽的关系  
  138. |-->写头信息  
  139.     |-->checksum.writeHeader(out);  
  140.     |-->out.writeLong( offset );  
  141.     |-->out.flush();  
  142. |-->设置packetSize大小  
  143.     |-->int pktSize = DataNode.PKT_HEADER_LEN + SIZE_OF_INTEGER;  |初始化设置,并根据流性质,设定大小  
  144. |-->ByteBuffer pktBuf = ByteBuffer.allocate(pktSize);  
  145. |-->while (endOffset > offset)    |循环读,直到读取完成  
  146.     |-->long len = sendChunks(pktBuf, maxChunksPerPacket,   
  147.                               streamForSendChunks);  
  148. |-->out.writeInt(0); 设置0为标志位,读取完成  
  149. |-->return totalRead;  
  150.   
  151. 4.1.1.2 sendChunks() 一共分为三个部分  
  152. |-->1:较验数据  
  153.     |-->设置packet头信息  
  154.         |-->pkt.putInt(packetLen)  
  155.         |-->pkt.putLong(offset)  
  156.         |-->pkt.putLong(seqno);  
  157.         |-->pkt.put((byte)  
  158.         |-->pkt.putInt(len);  
  159. |-->checksumIn.readFully(buf, checksumOff, checksumLen);  
  160. |-->2:读取流信息  
  161. |-->int dataOff = checksumOff + checksumLen;  
  162. |-->IOUtils.readFully(blockIn, buf, dataOff, len);  |从blockIn中读取block流信息  
  163. |-->for (int i=0; i<numChunks; i++)                 |针对每个checkSum的chunk块,进行较验  
  164.     |-->checksum.update(buf, dOff, dLen);  
  165. |-->3:写流数据  
  166. |-->if (blockInPosition >= 0)  |如果blockPosition大于0,则为socketOutputSteam流  
  167.     |-->SocketOutputStream sockOut = (SocketOutputStream)out;  
  168.     |-->sockOut.write(buf, 0, dataOff);  
  169.     |-->sockOut.transferToFully(((FileInputStream)blockIn).getChannel(),   
  170.                                 blockInPosition, len);  
  171.   
  172. |-->else   
  173.     |-->out.write(buf, 0, dataOff + len);  
  174. |-->throttler.throttle(packetLen);  |调节带宽与传输流  
  175. |-->return len;  |返回读取大小  
  176.   
  177.   
  178.   
  179. 4.1.1.3 writeBlock() |写block数据流,比读要复杂,涉及到与上下datanode节点的交互  
  180. 1:读取头文件信息  
  181. |-->Block block = new Block(in.readLong(),   
  182.         dataXceiverServer.estimateBlockSize, in.readLong());  
  183. |-->int pipelineSize = in.readInt();  
  184. |-->boolean isRecovery = in.readBoolean();  
  185. |-->String client = Text.readString(in)  
  186. |-->boolean hasSrcDataNode = in.readBoolean()  
  187. |-->srcDataNode.readFields(in);    |此时为发送命令的datanode节点,srcDataNode  
  188. |-->int numTargets = in.readInt(); |共需要传递的节点数,最后一个节点就是1  
  189. |-->DatanodeInfo targets[] = new DatanodeInfo[numTargets];   
  190. |-->for (int i = 0; i < targets.length; i++) |从流当中读取DatanodeInfo信息  
  191.     |-->tmp.readFields(in);  
  192.     |-->targets[i] = tmp;  
  193.   
  194. 2:创建输入、输出流,及socket端口  
  195. |-->mirrorOut = new DataOutputStream(   |创建下一节点的输出流  
  196.              new BufferedOutputStream(  
  197.                          NetUtils.getOutputStream(mirrorSock, writeTimeout),  
  198.                          SMALL_BUFFER_SIZE));    
  199. |-->mirrorIn = new DataInputStream(NetUtils.getInputStream(mirrorSock));|创建下一节点的输入流  
  200. |-->replyOut = new DataOutputStream(    |响应上一节点的输出流  
  201.                      NetUtils.getOutputStream(s, datanode.socketWriteTimeout));   
  202. |-->Socket mirrorSock        |创建下一节点的端口号  
  203. |-->BlockReceiver blockReceiver  = new BlockReceiver(block, in, |创建block接收者,并写block数据  
  204.           s.getRemoteSocketAddress().toString(),  
  205.           s.getLocalSocketAddress().toString(),  
  206.           isRecovery, client, srcDataNode, datanode);  
  207. 3.数据传递  
  208. |-->mirrorNode = targets[0].getName();  
  209. |-->mirrorTarget = NetUtils.createSocketAddr(mirrorNode);  
  210. |-->mirrorSock = datanode.newSocket();  
  211. |-->NetUtils.connect(mirrorSock, mirrorTarget, timeoutValue);  |连接到下一节点datanode的客户端  
  212. |-->写下一节点输出流的版本等信息  
  213.       |-->mirrorOut.writeShort( DataTransferProtocol.DATA_TRANSFER_VERSION );  
  214.           |--> mirrorOut.write( DataTransferProtocol.OP_WRITE_BLOCK );  
  215.           |-->mirrorOut.writeLong( block.getBlockId() );  
  216.           |-->mirrorOut.writeLong( block.getGenerationStamp() );  
  217.           |-->mirrorOut.writeInt( pipelineSize );  
  218.           |-->mirrorOut.writeBoolean( isRecovery );  
  219.           |-->Text.writeString( mirrorOut, client );  
  220.           |-->mirrorOut.writeBoolean(hasSrcDataNode);  
  221.       |-->srcDataNode.write(mirrorOut);  |前提条件hasSrcDataNode  
  222.       |-->mirrorOut.writeInt( targets.length - 1 );  
  223.       |-->for ( int i = 1; i < targets.length; i++ )  
  224.         |-->targets[i].write( mirrorOut );  
  225.       |-->blockReceiver.writeChecksumHeader(mirrorOut); |写入检验头文件  
  226.       |-->mirrorOut.flush();  
  227.       |-->if (client.length() != 0)   
  228.         |-->firstBadLink = Text.readString(mirrorIn);  |当为client端的时候,读取ack信息  
  229.   
  230. 4.接收block数据及发送miorror镜像  
  231. |-->blockReceiver.receiveBlock(mirrorOut, mirrorIn, replyOut,  
  232.                                  mirrorAddr, null, targets.length);  
  233. |-->datanode.notifyNamenodeReceivedBlock(block, DataNode.EMPTY_DEL_HINT);  
  234. |-->datanode.blockScanner.addBlock(block);  
  235.   
  236. 4.1:接收block信息 receiveBlock()  
  237. |-->BlockMetadataHeader.writeHeader(checksumOut, checksum);  
  238. |-->responder = new Daemon(datanode.threadGroup,     
  239.                                new PacketResponder(this, block, mirrIn,   
  240.                                                    replyOut, numTargets));  
  241. |-->responder.start();  
  242. |-->while (receivePacket() > 0) {}  |接收流数据,写磁盘,每一次writeBlock只写一次磁盘  
  243. |-->mirrorOut.writeInt(0);  
  244. |-->((PacketResponder)responder.getRunnable()).close();  
  245. |-->if (clientName.length() == 0)    
  246.     |-->block.setNumBytes(offsetInBlock);  
  247.     |-->datanode.data.finalizeBlock(block);  
  248.   
  249.   
  250. 4.2:receivePacket()  |不断读packet数据至buf当中,循环至数据长度为o  
  251. |-->int payloadLen = readNextPacket();  |读取下一个packet,下述是处理和传输过程  
  252. |-->读取packet的头信息 ,然后回滚至最初位置  
  253.     |-->buf.mark();  
  254.     |-->buf.getInt()  
  255.     |-->offsetInBlock = buf.getLong()  
  256.     |-->long seqno = buf.getLong()  
  257.     |-->lastPacketInBlock = (buf.get() != 0)  
  258.     |-->int endOfHeader = buf.position();  |header头最后的位置  
  259.     |-->buf.reset();  
  260. |-->setBlockPosition(offsetInBlock);  
  261. |-->写入下一DataNode节点镜像  
  262.     |-->mirrorOut.write(buf.array(), buf.position(), buf.remaining());|整个Packet包往下传,  
  263.                                         position和remaining确定包大小  
  264.     |-->mirrorOut.flush();  |flush使之生效  
  265. |-->buf.position(endOfHeader);  |从文件头处开始处理  
  266. |-->int len = buf.getInt();     |获取data的长度初始值  
  267. |-->offsetInBlock += len;       |设置Block当中的offset值  
  268. |-->checksumLen = ((len + bytesPerChecksum - 1)/bytesPerChecksum)*checksumSize  |获取checksumLen的长度  
  269. |-->int checksumOff = buf.position();  |此时bytebuffer的初始位置已经为真实的data数据  
  270. |-->int dataOff = checksumOff + checksumLen;  |data数据的存储右端值  
  271. |-->byte pktBuf[] = buf.array();  
  272. |-->buf.position(buf.limit());  |移到数据data的末尾  
  273. |-->verifyChunks(pktBuf, dataOff, len, pktBuf, checksumOff); |验证chunk信息  
  274. |-->out.write(pktBuf, dataOff, len); |数据写本地磁盘  
  275. |-->验证chunk是否为packet  
  276.     |-->partialCrc.update(pktBuf, dataOff, len);  
  277.     |-->checksumOut.write(pktBuf, checksumOff, checksumLen);  
  278. |-->flush();  
  279.     |-->checksumOut.flush()  
  280.     |-->out.flush()  
  281. |-->responder.getRunnable()).enqueue(seqno,lastPacketInBlock) |用responder返回packet包  
  282. |-->throttler.throttle(payloadLen);  
  283.   
  284. |-->return payloadLen;  
  285.   
  286.   
  287. 5.关闭流及端口  
  288. |-->IOUtils.closeStream(mirrorOut);  
  289. |-->IOUtils.closeStream(mirrorIn);  
  290. |-->IOUtils.closeStream(replyOut);  
  291. |-->IOUtils.closeSocket(mirrorSock);  
  292. |-->IOUtils.closeStream(blockReceiver);  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值