说明
DataTransferProtocol.readBlock给出了读操作的定义,最终实现是在DataXceiver.readBlock().
- DataXceiver.readBlock首先给客户端一个响应,给出DN的校验方式
- 数据块分包
- 依次发送给客户端
- 客户端校验
- 失败,选择新的数据节点
- 成功,客户端发送checkSum_OK
客户端清楚的知道访问那一个DN,发送请求。DN中的DataXceiverServer接收请求,构建DataXceiver。执行readBlock方法。
DataXceiver ReadBlock
public void readBlock(block,blockToken,clientName,blockOffset,length,sendChecksum,cachingStrategy);
输入参数
- ExtendedBlock blk
读取的块 - clientName
客户端名称 - blockOffset
读取数据长度 - length
是否发送校验和 - 缓存策略
readahead,dropbehind
预读取和丢弃策略
DataXceiver使用其中的ReadBlock方法进行实际的执行
ReadBlock方法
- getOutputStream获取客户端IO流,构建BlockSender信息。
构造Blocksender需要客户端IO流,需要datanode节点的bp信息。 - BlockSender通过XX方法发送XX给客户端,通知客户端请求已经接收成功,告知数据节点校验信息
- BlockSender方法发送数据块给客户端
- BlockSender发送完所有块之后,客户端响应状态码
数据块传输格式
磁盘数据读取之后,需要一定的组织结构。BlockSender发送数据结构如下:
校验和包括
- 校验类型
- 块大小
数据包序列 - 包头
- 校验数据
- 实际数据
BlockSender实现
作用是数据块发送,包括 发送准备,发送数据块,以及清理工作。
发送准备
构造方法
- 处理预读取数据
- 赋值和校验
datanode数据被读取时间戳,数据块的长度等状态 - transferTo
短路读相关 - 校验信息
Meta信息中获取 - 计算offset和endset
看看是不是在块的边界,如果是的话,修改下起始和中止位置。
getInputStream时,可以指定block和offset
预读取和丢弃
manageOsCache
读取数据到操作系统缓存中
通过读取offset和lastCacheDropOffset判断是否丢弃
- 预读条件
开启了预读,具有资源readaheadpool资源等。
发送数据块
- manageOsCache进行预读
- 构造存放数据包的缓冲区(packet)
如果是短路读的话,值缓冲校验。如果是iostream,需要缓存数据和校验和。一个packet可以包含最多X个校验块 - 循环发送,循环预读
使用sendpacket发送 - 更新offset
发送空包作为结束
发送数据包主要是通过sendPacket方法
sendPacket
- 校验缓冲区数据
- 写入pkt缓存
- 发送数据块
零拷贝读的话,直接transferToFully(getChannel,position,len,time,transfertime)中
outStream时,out.write(buf,XXX) - 调整节流器
控制发送速率
零拷贝数据传输
读取数据时
磁盘到内核(步骤1)
内核到进程(步骤2)
跨内核推回到套接字缓冲区(步骤3)
写入网卡缓冲区 (步骤4)
需要网卡支持,调用java nio方法
transferto
节流器(throttle)
为了控制IO操作,在sendpacket最后的时候,使用了节流器控制。
他就是一个线程安全的, 多个任务线程共享的阻塞线程,满足条件放行,否则阻塞。
包含字段
- period
- curReserve
等等
小结
readBlock 客户端发送请求, DataXceiverServer接收,构造工作线程DataXceiver
调用readBlock方法和客户端交互
构造BlockSender进行具体的发送准备工作(校验,分割,预读),清理工作等
调用sendpacket进行数据发送,通过节流器IO阻塞控制