@mircode
2017-02-28T13:28:33.000000Z
字数 5595
阅读 649
分布式组件
上一章中,我们介绍了组件的构成、组件生命周期和组件间的调用等相关内容。通过标准的组件模型,我们可以像组装积木一样,组装我的程序。但是单台主机所能提供的资源,终归是有限度的。尤其,是在互联网领域,面对庞大的互联网用户,单台主机所提供的运算能力往往是不够的。因此,我们就需要将多台主机组成庞大的集群来统一对外进行服务。这时候,标准的组件模型,已经不能够满足集群环境下的使用。这就需要我们对模型进行分布式改造,也就是这章所要介绍的内容————分布式组件。
为了应对与日俱增的用户量,我们通常会将系统,进行垂直和水平两个方向进行拆分。
垂直拆分
所谓的垂直拆分,就是将系统不同的模块,部署到不同的主机之上,从而减少单台主机的压力。例如:网站初期,用户量不是很大时,我们通常将数据库和应用程序部署在同一台服务器之上。而随着用户量的增加,我们就会将网站的应用部分和数据库部分,分别部署到不同的主机之上。如果用户量,进一步增加,那么我们可能会将应用部分,进一步垂直拆分,拆分为静态资源和动态资源两个部分,静态资源我们部署在独立的服务之上,提供对外提供标准的HTTP服务,而动态资源则独立部署在应用服务器之上,从而提升系统的整体性能。
这种垂直切分的做法,虽然能够一定程度上缓解,系统的压力。但是,系统不可能进行无限度的拆分,当用户量访问巨大时,系统已经无法在进行合理的拆分时,整个系统就进入瓶颈期。纠其根本原因在于,垂直拆分后的各个模块,都是单实例状态,某个模块遇到瓶颈,就会造成系统整体吞吐量下降。这就需要我们对架构进行水平拓展,也就是下面我们将谈到的。
水平拓展
水平拓展不同于垂直拆分的是,对于拆分出来的模块,我们会在多台服务器上部署。比如,在上面提到当用户量进一步增大时,垂直拆分已经应对不暇是,我们可以通过水平拓展的方式,将数据库和应用程序,分别部署到多台服务器之上,通过合适的负载均衡策略,分别对多个数据库实例和应用实例进行访问,达到分担系统压力的作用。假如用户量,进一步增加,我们只需要购买更多主机,然后部署新的应用就可以。这就可以做到,系统整体性能的线性增长。
与垂直拆分不同的是,要实现模块的多实例,我们需要引入负载均器这个概念。负载均衡器,承担着将用户请求分散到各个实例的作用。
分布式组件
前面介绍了两种拆分系统的手段,对于我们的分布式组件来说,组件化本身就是垂直拆分的结果,剩下我们所需要做的就是对组件进行水平的多实例的拓展。除了负载均衡器这个所必须要考虑的因素外,在实现分布式组件之前,我们还需要考虑的一点就是操作的便捷性,也就说对于分布式环境下组件的安装,卸载,启动等操作,应该和单台机器上执行的操作和命令大体一致的,因为我们不可能要求一个软件的用户,去逐台的安装组件,那将会是一个很繁琐的操作。所以,我们在开始之前,应该具备的一种思想就是,将集群中众多的主机抽象化,虚拟成一台能力巨大的计算机。我们通过这个虚拟的计算机,来完成对组件的安装,运行等操作。这个虚拟主机提供了组件的运行环境,我们并不需要关心组件具体运行在哪个主机之上,我们只需要像操作单台主机一样,操作我们这台虚拟主机即可。单节点的宕机等,对用户来说都是透明的,用户是不会觉察的。
上节中当中介绍了分布式组件的逻辑结构,这节主要介绍组件运行的实际物理环境。要做到像单机环境一样实现组件的安装,运行等操作,我们就需要一台主控机去操作集群中的所有机器,这台主控机器可以看做所有机器的代理主机。所有,从物理架构上看,整体上采用了主从的结构。下面分别介绍了两种主从结构的网络拓扑:
主从结构
主从架构是管理众多主机的一种主要手段,主节点通常会作为整个集群的控制中心,存储着集群的主要元信息。所有对集群的管理,都是通过主节点进行的。当在主控节点上执行,安装组件命令时,该命令散发给集群当中的从主机,从主机接受对应的命令,并完成对应的操作。
多主结构
主从结构很简单,但是却有个致命的缺点,那就是主控节点的单点问题。如果主控节点发生宕机或者网络不可达,那么依赖于主控节点的从节点,将无法正常工作。针对这种情况,我们就需要将主控节点多实例化。解决该问题的方式有很多,最常用的就是我可以使用Zookeeper
来实现多个主节点的架构,当一台主控机失效时,Zookeeper
会采用选举算法,该算法遵从少数服从多数的原则,从剩 余的可用主机中选择合适的主机作为新的主控节点。Zookeeper
所管理的主控节点,在某个时刻只有一个是处于活跃状态的,当活跃状态的主机宕机时,会有其他备用的主控节点进行接管。
注意:此处可以多写一些Zookeeper
的原理和机制。
这节中,我们从集群的逻辑架构和物理架构两方面描述了,组件的运行环境。物理架构讲述了,实际物理主机的网络拓扑结构。而逻辑架构,则依托于改物理架构为用户提供了一个虚拟的主机环境,同时也讲述分布式组件基本的构成。
改组件主要提供了两方面能力,一方面可以通过该组件添加和删除集群中的主机信息。构建主机资源列表,该资源列表保存了当前集群中有哪些可用的主机资源。当安装组件时,会更具该表进行实际物理主机的分配。该资源表保存在集群的元数据中。
另一方面,我们可以通过管理组件,进行全局形式,安装,运行,停止,卸载组件。和在单机环境稍微不同的是,通过该组件安装组件是,需要指明组件的实例数目。当安装组件的实例数目大于集群中的主机资源时,那么将安装等于集群个数的组件。我们可以通过改组件动态的调整集群中组件实例的个数,从而为系统提供最优的性能。比如,当用户量较少时,我们可以指定组件A
的实例个数为2;当用户量增加时,我们可以通过管理组件将组件A
的实例数目调整为10;当用户量减少时,我们可以通过管理组件将组件A
的实例数目调会2,从而节省系统资源。
改组件被集群中的所有主机依赖,提供了组件之间跨主机调用的能力。运行改组件的节点,会占用网络的一个端口,进行远程组件之间的通讯。该组件主要提供了以下三方面能力:
一方面:可以通过在组件获取对应主机节点的IP,运行着的组件详细信息。
一方面:改组件能够响应外部组件的远程调用,当接受到外部组件的远程调用请求时,网络组件会查询本机节点中的合适的组件进行调用。
一方面:改组件还监听者集群路由组件中路由表信息的变化,当路由表信息发生改变时,网络组件会获取到对应的更新并缓存路由表,当内部组件需要调用外部远程组件时,通过查询本地缓存的路由表,获取目标组件的IP地址等信息,进而发起远程调用。
路由组件存储了集群相关的源信息,并提供了组件之间寻址的能力。该件延续了单机环境下的SOA模型,但是又对该模型进行拓展。
该组件提供了两种形式的获取服务注册信息的机制:
push机制
该机制采用push推的动作,将信息主动推送给路由节点。该机制类似单机环境下的SOA模型,由服务提供者主动注册所提用的服务信息。当服务启动时,会触发服务注册事件,该事件会向路由中心注册服务IP地址等源信息。当服务停止时,会触发服务释放事件,该事件会向路由中心请求释放服务。push机制机制,能更实时的感知服务的变化。但是该机制会导致集群中的所有节点依赖于路由组件,当路由组件不可用时,服务注册和注销操作将失败,从而造成组件通讯问题。
pull机制
该机制不同于push机制,该机制采用拉的方式。由路由节点,定期获取所有节点network
组件所提供的元信息,从而构建信息表。当从节点安装,卸载组件时,路由节点并不会马上感知到变化,只有在下一个扫描周期到达时,才能感知到节点组件元信息的变化。该中模型好处,在于从节点不依赖于路由组件,从节点并不知道注册中心存在,具有高度的独立性。但是,该模型也具有不能实时反馈服务变化的缺点。
在实际应用之中,我们通常会更具实际需要,去合理使用两种机制。
路由表
无论采取什么机制,目的都是为了维护系统组件的路由表,该表存着集群组件的元信息。所以,对于改表的存储至关重要,如果路由节点不可用,那么就会造成系统组件的访问的不可达。所以,对于路由表的存储,我们交由Zookeeper
去维护,Zookeeper
通过维护多台备用主机,达到高可用性。
路由表信息如下:
组件名称 | 组件版本 | 组件状态 | IP地址 |
---|---|---|---|
组件A | 版本1 | 运行 | 192.168.0.1 |
组件A | 版本1 | 运行 | 192.168.0.2 |
组件A | 版本1 | 运行 | 192.168.0.3 |
组件B | 版本1 | 运行 | 192.168.0.1 |
组件B | 版本1 | 运行 | 192.168.0.2 |
服务获取
和单机环境稍有不同的是,分布式环境下路由表会由network
组件进行更新和维护,network
监听路由组件中路由表的变化,当路由表发生变更时,network
组件,第一时间感知变化,并更新本地路由表的缓存。当组件A对组件B发起远程调用时,如果此时缓存的路由表中,含有目标组件B的信息,则A将发起远程调用。如果路由表中,没有组件B的信息,那么A组件将等待一段时间后,继续发起调用。当尝试一定次数之后,如果B组件还是不可达,那么A组件将终止访问,并抛出系统异常。
负载均衡
因为每个组件都是多实例状态存在的,所以就需要采用合适的负载均衡算法选取一个合适的实例进行调用。
常用的负载均衡策略有:
1、轮循策略
通过对组件A的所有实例,进行简单的逐一访问,来讲请求分摊到多个实例
该算法能够保证每个实例都能均匀的访问,这在每个服务器配置环境都相同的情况下很合适,并且算法也很简单。但是,当服务器配置之间存在差异时,我们就需要根据服务器配置来分摊请求,也就是我们的加权轮循。
2、加权轮循策略
当组件运行环境存在差异时,我们可以根据服务器硬件环境,来配置为不同实例配置权重。权重越大,说明主机性能越好,获取的调用机会也会更多。加权的大小取决于服务器的配置。一般来说配置越高权重也大,但是我们也可以更加服务器负载的多少,去配置加权。
3、最少连接数策略
最少连接数,更根据每个组件实例当前的请求数,作为分摊依据。将对请求数较少的组件发起优先调用。请求压力很大的实例,则有较低的优先级。
单机环境升级
组件篇我们并没有过多深入的讲解,组件的升级和卸载操作。虽然,概念很简单,但是细节就很复杂了。当对系统中组件,进行更新时,我们就需要卸载掉之前的组件而按照新版本的组件,这个过程就有可能造成服务的中断。当我们把组件依赖之间依赖的因素考虑在内之后,就会发现更新一个组件越发的不同容易,一个组件的中断,可能造成这个系统的崩溃。所以,我们需要采用合理的机制,对组件的升级进行控制。
这里我们将升级操作拆分成两个操作,首先是安装新版的组件。当安装完新版本的组件之后,组件之间的依赖关系,任然维持老的状态,也就说安装完更新后,组件并没有立刻升级到新的版本,之所以这样做是为了下一步的重新计算组件依赖整体刷新依赖关系。当安装完新版本的组件后,我们需要执行刷新操作,改操作会重新计算改组件的依赖关系网络,并对网络中所涉及的组件,统一执行依赖关系的解析,将依赖关系统一更新为新的组件包。这样就完成了对组件的更新。
集群环境升级
对于一个分布式环境的组价更新时,我们并没有采取同时升级的策略。同时升级意味着巨大的风险,如果升级的组件存在未知BUG那就意味着要回退所有组件实例,就会中断整个集群的服务。所以对于集群环境下的的组件的升级,我们采用采用逐个替换的原则对要升级的组件进行逐一升级。期间,可以设置一个时间间隔(delay time
),指定组件实例升级的时间间隔。如果在delay time
时间段内,检测到系统异常,那说明新版本的组件存在问题,系统将终止升级,将风险控制在最低。
对于由成千上万台主机组成的集群来说,节点的宕机几乎是必然必然发生的事情。当主机发生宕机时,我们通常会排查主机宕机的原因,然后恢复对应的服务,如果主机硬件损坏,那么我们就需要将改主机所运行的软件迁移到其他主机,这种工作可以说是比较繁琐和低效的,尤其是面对大规模集群的情况下。所以,我们就需要一种机制,去自动完成对故障节点的迁移工作,因为我们的组件都是运行在标准的内核之上的,所以我就可以很容易的将故障节点之上的组件,迁移到到其他稳定的主机,从而就大大降低了软件的问题工作。
要做到这一点,我们就需要对路由表定时check
,检测组件存活的的实例数目是否和预期一致。如果发现当前组件存活的实例数目小于设定实例数目时。改check
任务并不会立即发送迁移操作,而是等待一段时间。之所以这样做是考虑到,短暂的网络中断或者异常,可能在一段时间内恢复如初。如果等待了一段时间再次check
路由表,存活的实例数目任然小于目标实例个数。此时,改check
任务就会像集群中的管理者发送,启动对应实例的的命令。当组件管理者接受到改命令时,就会查找集群中的主机资源列表,分配合适的主机,然后安装并启动对应的组件。当组件启动之后,就会像注册中心注册,此时实例数目再次达到设定的数据,从而完成了对组件的异常恢复。而这一些列的操作并不会造成系统中断,用户对则一切都是无从察觉的。
通过组件异常恢复机制,大大减弱了大规模集群下主机宕机所造成的影响,提高了软件动态适应环境的能力,大大提升了软件的柔性程度。
本节主要介绍了分布式环境下的几个主要核心部件,并且介绍了分布式组件的异常恢复和滚动升级机制。但是,对于实现一个软件分布式还需要面对很多问题,这里只做抛砖引玉,介绍几个典型的问题,更多问题探索,有待大家继续发掘。