前面章节提到,我们的分布式文件系统要追求高性能,需要从最基本的两个方面去考量:客户端连接方式:目前客户端采用短连接,每次发送请求都需要重新建立连接,频繁的建立和断开连接使得系统的性能开销很大;客户端请求方式:目前客户端采用同步请求方式,效率非常低,我们可以参考Kafka,实现异步请求及回调机制。本章,我就来对DFS客户端进行改造,支持长连接机制和异步请求机制。本章的代码存放在:https://gitee.com/ressmix/source-code/tree/master/5.dfs/6.performance,需要的读者请自行下载参阅。一、整体设计新的DFS客户端的设计主要分为两部分:连接
分布式系统的设计还有一块非常重要的考量点——高性能和高并发。高性能和高并发通常放在一起讲,高性能一般体现在系统内部的性能优化上,比如基于内存实现的数据存储,文件I/O,异步刷盘机制,锁争用,长短链接等等。对于我们的分布式文件系统来说,高性能的瓶颈主要在于客户端与DataNode的连接方式——短连接,以及文件上传方式——逐一连接重复上传。我会在后面章节,针对性能问题对客户端的网络机制和文件上传机制进行优化。本章,我们重点关注分布式文件系统的高并发架构。所谓高并发,就是说系统在单位时间内能够并发处理的DFS客户端请求应该尽量大,也就是QPS尽量大。分布式文件系统,并发瓶颈主要在于DataNode处
本章,我将对分布式文件系统的可扩展架构进行讲解。我们知道,对于一个分布式系统来说,可扩展意味着能够自由伸缩。那么,对应到我们的分布式文件系统的场景,就是:当DataNode集群的存储容量快满了以后,通过新增DataNode节点,可以实现水平动态扩容;当DataNode集群中的某个节点下线后,该节点上的文件会自动重分配到其它节点上。上述两个过程,本质就是分布式存储系统的Rebalance机制。关于第二点,我已经在高可用架构中的文件副本重分配中讲解过了,本章,我们重点关注如何实现DataNode节点的水平扩容。本节涉及得代码存放在:https://gitee.com/ressmix/source-
本章,我将对DataNode的另一类高可用问题进行讲解,即DFS客户端在文件传输过程中,如果出现中断或异常该如何处理?DFS客户端与DataNode在文件传输过程中,发生中断异常。此时,DFS客户端需要向NameNode上报异常节点的信息,然后NameNode再选择可用的DataNode让DFS重新传输。一、文件下载重传输客户端对传输中断异常的处理主要分为两种情况:文件下载重传输和文件上传重传输,我们先来看下载重传输的情况。DFS客户端下载文件出现中断时,需要做两件事情:告诉NameNode哪个DataNode出现了问题;NameNode重新选择一个可用DataNode返回给客户端,客户端重新
本章开始,我将对分布式文件系统的高可用架构进行讲解。我们的分布式文件系统,主要考虑两个地方的高可用:NameNode高可用:NameNode负责管理整个分布式文件系统的元数据,我们使用BackupNode作为它的冷备节点。当NameNode故障时,虽然可以通过editslog和fsimage快照机制保证元数据不会丢失,但是无法实现故障的自动转移,需要手动进行故障节点切换;DataNode高可用:DataNode负责文件的实际存储,我们采用了双副本机制来保障DataNode的高可用,但是如果一个DataNode节点挂掉,可能会导致一批文件只有1个副本。另外,如果DataNode在文件传输过程中出
分布式文件系统的文件存储的最后一块内容,就是实现DataNode的信息上报。我在前面章节已经讲过,DataNode信息上报分为增量信息上报和全量信息上报,增量信息上报在文件上传完成后由DataNodeNIOServer调用,全量信息上报则需要在DataNode首次注册时完成。一、信息上报我们首先来看DataNode的启动。DataNode内部封装了很多核心组件,与信息上报最相关的就是LeaseManager和StorageManager:/***DataNode启动类*/publicclassDataNode{privatevolatileBooleanisRunning;//与NameNod
上一章,我对分布式文件系统的存储架构进行了整体讲解。我们回顾一下,dfs-client客户端首先需要通过RPC调用获取DataNode节点信息,然后再基于JavaNIO完成文件的上传和下载。本章,我就来实现JavaNIO这块的功能。一、DFS客户端接口首先,我们来考虑下如何基于JavaNIO实现DFS客户端的文件上传和下载功能。DFS客户端需要提供两个接口,并整合上一章讲到的RPC调用:/***dfs客户端接口*/publicinterfaceFileSystem{/***上传一个文件**@paramfilefilebytearray*@paramfilename文件名,以如如文件分隔符开头,
从本章开始,我将对分布式文件系统的文件上传功能进行讲解。我们的分布式文件系统的大脑是NameNode,它负责管理整个集群的元数据,而真正负责数据存储的是DataNode。所以,我们在设计文件存储功能时,需要重点考虑以下几个方面的内容:如何在DataNode集群中分布式地存储各种小文件?如何保证DataNode的可用性?NameNode如何感知DataNode集群的信息?客户端如何与NameNode、DataNode通信,完成文件的上传和下载?解决了上述四个问题,我们的分布式文件系统的存储架构也就浮现出来了。本章,我先以文件上传和下载功能为例,讲解分布式文件系统的文件存储全流程,后续章节再带领大
本章,我将对BackupNode的启停与元数据恢复机制进行讲解,主要分为两部分内容:BackupNode的元数据恢复机制;BackupNode的优雅停机机制。一、元数据恢复BackupNode会定期生成fsimage快照和checkpoint信息保存到磁盘上,同时定期从NameNode拉取EditsLog日志,如果BackupNode突然宕机,内存中的元数据就会丢失。所以,BackupNode在启动时需要查找磁盘中的fsimage快照文件和checkpoint文件,恢复内存文件目录树,从checkpoint之后开始拉取editslog日志。元数据由FSNameSystem管理,所以需要在FSN
上一章,我对BackupNode的checkpoint机制进行了深入剖析,还遗留了fsimage快照传输没有讲解。BackupNode在生成完fsimage快照文件后,需要基于NIO网络通信将fsimage文件传输给NameNode。关于NIO的网络通信机制,我在《透彻理解Kafka》系列中深入讲解过,本节我就来仿照Kafka的通信组件实现fsimage快照传输的BackupNode客户端/NameNode服务端的代码。一、快照传输首先,fsimage快照传输是通过BackupNode的FSImageUploader组件来完成,FSImageCheckPointer在执行checkpoint机
BackupNode从NameNode复制了EditsLog后,会生成内存文件目录树,并且会定期生成一个fsimage文件快照。所谓fsimage文件快照,本质就是一个基于元数据的持久化文件:BackupNode每触发一次checkpoint机制,都会执行以下操作:生成一个fsimage快照并持久化保存;将fsimage文件发送给NameNode;将本次checkpoint的信息发送给NameNode;本地持久化保存checkpoint信息。BackupNode会把checkpoint信息通过RPC方式发送给NameNode,NameNode记录最新的checkpoint信息后,就知道万一自己
NameNode负责管理分布式文件系统的元数据,但本身是单点,所以为了保证可用性,我们还需要一个BackupNode节点,从NameNode同步EditsLog日志,并回放日志生成内存文件目录树:本章,我们先来看BackupNode是如何从NameNode同步EditsLog日志的?在设计BackupNode节点的同步机制时,我们需要考虑以下方面的内容:采用pull模式还是push模式同步editslog;采用gRPC还是自研NIO组件同步editslog;批量拉取机制;NameNode侧的缓存机制。一、设计思路1.1pull模型事实上,大多数分布式中间件都会采用pull模式同步数据,比如Ka
EditsLog日志,就是NameNode用来记录文件操作命令的日志。NameNode每次对内存文件目录树进行增删改时,都必须将操作记录到editslog日志中。这样即使NameNode宕机了,重启NameNode后也可以读取日志进行回放,恢复出一份完整的元数据。一、写日志NameNode在持久化EditsLog日志时,如果直接写磁盘,那么性能必然很差,所以一般都会先写缓存,然后异步刷盘。但是,因为EditsLog会先在内存中缓存,所以如果有些Log还没刷入磁盘,此刻NameNode就宕机了,会导致内存中的部分数据丢失。当然,你也可以使用同步刷盘模式,不过性能会大幅下降。很多开源框架,比如El
内存文件目录树,在一些开源的分布式存储系统中,有时也叫做Namespace或者元数据。它是NameNode节点的一种记录客户端文件操作命令的抽象数据结构,维护在NameNode的自身内存中。在上一章《dfs客户端》中,我提到NameNodeRpcServer在接受到Client的文件操作RPC请求后,会委托给FSNameSystem处理:publicclassNameNodeServiceImplextendsNameNodeServiceGrpc.NameNodeServiceImplBase{//负责管理元数据的核心组件privateFSNameSystemnamesystem;/***创
dfs-client,也就是分布式文件系统的客户端,需要提供各种文件/目录操作的命令,比如目录创建,文件上传/下载等等,然后实现跟分布式文件系统的通信。本章,我以"目录创建"命令为例,来实现客户端文件操作,客户端的操作接口我定义在FileSystem接口中:一、客户端接口首先,我们需要定义好gRPC接口。1.1RPC接口存根我直接修改dfs-rpc工程src/main/proto目录下的NameNodeServiceProto.proto文件,新增一个“目录创建”接口,然后执行mvncleancompile命令生成新的接口存根:syntax="proto3&quo
DataNode节点启动后,会基于RPC请求立即向NameNode注册自身信息,注册成功后,还会发送心跳进行保活。NameNode接受到DataNode的请求后,会在内存中维护DataNode信息,定期检测并剔除长时间未发送心跳的DataNode节点。本章,我就来实现分布式文件系统的DataNode节点注册与心跳的核心逻辑。DataNode发送注册和心跳请求是基于一个名为NameNodeConnService的组件来实现的,如下图(服务注册请求):NameNode接受到注册/心跳请求后,会在内存中维护DataNode的信息,委托组件DataNodeManager来实现,如下图(服务心跳请求):
本章,我将对dfs-rpc这个模块进行讲解。该模块依赖gRPC,存放各个RPC服务的存根。我们的分布式文件系统的服务间的调用需要以gRPC作为通信组件,所以本章我会讲解gRPC的基本使用,但我不会对gRPC作深入介绍,gRPC的底层原理和各种高阶用法,读者可以参考其它资料。一、dfs-rpc工程gRPC本身支持不同语言,通过ProtoBuf编写的.proto文件,然后使用不同语言的编译器,可以生成特定语言相关的模板代码。由于我们的工程使用Java,所以需要使用grpc-java。Protobuf是一种语言中立、平台无关、可扩展的序列化数据的格式,可用于通信协议,数据存储等。我们的系统中涉及的服
本章,我还是和之前的其它专栏一样,先从整体架构上讲解我们自研的分布式文件系统,后续再逐一拆分讲解每个核心组件,即整体的思路依然延续“自顶向下”的架构讲解结合"自底向上"的模块实现。通过本章的学习,你将对该分布式文件系统的整体架构有一个清晰的认识,同时我会对落地分布式文件系统的重要技术点在本章进行概要讲解。注意:本系列我讲解的分布式文件系统只适用于大量小文件的分布式存储,并不是HDFS那种单个大文件的拆分存储,在实现上大量借鉴了Hadoop的设计,事实上,很多开源框架在数据存储这一块的设计思想都是类似的。一、整体架构整个分布式文件系统,我将它划分为四个部分:客户端:客户端集成