您好,欢迎访问三七文档
当前位置:首页 > IT计算机/网络 > 数据挖掘与识别 > 高并发服务端分布式系统设计概要
高并发服务端分布式系统设计概要======张峻崇原创。转载请注明出处。======又是快一年没写博客了,2013年也只剩尾巴,也不知道今年都忙了些什么。写这篇文章的目的,主要是把今年以来学习的一些东西积淀下来,同时作为之前文章《高性能分布式计算与存储系统设计概要》的补充与提升,然而本人水平非常有限,回头看之前写的文章也有许多不足,甚至是错误,希望同学们看到了错误多多见谅,更欢迎与我讨论并指正。我大概是从2010年底起开始进入高并发、高性能服务器和分布式这一块领域的研究,到现在也差不多有三年,但其实很多东西仍然是一知半解,我所提到的许许多多概念,也许任何一个我都不能讲的很清楚,还需要继续钻研。但我们平时在工作和学习中,多半也只能从这种一知半解开始,慢慢琢磨,不断改进。好了,下面开始说我们今天要设计的系统。这个系统的目标很明确,针对千万级以上PV的网站,设计一套用于后台的高并发的分布式处理系统。这套系统包含业务逻辑的处理、各种计算、存储、日志、备份等方面内容,可用于类微博,SNS,广告推送,邮件等有大量线上并发请求的场景。如何抗大流量高并发?(不要告诉我把服务器买的再好一点)说起来很简单,就是“分”,如何“分”,简单的说就是把不同的业务分拆到不同的服务器上去跑(垂直拆分),相同的业务压力分拆到不同的服务器去跑(水平拆分),并时刻不要忘记备份、扩展、意外处理等讨厌的问题。说起来都比较简单,但设计和实现起来,就会比较困难。以前我的文章,都是“从整到零”的方式来设计一个系统,这次咱们就反着顺序来。那我们首先来看,我们的数据应该如何存储和取用。根据我们之前确定的“分”的方法,先确定以下2点:(1)我们的分布式系统,按不同的业务,存储不同的数据;(2)同样的业务,同一个数据应存储多份,其中有的存储提供读写,而有的存储只提供读。好,先解释下这2点。对于(1)应该容易理解,比如说,我这套系统用于微博(就假想我们做一个山寨的推特吧,给他个命名就叫“山推”好了,以下都叫山推,Stwi),那么,“我关注的人”这一个业务的数据,肯定和“我发了的推文”这个业务的数据是分开存储的,那么我们现在把,每一个业务所负责的数据的存储,称为一个group。即以group的方式,来负责各个业务的数据的存储。接下来说(2),现在我们已经知道,数据按业务拆到group里面去存取,那么一个group里面又应该有哪些角色呢?自然的,应该有一台主要的机器,作为group的核心,我们称它为GroupMaster,是的,它就是这个group的主要代表。这个group的数据,在GroupMaster上应该都能找到,进行读写。另外,我们还需要一些辅助角色,我们称它们为GroupSlaves,这些slave机器做啥工作呢?它们负责去GroupMaster处拿数据,并尽量保持和它同步,并提供读服务。请注意我的用词,“尽量”,稍后将会解释。现在我们已经有了一个group的基本轮廓:一个group提供对外的接口(废话否则怎么存取数据),group的底层可以是实际的FileSystem,甚至是HDFS。GroupMaster和GroupSlave可以共享同一个FileSystem(用于不能丢数据的强一致性系统),也可以分别指向不同的FileSystem(用于弱一致性,允许停写服务和系统宕机时丢数据的系统),但总之应认为这个FileSystem是无状态,有状态的是GroupMaster和各个GroupSlave。下面来说一个group如何工作,同步等核心问题。首先,一个group的GroupMaster和GroupSlave间应保持强一致性还是弱一致性(最终一致性)应取决于具体的业务需求,以我们的“山推”来说,GroupMaster和GroupSlave并不要求保持强一致性,而弱一致性(最终一致性)即能满足要求,为什么?因为对于“山推”来讲,一个GroupMaster写了一个数据,而另一个GroupSlave被读到一个“过期”(因为GroupMaster已经写,但此GroupSlave还未更新此数据)的数据通常并不会带来大问题,比如,我在“山推”上发了一个推文,“关注我的人”并没有即时同步地看到我的最新推文,并没有太大影响,只要“稍后”它们能看到最新的数据即可,这就是所谓的最终一致性。但当GroupMaster挂掉时,写服务将中断一小段时间由其它GroupSlave来顶替,稍后还要再讲这个问题。假如我们要做的系统不是山推,而是淘宝购物车,支付宝一类的,那么弱一致性(最终一致性)则很难满足要求,同时写服务挂掉也是不能忍受的,对于这样的系统,应保证“强一致性”,保证不能丢失任何数据。接下来还是以我们的“山推“为例,看看一个group如何完成数据同步。假设,现在我有一个请求要写一个数据,由于只有GroupMaster能写,那么GroupMaster将接受这个写请求,并加入写的队列,然后GroupMaster将通知所有GroupSlave来更新这个数据,之后这个数据才真正被写入FileSystem。那么现在就有一个问题,是否应等所有GroupSlave都更新了这个数据,才算写成功了呢?这里涉及一些NWR的概念,我们作一个取舍,即至少有一个GroupSlave同步成功,才能返回写请求的成功。这是为什么呢?因为假如这时候GroupMaster突然挂掉了,那么我们至少可以找到一台GroupSlave保持和GroupMaster完全同步的数据并顶替它继续工作,剩下的、其它的GroupSlave将“异步”地更新这个新数据,很显然,假如现在有多个读请求过来并到达不同的GroupSlave节点,它们很可能读到不一样的数据,但最终这些数据会一致,如前所述。我们做的这种取舍,叫“半同步”模式。那之前所说的强一致性系统应如何工作呢?很显然,必须得等所有GroupSlave都同步完成才能返回写成功,这样GroupMaster挂了,没事,其它GroupSlave顶上就行,不会丢失数据,但是付出的代价就是,等待同步的时间。假如我们的group是跨机房、跨地区分布的,那么等待所有GroupSlave同步完成将是很大的性能挑战。所以综合考虑,除了对某些特别的系统,采用“最终一致性”和“半同步”工作的系统,是符合高并发线上应用需求的。而且,还有一个非常重要的原因,就是通常线上的请求都是读写,这也正是“最终一致性”符合的应用场景。好,继续。刚才我们曾提到,如果GroupMaster宕机挂掉,至少可以找到一个和它保持同不的GroupSlave来顶替它继续工作,其它的GroupSlave则“尽量”保持和GroupMaster同步,如前文所述。那么这是如何做到的呢?这里涉及到“分布式选举”的概念,如Paxos协议,通过分布式选举,总能找到一个最接近GroupMaster的GroupSlave,来顶替它,从而保证系统的可持续工作。当然,在此过程中,对于最终一致性系统,仍然会有一小段时间的写服务中断。现在继续假设,我们的“山推”已经有了一些规模,而负责“山推”推文的这个group也有了五台机器,并跨机房,跨地区分布,按照上述设计,无论哪个机房断电或机器故障,都不会影响这个group的正常工作,只是会有一些小的影响而已。那么对于这个group,还剩2个问题,一是如何知道GroupMaster挂掉了呢?二是在图中我们已经看到GroupSlave是可扩展的,那么新加入的GroupSlave应如何去“偷”数据从而逐渐和其它节点同步呢?对于问题一,我们的方案是这样的,另外提供一个类似“心跳”的服务(由谁提供呢,后面我们将讲到的GlobalMaster将派上用场),group内所有节点无论是GroupMaster还是GroupSlave都不停地向这个“心跳”服务去申请一个证书,或认为是一把锁,并且这个锁是有时间的,会过期。“心跳”服务定期检查GroupMaster的锁和其有效性,一旦过期,如果GroupMaster工作正常,它将锁延期并继续工作,否则说明GroupMaster挂掉,由其它GroupSlave竞争得到此锁(分布式选举),从而变成新的GroupMaster。对于问题二,则很简单,新加入的GroupSlave不断地“偷”老数据,而新数据总由于GroupMaster通知其更新,最终与其它所有结点同步。(当然,“偷”数据所用的时间并不乐观,通常在小时级别)高并发服务端分布式系统设计概要(中)上篇我们完成了在此分布式系统中,一个group的设计。那么接下来,我们设计系统的其他部分。如前文所述,我们的业务及其数据以group为单位,显然在此系统中将存在manymany的groups(别告诉我你的网站总共有一个业务,像我们的“山推”,那业务是一堆一堆地),那么由谁来管理这些groups呢?由Web过来的请求,又将如何到达指定的group,并由该group处理它的请求呢?这就是我们要讨论的问题。我们引入了一个新的角色——GlobalMaster,顾名思义,它是管理全局的一个节点,它主要完成如下工作:(1)管理系统全局配置,发送全局控制信息;(2)监控各个group的工作状态,提供心跳服务,若发现宕机,通知该group发起分布式选举产生新的GroupMaster;(3)处理Client端首次到达的请求,找出负责处理该请求的group并将此group的信息(location)返回,则来自同一个前端请求源的该类业务请求自第二次起不需要再向GlobalMaster查询group信息(缓存机制);(4)保持和GlobalSlave的强一致性同步,保持自身健康状态并向全局的“心跳”服务验证自身的状态。现在我们结合图来逐条解释上述工作,显然,这个系统的完整轮廓已经初现。首先要明确,不管我们的系统如何“分布式”,总之会有至少一个最主要的节点,术语可称为primarynode,如图所示,我们的系统中,这个节点叫GlobalMaster,也许读过GFS+Bigtable论文的同学知道,在GFS+Bigtable里,这样的节点叫ConfigMaster,虽然名称不一样,但所做的事情却差不多。这个主要的GlobalMaster可认为是系统状态健康的标志之一,只要它在正常工作,那么基本可以保证整个系统的状态是基本正常的(什么?group或其他结点会不正常不工作?前面已经说过,group内会通过“分布式选举”来保证自己组内的正常工作状态,不要告诉我group内所有机器都挂掉了,那个概率我想要忽略它),假如GlobalMaster不正常了,挂掉了,怎么办?显然,图中的GlobalSlave就派上用场了,在我们设计的这个“山推”系统中,至少有一个GlobalSlave,和GlobalMaster保持“强一致性”的完全同步,当然,如果有不止一个GlobalSlave,它们也都和GlobalMaster保持强一致性完全同步,这样有个好处,假如GlobalMaster挂掉,不用停写服务,不用进行分布式选举,更不会读服务,随便找一个GlobalSlave顶替GlobalMaster工作即可。这就是强一致性最大的好处。那么有的同学就会问,为什么我们之前的group,不能这么搞,非要搞什么最终一致性,搞什么分布式选举(Paxos协议属于既难理解又难实现的坑爹一族)呢?我告诉你,还是压力,压力。我们的系统是面向日均千万级PV以上的网站(“山推”嘛,推特是亿级PV,我们千万级也不过分吧),但系统的压力主要在哪呢?细心的同学就会发现,系统的压力并不在GlobalMaster,更不会在GlobalSlave,因为他们根本不提供数据的读写服务!是的,系统的压力正是在各个group,所以group的设计才是最关键的。同时,细心的同学也发现了,由于GlobalMaster存放的是各个group的信息和状态,而不是用户存取的数据,所以它更新较少,也不能认为读写,这是不成立的,所以,GlobalSlave和GlobalMaster保持强一致性完全同步,正是最好的选择。所以我们的系统,一台Globa
本文标题:高并发服务端分布式系统设计概要
链接地址:https://www.777doc.com/doc-3467272 .html