分布式文件系统的事务控制(5)

如果说事务控制在数据库中是有时候用到,在普通的文件系统中是偶尔要处理。那么在分布式文件系统中,事务控制是每一个数据操作都要关注的内容。当然,有很多的现成的系统给我们提供了很多的资源。

分布式文件系统是一个快速发展的概念,目前还没有形成类似ORACLE在数据库中的地位,因此,关于这块,参考了一些网络上的内容,泛泛而谈。Hadoop分布式文件系统(HDFS)在JAVA环境中使用相对比较多,用自己的半桶水水平进行解读。

部分文字来自网络,出处也找不到。 :(

在分布式环境中,有太多的意外,数据随时传输错误,服务器时刻可能当即,很多平常称为异常的现象,在这里都需要按照平常事来对待。因此,对于分布式文件系统而言,仅仅是满足了正常状况下文件系统各项服务还不够,还需要保证它在各种意外场景下健康持续的服务。

1、分布式文件系统中服务器的错误和恢复

在分布式环境中,服务器宕机是常见的事情。HDFS有三类服务器,每一类服务器出错了,都有相应的应急策略。

客户端

客户端做为分布式文件系统的使用者,在整个系统中,地位也是最低的;只有当它在写入数据的时候出现异常(机器挂了,或者网络断了,系统挂了…),才会对整个系统造成一定的影响。此刻,这个挂掉的客户端已经锁定了这个文件,让这个文件无法被其他人写入或者维护,这个事情的解决办法是:租约。

租约,顾名思义,就是当客户端需要占用某文件的时候,与主控服务器签订的一个短期合同。这个合同有一个期限,在这个期限内,客户端可以延长合同期限,一旦超过期限,主控服务器会强行终止此租约,将这个文件的享用权,分配给他人。。。

在打开或创建一个文件,准备追加写之前,会调用LeaseManager的addLease方法,在指定的路径下与此客户端签订一份租约。客户端会启动DFSClient.LeaseChecker线程,定时轮询调用ClientProtocol的renewLease方法,续签租约。在主控服务器一端,有一个LeaseManager.Monitor线程,始终在轮询检查所有租约,查看是否有到期未续的租约。如果一切正常,该客户端完成写操作,会关闭文件,停止租约,一旦有所意外,比如文件被删除了,客户端牺牲了,主控服务器都会剥夺此租约,如此,来避免由于客户端停机带来的资源被长期霸占的问题。

数据服务器

在分布式文件系统中,海量的数据服务器随时有可能出现异常。一旦某数据服务器宕机,如果主控服务器不知道这个情况,系统就会给客户端提供虚假的服务。

数据服务器必须时刻向主控服务器汇报,保持主控服务器对其的完全了解,这个机制,就是心跳消息。在HDFS中,主控服务器NameNode实现了DatanodeProtocol接口,数据服务器DataNode会在主循环中,不停的调用该协议中的sendHeartbeat方法,向NameNode汇报状况。在此调用中,DataNode会将其整体运行状况告知NameNode,比如:有多少可用空间、用了多大的空间,等等之类。NameNode会记住此DataNode的运行状况,作为新的数据块分配或是负载均衡的依据。当NameNode处理完成此消息后,会将相关的指令封装成一个DatanodeCommand对象,交还给DataNode,告诉数据服务器什么数据块要删除什么数据块要新增等等之类,数据服务器以此为自己的行动依据。

但是,sendHeartbeat并没有提供本地的数据块信息给NameNode,那么主控服务器就无法知道此数据服务器应该分配什么数据块应该删除什么数据块,那么它是如何决定的呢?答案就是DatanodeProtocol定义的另一个方法,blockReport。DataNode也是在主循环中定时调用此方法,只是,其周期通常比调用sendHeartbeat的更长。它会提交本地的所有数据块状况给NameNode,NameNode会和本地保存的数据块信息比较,决定什么该删除什么该新增,并将相关结果缓存在本地对应的数据结构中,等待此服务器再发送sendHeartbeat消息过来的时候,依照这些数据结构中的内容,做出相应的DatanodeCommand指令。blockReport方法同样也会返回一个DatanodeCommand给DataNode,但通常,,只是为空(只有出错的时候不为空),我想,增加缓存,也许是为了确保每个指令都可以重复发送并确定被执行。。。

主控服务器

主控服务器作为整个系统的核心和单点,一旦主控服务器瘫痪,整个分布式文件服务集群将彻底瘫痪罢工。在主控服务器当即后,通过日志进行提拔新的主控服务器并迅速使其进入工作角色

使用日志来进行系统恢复和回滚,是分布式文件系统进行事物控制的重要环节。

在主控服务器上,所有对文件目录操作的关键步骤(具体文件内容所处的数据服务器,是不会被写入日志的,因为这些内容是动态建立的…),都会被写入日志。另外,主控服务器会在某些时刻,将当下的文件目录完整的序列化到本地,这称为镜像。一旦存有镜像,镜像前期所写的日志和其他镜像,都纯属冗余,其历史使命已经完成,可以报废删除了。在主控服务器不幸牺牲,或者是战略性的停机修整结束,并重新启动后,主控服务器会根据最近的镜像 + 镜像之后的所有日志,重建整个文件目录,迅速将服务能力恢复到牺牲前的水准。

对于数据服务器而言,它们会通过一些手段,迅速得主控服务器的更迭消息。它们会立刻向新的主控服务器其发送心跳消息。

在HDFS的实现中,FSEditLog类是整个日志体系的核心,提供了一大堆方便的日志写入API,以及日志的恢复存储等功能。目前,它支持若干种日志类型,都冠以OP_XXX,并提供相关API,具体可以参见这里。为了保证日志的安全性,FSEditLog提供了EditLogFileOutputStream类作为写入的承载类,它会同时开若干个本地文件,然后依次写入,防止日志的损坏导致不可估量的后果。在FSEditLog上面,有一个FSImage类,存储文件镜像并调用FSEditLog对外提供相关的日志功能。FSImage是Storage类的子类,如果对数据块的讲述有所印象的话,你可以回忆起来,凡事从此类派生出来的东西,都具有版本性质,可以进行升级和回滚等等,以此,来实现产生镜像是对原有日志和镜像处理的复杂逻辑。

目前,在HDFS的日志系统中,有些地方与GFS的描述有所不同。在HDFS中,所有日志文件和镜像文件都是本地文件,这就相当于,把日志放在自家的保险箱中,一旦主控服务器挂了,别的后继而上的服务器也无法拿到这些日志和镜像,用于重振雄风。因此,在HDFS中,运行着一个SecondaryNameNode服务器,它做为主控服务器的替补。其中,核心内容就是:定期下载并处理日志和镜像。SecondaryNameNode看上去像客户端一样,与NameNode之间,走着NamenodeProtocol协议。它会不停的查看主控服务器上面累计日志的大小,当达到阈值后,调用doCheckpoint函数,此函数的主要步骤包括:

首先是调用startCheckpoint做一些本地的初始化工作;

然后调用rollEditLog,将NameNode上此时操作的日志文件从edit切到edit.new上来,这个操作瞬间完成,上层写日志的函数完全感觉不到差别;

接着,调用downloadCheckpointFiles,将主控服务器上的镜像文件和日志文件都下载到此候补主控服务器上来;

并调用doMerge,打开镜像和日志,将日志生成新的镜像,保存覆盖;

下一步,调用putFSImage把新的镜像上传回NameNode;

再调用rollFsImage,将镜像换成新的,在日志从edit.new改名为edit;

最后,调用endCheckpoint做收尾工作。

人的一辈子唯一做的就是,不断地用你手中

分布式文件系统的事务控制(5)

相关文章:

你感兴趣的文章:

标签云: