在几十年前,一个独立的计算机上往往部署着一套完整的应用系统。当时因为网络稳定性及速度的限制,将相关联的服务部署在一台机器上,让它们使用系统机制通信——比如管道,文件等,往往是最稳定和最高效的。然而随着网络技术的发展,特别是“云计算”和“大数据”的盛行,我们越来越多的谈到“分布式系统”——建立在网络上的软件系统。这样部署在不同机器上的关联服务就依赖于网络以及它们自定义的“协议”和“机制”来构建出更加健壮和系统。之前在单机时代,“机制”是受限于操作系统;而网络时代,“机制”将变得更加自由——不同厂商可以定义自己的“机制”以让分布式系统运转起来。(转载请指明出于breaksoftware的csdn博客)

        我们先闭眼冥想下:如果我们去构建分布式系统,需要考虑些什么问题?

  1. 网络通信协议。这个难度相对低些,我们忽略。
  2. 异常处理方案。这个就非常复杂了,异常有来自网络的,有来自系统的,有来自用户错误的使用……
  3. 分布式系统维护方案。如果系统内部发生机器增删行为,怎么处理?如果某台机器变得不稳定了,怎么办?……
  4. CAP。除了上述,我们还会遇到CAP定律(https://en.wikipedia.org/wiki/CAP_theorem):一致性(Consistency),可用性(Availability)和分区容错性(Partition Tolerance)最多只能同时满足两个。

        简单的说,就是在一个分布式系统中,如果发生网络故障,我们无法同时满足强一致性和可用性:

  • 如果要满足强一致性。请求落在故障机器上时,要一直等待该机器和其他机器同步完最新数据后才能返回。这就牺牲了可用性。
  • 如果要满足可用性。请求落在故障机器上后立即返回该机器上的数据,但是其数据可能不是最新的。这就牺牲了强一致性。

        可见设计一套分布式系统还是要面临很多问题,而且我们设计往往会结合自身业务特征,从而增加了重用的难度。如果每个公司都设计出自己的一套,其场景就像动物园里的动物:琳琅满目。虽然这对看客来说是件好事,但是增加动物园的管理负担——比如喂养老虎的饲养员可能不会喂养兔子,需要雇佣更多的员工或者培训现有的员工。这个时候,一个超级饲养员出现了——zookeeper——动物园(zoo)饲养员(keeper)。

        zookeeper设计了一套通用“机制”,提供了少量简单的API,可以让我们便捷的构建分布式协作系统。依然以动物饲养为例,一个饲养员如果是要给兔子去做饭,就可以用zookeeper提供的切割机去切草——多切几次变得比较细;他要是去给老虎做饭,还可以使用zookeeper提供的切割机去切肉——只切一次让老虎可以大口吃肉。

        zookeeper为什么大受欢迎?我觉得除了方案具有通用性外,还和其设计特点有关——将复杂的问题简单化,把抽象的问题实例化。

        zookeeper并没有让各种信息逻辑糅杂在一个服务中。设想下,如果我们将业务逻辑、分布式系统维护逻辑都放在下面一台机器上,则要求每台机器互联,而且一旦发生某台机器断网,其他机器都将要处理这样的异常。这样看似服务部署变得简单,但是会导致服务内部比较杂乱。

        zookeeper则采用“分角色”的方案。在一个经典的zookeeper系统中,业务代码只在Zookeeper Client中,这样开发者只要专心于Client上的业务逻辑实现就行了,而分布式系统中大部分异常可以交由zookeeper提供的Zookeeper Server服务去解决。

        那么Client和Server间交互的是什么信息呢? 我们称这样的信息叫做“元信息”。元信息存在于Server上,而Client端通过网络向Server端进行读写,从而达到“分布式协同”的功能。于是元信息的设计关系了整个系统的复杂程度。

        zookeeper设计的元信息结构和我们日常使用的文件目录系统很像。这样我们在遇到“分布式协同难题”时,可对照着“文件目录系统”这样“老知识”去梳理和解决,这无疑大大降低了理解的难度。

        不同的Client可以同时读写同一个节点信息。Zookeeper Server可以保证信息要么被全部写入,要么写失败。而信息读取方,也只能一次性读到全部数据,而不可以读取部分数据。所以这种类似“原子性”的特性,可以让Client设计者不用去关心脏数据的问题。

        Client还可以向Server端注册对节点变动的监视。这样就免除了Client端采用轮训这种低效的模式。这和操作系统提供的监控文件变动功能也很像。当然Zookeeper设计的这套结构自然有自己的特点,比如临时节点、有序节点、节点版本等,这些我们在之后的章节会介绍。

        跳出这些微观的设计,我们再看下基于zookeeper的分布式系统整体结构,发现Zookeeper Server之间的并非相互连接的。如果我们让不同Server之间相互连接,会使得每个Server的设计变得非常复杂,因为它要兼顾到每个和其他Server的连接信息。

        zookeeper做了一个这样的设计:选举“领导”(leader)。这就像我们选举领导一样,一个集群里有选举权的员工可以相互交流推选出一个领导,落选的自然成为“追随者”(follower)。被推选为“领导”的服务负责维护各个Zookeeper Server的一致性和可用性。比如一个连接到“追随者”的Client提交了一个修改信息的请求,“追随者”会将“修改”提交给“领导”,“领导”要将这个修改同步给其他“追随者”。这样就达到整个系统的信息的一致性。为了让“领导”可以专心做自己的事,所以不太建议Client端直接连接到Leader服务。

        在大部分场景下,只有“领导”和“追随者”是足够的。但是如果这个集群的需要承载比较大的请求量,我们就需要增加机器,也就是增加“追随者”。这样虽然解决了问题,但是会带来另外一个问题——太多的“追随者”需要更多的时间去选举“领导”,而且这个耗时会导致系统一段时间内部分功能不可用,最最关键的是这个时耗可能很长。于是我们就剥夺了一些“追随者”的选举权,让它们成为“观察员”。这样在Leader失效时,只有少部分Follower进行投票选举,从而降低了系统不可用的时间。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐