架构解密分布式到微服务:架构实践DIY一个有难度的分布式集群

DIY一个有难度的分布式集群

本节的目标,是不依赖任何分布式框架和中间件,全程“手工”设计、开发一个有难度的分布式集群MyCluster,以提升自我的硬核架构能力,具体的技术要求如下。

●不使用任何分布式相关的框架和中间件,不限语言,纯手工编码打造一个原生的分布式集群MyCluster, MyCluster 集群主要用于实现某种简单的分布式计算功能。

●MyCluster 总体来说属于-种主从结构,Master 节点负责整个集群的控制,收集用户发出的任务MyTask, 并且将其派发到整个集群; MyCluster 节点的规模是事先确定好的,在配置好以后,节点可能因为宕机而下线,但不会增加新的未知节点。

●MyTask如果任务执行失败,则可能涉及重新派发,部分任务可能有状态数据需要恢复。

●Master节点可能会宕机,此时需要选举新的Master节点,旧的Master节点恢复后,也不会取代新选择的Master节点。

MyCluster集群中的节点共享- -个配置, 个别参数可能不同,比如监听端口、线程池数量这些与特定机器和环境相关的参数。对MyCluster 集群的配置(文件)需要集中化管理,即意味着集中在一台机器上,集群配置文件可以回归到上一个版本。

从上面的要求来看,MyCluster 是目前主流的一种分 布式集群,具有很高的容错性和分布式任务协调能力。接下来我们一起看看如何设计这个系统的架构。

首先,我们需要解决第一个难题,即如何设计集群的架构并实现故障情况下的自动主从切换?我们注意到MyCluster有一个特点“不会增加新的未知节点”,这个特点很重要,也是区分常规集群与P2P集群的关键因素。有了这个前提,我们不需要引入复杂的算法就可以解决集群的Master节点宕机后新任Leader 节点的选举问题了。

具体逻辑:每个节点都有唯- -的 名称,名称可以按照字母表排序,规定集群中排在前面的节点是Leader.假如节点的名称为A、B、 C、D、E、F,则A为Leader节点;在A宕机后,推举B接替A;在集群中其余活着的节点都确认新Leader节点之后,集群恢复正常的工作机制。

这里可能会遇到一个经典的脑裂问题,如下图所示,在A宕机后,整个集群被分成左右两部分,其中左边的B与C互联互通,右边的D、E、F互联互通,但两个小集群直接互联互不通,这就是集群分裂后的脑裂现象。

架构解密分布式到微服务:架构实践DIY一个有难度的分布式集群_第1张图片

在脑裂发生后,为了避免两个集群都独立工作而导致数据不一致,一 般的做法是要么大家都原地等待不再工作,要么选择规模最大的集群继续提供服务,其他集群则等待。对于集群节点数量不定的系统,这是一个难题,因为每个集群都无法确定谁是规模最大的,但在节点数量确定的情况下,这件事情就很简单了;集群拥有的节点数量超过一半 的就是规模最大的集群。

在上述情况下,若A宕机,则在有5个存活节点的情况下,右边的D-E-F集群胜出。

这里再总结一下集群的Leader选举问题:按照配置文件,每个节点都知道整个集群的节点地址,所以在节点启动后进入Leader选举状态。在这种状态下,我们要求节点编号小的节点主动与编号比它大的节点都发起通信链接,快速建立起网状通信网络。以编号最小的节点A为例,拓扑图如下所示。

架构解密分布式到微服务:架构实践DIY一个有难度的分布式集群_第2张图片

此时,每个节点都会根据与之相连的邻居节点列表,对照配置文件中的节点信息来判断自己是否是“老大”一即活着的最小编号节点。比如图中的A在连上周围的节点后,发现当前在线的节点数量超过集群节点数量的- -半,自己也是配置中编号最小的节点,就判断自己是Master节点,并且下发“加冕申请报文”给周围的节点,周围的节点在收到报文后确认回复报文,并记录当前的Master节点是A,然后集群状态进入工作状态。各个节点开始定期汇报自己的资源使用情况、任务执行情况给Master节点,并接受Master节点的统一领导。 如果某一时刻A宕机,此时每个节点都发现与A的通信被意外中断,则由于A是Master 节点,所以集群立刻重新进入Leader选举状态,重复启动时的选举机制。B此时当选下一任Leader节点,在集群选举完成后,如果A恢复,它就会主动连接其他节点,每个节点的回复报文都会告知A目前的集群状态为工作状态,并且Master节点是B,于是A成为普通的Slave节点,在B宕机以后,A才能恢复为Master节点。

从上面的分析过程来看,集群有3种状态,如下图所示,初始状态为Leader-Select状态,在集群选举成功时进入Working状态,在集群选举失败时(集群分裂中的小集群)进入Standby状态。

架构解密分布式到微服务:架构实践DIY一个有难度的分布式集群_第3张图片

其次,我们看看另一个关键需求:在配置文件方面,整个集群都需要共享一份配置文件,配置文件中的部分配置还能实时生效。这里其实是要实现一个简化的配置中心,并且需要保证数据一致性的实现。 我们可以把配置文件分为两部分:一部分类似于P2P集群的种子文件,不同的是,这里的配置文件包括集群所有节点的编号、IP和端口号(以及其他可能的鉴权文件如证书、密码),这个文件是每个节点在本地保留的,基本不变;另一部分则是需要集中存储并动态分发到各个节点的共享配置文件,由Master节点负责更新内容并下发到各个节点,所以各个节点在收到共享配置文件后,都需要将该文件保存到本地磁盘做备份,在系统启动时,Master节点将其在本地保存的最新共享配置文件下发到集群的各个节点。另外,我们为共享配置文件增加了版本号和日期属性,在修改配置后,版本号增加。这样就比较方便回退版本并可能降低配置文件在集群中的传输频率了。

如果你觉得自己学习效率低,缺乏正确的指导,可以加入资源丰富,学习氛围浓厚的技术圈一起学习交流吧!
[Java架构群]
群内有许多来自一线的技术大牛,也有在小厂或外包公司奋斗的码农,我们致力打造一个平等,高质量的JAVA交流圈子,不一定能短期就让每个人的技术突飞猛进,但从长远来说,眼光,格局,长远发展的方向才是最重要的。

为了方便更新和共享配置文件,Master 节点可以增加管理命令update config, 该命令可以将用户指定的配置文件上传到Master 节点,由Master 节点负责将配置文件的内容下发到各个Slave节点并生效。为了提高可靠性,可以模仿XA二阶段的提交过程,具体设计如下。

(1) Master 节点将新版本的配置文件传输到各个Slave节点。

(2)Slave节点在检查配置的正确性并保存到本地以后回复报文给Master节点。

(3) Master 节点在确定所有Slave节点都成功应答后,下 达commit config指令。

在收到comnmit: config指令后,若个别节点无法成功升级到新版本,则可以考虑此节点在输出错误日志后,立即停止工作,避免集群陷入不一致的工作状态。在Master节点通知各个节点自己是Leader节点的报文中,我们可以增加Master节点当前的配置文件版本号,在Slave节点回复的报文中也增加其配置文件版本号,这样一来, Master 节点就有机会纠正因某些特殊原因导致的集群配置文件不-一样的问题了。在有了版本号以后,各个节点是否要拉取新版本的配置文件的内容,也就更加清晰了。如下所示为配置文件相关设计的示意图。

架构解密分布式到微服务:架构实践DIY一个有难度的分布式集群_第4张图片

最后,我们看看集群通信协议的设计问题。

这里可以采用TCP长连接的设计思路,报文可以用比较传统的二进制格式的报文结构。为了方便快速解析报文的类型,我们需要为每种报文都规定一种类型。此外,可能存在“异步”响应的问题,所以我们需要在报文中增加报文序号,方便匹配请求与应答报文,还可以增加协议版本号以兼容未来的协议升级。因此,报文的结构就可以定义如下。

架构解密分布式到微服务:架构实践DIY一个有难度的分布式集群

报文的长度被定义为short类型(无符号的short,即最大长度为65535个字节),这是为了避免因为不确定的超大报文导致网络编程中的大内存问题。这里最大的报文是配置文件,配置文件一般不会超过65535个字节。上述报文结构清晰简单,也方便解析和对应请求报文与响应报文。下面继续看看会涉及哪些种类的报文,以及这些报文的具体结构。

先看集群选举时的报文。首先是HelloMessage报文,节点在启动后主动向比自己编号大的节点发起连接,在连接建立后双方交换的第一个报文就是HelloMessage。在报文里面包括的信息如下:

●节点ID;

●节点的启动、运行时长:

●集群状态;

●当前的MasterID;.

●配置文件版本。

如下图所示,节点A首先向节点B发出包括自己信息在内的HelloMessage报文,节点B在收到报文后发出自己的HelloMessage报文响应,这样节点A. B都清楚了对方的情况,也通过对方了解到当前集群的状态。

架构解密分布式到微服务:架构实践DIY一个有难度的分布式集群_第5张图片

节点A在与其他节点都连接完成并交换了信息,确定当前是Leader选举状态并且自己是新的Leader节点后,再发出LeaderDeclare报文,报文的内容如下:

  • Master节点的ID;
  • 目前确定的在线节点数量;
  • 启用的配置文件版本号。

在收到Leader候选人发出的上述报文后,每个节点都设置集群状态为“正常”,同时设置Master节点为A,并回复报文FollowKing,报文内容如下:

  • 节点ID;
  • 是否要Master节点下发新的配置文件的内容。

节点A在收到所有节点的响应后,即完成集群Leader选举,集群完全进入正常状态,并根据节点的需求,主动下发新的配置文件的内容。如果此时有部分节点恰好宕机,则节点A需要重新计算当前节点总数是否满足超过半数的条件,如果不满足,则发出WaitInPlace 报文,让大家“原地等待”,此时集群进入Standby 状态,但Master节点仍然是A。WaitnPlace 报文的内容如下:

●节点ID;

●目前确定的在线节点数量。

WaitInPlace对应的回复报文是ImWaiting。结合集群状态与相关报文的示意图如下。

架构解密分布式到微服务:架构实践DIY一个有难度的分布式集群_第6张图片

Master节点在运行过程中监测到节点上下线后,可能会触发集群的以下状态变化。

●处于Standby状态的集群,因为节点上线,所以可能重新引发集群选举从而恢复Working状态。

●处于Working状态的集群,可能因为节点下线,引发Leader选举状态,选举的结果是: 可能产生新的Leader 节点或者原来的Leader节点,集群恢复正常或者进入Standby 状态。

如果集群处于Working 状态,则在新的节点加入后,通过邻居发送的HellMessage报文就可以知道当前的Leader节点,获取最新的配置文件并成功加入集群。此外,HelloMessage 报文负责集群心跳监测,节点彼此需要定时发送HelloMessage报文来表明自己是在线状态。

HelloMessage还可以加入一-些字段来上报节点的资源使用情况:空闲内存、CPU使用率、磁盘使用率、空余磁盘的大小、带宽使用率。

接着,我们分析- -下配置文件相关的报文,配置文件的报文ConfigMessage 比较简单,只要包括如”下信息即可:

●版本号;

●是否压缩, 在内容超长时可以压缩传输;

●字符串方式的配置文件的内容。

也可以考虑把压缩算法放入消息报文中,这样可方便升级新的压缩算法。在更新配置文件时,由Master节点主动推送ConfigMessage报文,在收到新的ConfigMessage报文后节点才开始组装并启动其他组件。

最后,我们看看MyCluster集群应该有哪些基本的命令控制报文,建议有如下几种:

●节点下线;

●集群拓扑报告;

●更新集群配置文件。

这些报文也相对简单,所以这里不再一一给 出。

我们还需要一个管理集群的客户端,有两种做法:一种是开发传统的CLI命令行客户端,与Master节点建立网络链接,通过发送相关报文来实现管理命令;另一种是在节点内部直接实现一个简单版的Web Server, 通过浏览器管理集群,这也是比较流行的思路。这里也建议采用后一种做法实现,可以将这些接口定义为REST接口,方便第三方程序集成。最终,整个MyCluster集群的完整架构如下图所示。

架构解密分布式到微服务:架构实践DIY一个有难度的分布式集群_第7张图片

最后

给大家分享一篇一线开发大牛整理的java高并发核心编程神仙文档,里面主要包含的知识点有:多线程、线程池、内置锁、JMM、CAS、JUC、高并发设计模式、Java异步回调、CompletableFuture类等。

文档地址:一篇神文就把java多线程,锁,JMM,JUC和高并发设计模式讲明白了

码字不易,如果觉得本篇文章对你有用的话,请给我一键三连!关注作者,后续会有更多的干货分享,请持续关注!

你可能感兴趣的:(分布式,大数据,编程语言,hadoop,数据库)