2020/12/22 sunhaiqi@bonc.com.cn
文章目录
HDFS的短路读机制
一、背景
Hadoop的一个重要思想就是移动计算,而不是移动数据。我们更愿意尽可能将计算移动到数据所在节点。因此,HDFS中经常出现客户端和数据在一个节点上,当客户端读取一个数据块时,就会出现本地读取。例如HBase场景,ResionServer写数据一般在HDFS中都会存储三备份副本并且肯定会往本地节点写一备份,当ResionServer读取该数据时也会优先选择同一节点的数据进行读取。
二、短路读的演进
2.1、网络读
最初,HDFS中本地读取的处理方式和远程读取相同,也是通过网络读来实现。客户端通过TCP套接字连接到DataNode,并通过DataTransferProtocol协议传输数据(如下图):
这种方式简单,但有明显的问题:
DataNode必须为每个正在读取数据块的客户端保留一个线程和一个TCP套接字。内核中会有TCP协议的开销,以及DataTransferProtocol协议的开销,因此这里有很大的优化空间。
2.2、HDFS-2246 不安全短路读
短路读关键思想是因为客户端和数据块在同一节点上,所以DataNode不需要出现在读取数据路径中。而客户端本身可以直接从本地磁盘读取数据。这样会使读取性能得到很大的提高。在HDFS-2246中实现的短路读是DataNode将所有数据块路径的权限开放给客户端,客户端直接通过本地磁盘路径来读取数据,见下图:
但这种方式引入了很多问题:
- 系统管理员必须更改DataNode数据目录的权限,以允许客户端打开相关文件。将能够使用短路读的用户专门列入白名单,不允许其他用户使用。通常,这些用户也必须放在特殊的Unix组中。
- 这些权限更改会引入了一个安全漏洞,具有读取DataNode节点上数据块文件权限的用户可以任意读取路径上所有数据块,而不仅仅是他们所需访问的数据块,这好像让用户变成了超级用户,对于少数用户来说可能是可以接受的,例如HBase用户。但总的来说,它会带来很大安全隐患。
2.3、HDFS-347 安全短路读
HDFS-2246的主要问题是向客户端打开了DataNode的数据目录,而我们真正需要读取的只是一部分数据块文件。Unix有一种机制是可以做到这一点,称为文件描述符传递。HDFS-347使用这种机制来实现安全短路读。DataNode不是将目录传递给客户端,而是打开块文件和元数据文件,并将它们的文件描述符通过domain socket传递给客户端(如图3):
基于以下两方面,安全短路读解决了HDFS-2246存在的安全性问题。
- 文件描述符是只读的,因此客户端无法修改传递描述符的文件。
- 客户端无法访问数据块目录本身,所以也无法读取它不应该访问的任何其他数据块文件。
三、HDFS安全短路读
3.1、短路读共享内存
了解了HDFS短路读的演进,我们来看下HDFS是如何实现安全短路读的。DataNode将短路读副本的文件描述符传给DFSClient,DFSClient缓存副本文件描述符。由于副本的状态可能随时发生改变,所以需要DFSClient和DataNode实时同步副本状态。同时,DFSClient和DataNode在同一台机器上,共享内存可以通过POSIX提供的 mmap接口实现将文件映射到内存,并且映射数据是实时同步的(如图4),所以共享内存可以维护所有短路读副本的状态,使得DFSClient和DataNode通过共享内存来实时同步副本信息。
共享内存会有很多槽位,每个槽位对应一个短路读副本的信息。共享内存保存了所有槽位的二进制信息。但是映射数据中的二进制槽位信息不便于管理,所以定义了Slot对象操作映射数据中的一个槽位,如下图:
Slot槽位大小是64字节,槽位数据格式,前4字节是Slot标志位,5到8字节是锚计数位,剩余字节保留将来使用,例如统计信息等。
两个标志位:
- VALID_FLAG:表示槽位是否有效。
DFSClient在共享内存中分配新的槽位时设置此标志位。当与此槽位关联的副本不再有效时,DataNode将会消除此标志位。DFSClient本身也会消除此槽位,认为DataNode不再使用此槽位进行通信。
- ANCHORABLE_FLAG:表示槽位对应的副本是否已经缓存。
DataNode将槽位对应的副本通过POSIX提供的mlock接口缓存时会设置该标志位。当标志位已设置,DFSClient短路读取该副本时不再需要进行校验,因为副本缓存时已经做了检验操作,并且这种副本还支持零拷贝读取。DFSClient对这样的副本进行读取时,需要在对应的槽位锚计数加1,只有当槽位的锚计数为0时,DataNode才可以从缓存中删除此副本。
共享内存段的最大是8192字节,当DFSClient进行大量短路读时, DFSClient和DataNode之间可能会有多段共享内存。HDFS中DFSClient定义了DFSClientShm类抽象了DFSClient端一段共享内存,DFSClientShmManager类管理所有的DFSClientShm,而DataNode端定义了RegisteredShm类抽象DataNode端的一段共享内存,ShortCircuitRegistry类管理所有DataNode端的共享内存,如下图所示:
在安全短路读中,DFSClient和DataNode是通过domain socket来同步共享内存槽位信息的。
-
DFSClient申请一段共享内存保存短路读副本的状态。DataNode会创建共享内存,并将共享内存文件映射到DataNode内存中,并创建RegisteredShm管理这段共享内存,之后会将共享内存文件的文件描述符通过domain socket返回给DFSClient。
-
DFSClient根据文件描述符打开共享内存文件,将该文件映射到DFSClient的内存中,并创建DfsClientShm对象管理这段共享内存。
-
DFSClient通过domain socket向DataNode申请数据块文件以及元数据文件的文件描述符,并且同步共享内存中slot槽位的状态。DFSClient会在DfsClientShm管理的共享内存中为数据块申请一个slot槽位,之后通过domain socket向DataNode同步信息,DataNode会在RegisteredShm管理的共享内存中创建相应的slot槽位,然后获取数据块文件以及元数据文件的文件描述符,并通过domain socket发送给DFSClient,见下图:
3.2、短路读流程
当客户端执行数据块副本短路读时,DFSClient与DataNode的交互过程如下:
-
DFSClient通过requestShortCircuitShm()接口向DataNode请求创建共享内存,DataNode创建共享内存文件并将共享内存文件描述符返回给DFSClient。
-
DFSClient通过allocShmSlot()接口申请共享内存中的槽位,并通过requestShortCircuitFds()接口向DataNode请求要读取的副本文件描述符,DataNode打开副本文件并将数据块文件和元数据文件的文件描述符返回给DFSClient。
estShortCircuitFds()接口向DataNode请求要读取的副本文件描述符,DataNode打开副本文件并将数据块文件和元数据文件的文件描述符返回给DFSClient。
- DFSClient读取完副本后,异步通过releaseShortCircuitFds()接口向DataNode请求释放文件描述符及相应槽位。