在复杂纷繁的分布式环境中, 我们坚定的相信,万事皆有可能。哪怕各个服务器都舒舒服服的活着,也可能有各种各样的情况导致网络传输中的数据丢失或者错误。并且在分布式文件系统中,同 一份文件的数据,是存在大量冗余备份的,系统必须要维护所有的数据块内容完全同步,否则,一人一言,不同客户端读同一个文件读出不同数据,用户非得疯了不 可。。。
在HDFS中,为了保证数据的正确性和同一份数 据的一致性,做了大量的工作。首先,每一个数据块,都有一个版本标识,在
Block 类中,用一个长整型的数
generationStamp 来 表示版本信息(Block类是所有表示数据块的数据结构的基类),一旦数据块上的数据有所变化,此版本号将
向前增加 。 在主控服务器上,保存有此时每个数据块的版本,一旦出现数据服务器上相关数据块版本与其不一致,将会触发相关的恢复流程。这样的机制保证了各个数据服务器 器上的数据块,在基本大方向上都是一致的。但是,由于网络的复杂性,简单的版本信息无法保证具体内容的一致性(因为此版本信息与内容无关,可能会出现版本 相同,但内容不同的状况)。因此,为了保证数据内容上的一致,必须要依照内容,作出
签名 。。。
当客户端向数据服务器追加写入数据包时,每一个 数据包的数据,都会切分成
512字节 大小的段,作为签名验证的基本单位,在HDFS中,把这个数据段称为
Chunk, 即传输块 (注意,在GFS中,Chunk表达的是数据块...)。在每一个数据包中,都包含若干个传输块以及每一个传输块的签名,当 下,这个签名是根据Java SDK提供的
CRC 算法算得的,其实就是一个奇偶校验。当数据包传输到流水线的
最 后一级 ,数据服务器会对其进行验证(想一想,为什么只在最后一级做验证,而不是每级都做...),一旦发现当前的传输块签名与在客户端 中的签名不一致,整个数据包的写入被视为无效,
Lease Recover(租约恢复)算法 被触发。。。
从基本原理上看,这个算法很简单,就是
取 所有数据服务器上此数据块的最小长度当作正确内容的长度,将其他数据服务器上此数据块超出此长度的部分切除 。从正确性上看,此算法无疑 是正确的,因为至少有一个数据服务器会发现此错误,并拒绝写入,那么,如果写入了的,都是正确的;从效率上看,此算法也是高效的,因为它避免了重复的传输 和复杂的验证,仅仅是各自删除尾部的一些内容即可。但从具体实现上来看,此算法稍微有些绕,因为,为了降低本已不堪重负的主控服务器的负担,此算法不是由 主控服务器这个大脑发起的,而是通过选举一个数据服务器作为
Primary ,由Primary发起,通过调用与其他各 数据服务器间的
InterDatanodeProtocol 协议,最终完成的。具体的算法流程,参见
LeaseManager 类 上面的注释。需要说明的是此算法的触发时机和发起者。此算法可以由
客户端 或者是
主控服务器 发 起,当客户端在写入一个数据包失败后,会发起租约恢复。因为,一次写入失败,不论是何种原因,很有可能就会导致流水线上有的服务器写了,有的没写,从而造 成不统一。而主控服务器发起的时机,则是在占有租约的客户端超出一定时限没有续签,这说明客户端可能挂了,在临死前可能干过不利于数据块统一的事情,作为 监督者,主控服务器需要发起一场恢复运动,确保一切正确。。。