class DFSOutputStream extends FSOutputSummer implements Syncable{}
private Socket s; //客户端与副本复制流水线中第一个存储节点的连接
boolean closed = false; //写操作是否关闭
private String src; // 文件路径
private DataOutputStream blockStream; // 写入数据的IO流
private DataInputStream blockReplyStream; //读取数据包确认的IO流
private Block block; private Token
private DataChecksum checksum; //数据校验和
private LinkedList
private LinkedList
private Packet currentPacket = null;
private int maxPackets = 80; // each packet 64K, total 5MB
// private int maxPackets = 1000; // each packet 64K, total 64MB
private DataStreamer streamer = new DataStreamer(); //数据包发送
private ResponseProcessor response = null; //数据包确认接受,一个数据块对应一个确认处理线程
private long bytesCurBlock = 0; // bytes writen in current block
private int packetSize = 0; // write packet size, including the header.
private int chunksPerPacket = 0; //一个数据包最多包含多少个校验块
private DatanodeInfo[] nodes = null; // list of targets for current block
private ArrayList
private volatile boolean hasError = false; private volatile int errorIndex = 0;//出错的数据节点
private volatile IOException lastException = null; private long artificialSlowdown = 0;
private long lastFlushOffset = 0; // offset when flush was invoked
private boolean persistBlocks = false; // persist blocks on namenode
private int recoveryErrorCount = 0; // number of times block recovery failed
private int maxRecoveryErrorCount = 5; // try block recovery 5 times
private volatile boolean appendChunk = false; // appending to existing partial block
private long initialFileSize = 0; // at time of file open private Progressable progress;
private short blockReplication; // replication factor of file
private class Packet {
客户端向第一个存储节点发送的数据是按照数据包的形式来组织的,以此来提高网络IO的效率。
ByteBuffer buffer;
byte[] buf; // 数据缓存区
long seqno; // 数据包在数据块中的序列号
long offsetInBlock; // 数据包在数据块中的偏移位置
boolean lastPacketInBlock; // 是否是数据块的最后一个数据包
int numChunks; // 数据包当前存放了多少个校验块
int maxChunks; // 数据包最多可有多少个校验块
int dataStart; // 数据在该数据包中的开始位置
int dataPos; // 当前的数据写入位置
int checksumStart; // 校验数据在该数据包中的开始位置
int checksumPos; // 当前的校验数据写入位置
方法有
writeData:将数据写入到缓冲区中 writeChecksum:将checksum写入到缓冲区中 getBuffer:将数据从buf拷贝到bufferDataStreamer把dataQueue队列中的packet发送到目标数据节点上。
private class DataStreamer extends Daemon {
DataStreamer从namenode取回块的id和位置,并将packet发送给datanode。当所有的packet发送完毕,并收到每个块的ack,DataStreamer关闭当前块。当没有Block或申请的一个Block已满时,它会调用ClientProtocol的addBlock远程方法向NameNode申请一个LocatedBlock,也就是要知道它应该要把这个Block的packet发送到那些DataNode节点上。另外,用户不能一味的发送数据,否则缓存扛不住,所以就有一个限制了,也就是总的缓存数据不能超过maxPackets个packet。
客户端都要确认每一个数据包是否被所有的存储节点所真确接受了,如果有一个存储节点没有真确接受,则客户端就需要立即回复当前数据块。
private class ResponseProcessor extends Thread {
ResponseProcessor负责处理datanode返回的应答,当一个packet的应答到达时,该packet从ackQueue中删除。关键的属性有如下这些:
closed:ResponseProcessor是否关闭
targets:目标datanode,每个packet只有收到targets所表示的所有datanode的ack才算数据发送成功
lastPacketInBlock:是否是块的最后一个packet