精华内容
下载资源
问答
  • 分布式交易系统设计 7 分布式

    千次阅读 2015-08-06 22:53:34
    分布系统总是在某种场景下需要确定顺序、决出主次的,此时就要用到分布式锁。通常来说这种行为开销比较大,在应用处理逻辑中应该较少地使用。近年来,由于Google对Paxos的成功实现-Chubby,以及Hadoop家族的...

    癸巳年正月初三。本篇博文是本次“癸巳春节分布技术系列”的尾声--介绍分布式锁。分布系统总是在某种场景下需要确定顺序、决出主次的,此时就要用到分布式锁。通常来说这种行为开销比较大,在应用处理逻辑中应该较少地使用。近年来,由于Google对Paxos的成功实现-Chubby,以及Hadoop家族的ZooKeeper的广泛应用,现在搞分布系统的技术工作者如果不了解Paxos算法,可能都不好意思和人打招呼。本文不能免俗,会将Chubby和ZooKeeper的原理做概况介绍,然而在提及Pasox算法之前,我还会介绍一种更传统的分布式锁模型DLM,这种模型在规模较小的分布系统的共享文件系统中得到了很好的应用,也在一些分布式交易系统中发挥着重要的作用。

    -WJF,2013年2月12日

    (三)分布式锁

    1、传统分布式锁

    分布式锁管理器DLM最早由VMS操作系统提供。首次出现在Digitial于1982年发布的VMS版本3中。从1984年发布的VMS版本4之后,其对用户提供的应用编程接口基本确定。2006年,开源操作系统Linux的版本2.6.19内核正式集成了与VMS DLM接口几乎一致的DLM。同年,Google通过论文公开了其分布式锁服务Chubby的设计。之后,Yahoo公开了其分布式锁服务ZooKeeper的源代码。

     

    Linux上的DLM需要基于Corosync机群引擎才能运行。DLM为整个集群中所有计算机提供一个统一的、共享的锁。每个计算机运行一个锁管理核心守护进程,这些守护进程之间相互通信,维护一个集群范围内的“数据库”,管理锁资源和拥有这些锁资源的锁。

     

    在这个集群范围内的“数据库”,锁管理器维护一份每个锁资源的主拷贝。初始化时,主拷贝驻留在锁初始请求的计算机上。之后的运行过程中,主拷贝可能驻留在任意一个计算机中。为此,锁管理器维护一个集群范围的目录,说明集群内所有锁资源的主拷贝的位置。同时,锁管理器试图均匀地把这个目录分布到所有的集群计算机节点上。这种设计下,当一个应用申请一个锁或者锁资源,锁管理器首先决定哪一个计算机节点拥有该锁资源的目录条目,之后通过读目录条目找出哪一个计算机节点拥有该锁资源的主拷贝。

     

    通过允许所有计算机都能维护锁资源的主拷贝的方式,当锁请求可以在本地处理时,降低了采用一个集群只在一个计算机上设计一个锁管理器可能带来的网络通信流量,以及减少了在设备故障发生后重建锁数据库所需的时间,并提高了锁请求处理的吞吐量。

     

    当一个计算机发生故障,运行在其他计算机上的锁管理器释放该故障计算机所持有的锁。之后,锁管理器处理之前被该故障计算机阻塞的其他计算机的锁请求。同时,其他计算机会对故障计算机之前维护的主拷贝进行重新分配。

     

    锁管理器通过锁资源的名称来区分锁。一个锁资源可以与多个锁关联,既在锁资源与锁之间具有一对多的关系。锁管理器提供6种上锁模式,通过转换动作来在不同模式之间进行升级或者降级,并且提供同步和异步执行的编程接口。

     

    锁具有如下若干种模式:空NL(Null),这个模式并不对资源进行访问,通常用来作为对资源“感兴趣”的标志,或者作为未来锁转换的占位符。并发读CR (ConcurrentRead),表示对资源进行读访问,并且允许其他访问者对该资源也进行共享读和写。这个模式通常用来进行细粒度的锁操作,或者从一个资源进行“不保护”的读取。并发写CW(ConcurrentWrite),表示对资源进行写访问,并且允许其他访问者对该资源也进行共享写。这个模式通常用来进行细粒度的锁操作,或者向一个资源进行“不保护”的写入。保护读PR(ProtectedRead),表示对资源进行保护读,其他访问者对该资源可进行共享读,但是不可写。保护写PW(ProtectedWrite),表示对资源进行保护写,允许其他访问者用并发读模式来共享读。但是不可写。专属EX(Exclusive),表示对资源进行独占,任何其他访问者都不可访问。

     

    这些模式的等级从最松到最严的关系是:NL;CR;CW、PR;PW;EX。其中CW和PR的等级是相同的。新申请的锁可以与原来的锁共享被称为“兼容”。

     

     

    对于一个调用进程,锁状态表明请求某个锁的当前状态。锁管理器根据新请求的模式与该锁资源上的其他模式之间的关系来确定锁状态。锁可能处在如下3个状态:已授予-锁请求成功,达到请求的模式。转换中-新模式与旧模式不兼容。已阻塞-新模式与旧模式冲突。锁管理器还有一个功能,可通过“锁取值块”在不同计算机节点之间共享一小块全局数据。

     

    锁管理器在内部设计时,把锁资源定义为一个可上锁的实体,该实体包括一个名字:资源名;一块内存:锁取值块;三个队列:授予队列、转换队列、等待队列。锁管理器在应用程序第一次申请锁时创建锁资源实体,在最后一个锁被释放时释放锁资源实体。

     

    对于指定的锁资源,如果当前没有任何一把锁与之相关,或者新申请的模式与该资源授予队列中所有锁中最严格模式都兼容,同时转换队列为空,那么锁管理器就把锁授予申请者,并把该锁增加到授予队列中。

     

    对已经拥有的锁,可以发出改变模式的请求,请求即可以从低等级模式向高等级模式转换,也可从高等级模式向低等级模式转换。需要注意的是,只有已经授予的锁才可以进行转换,不允许转换正在转换中的锁或者被阻塞在等待队列中的锁。

     

    在模式从低等级向高等级转换时,如果请求转换的目标模式与授予队列中所有锁中最严格模式兼容,同时没有阻塞的转换请求等候在转换队列中,那么锁管理器就授予该请求者锁。如果请求转换的模式与授予队列中已经授予的最严格模式不兼容,那么将该锁从授予队列移到转换队列的队尾。

     

    对于指定的锁资源,如果有锁位于转换队列中,那么在处理该资源相关锁的所有的向高等级转换的请求时,直接将该申请锁从授予队列移动到转移队列的队尾,并且不再判断申请转换的模式是否与授予队列中最严格的模式兼容。

     

    一把锁进入转换状态后,如果发生下列三种情况之一,状态会改变为授予或者取消:(1)申请该锁的进程中止,(2)原申请进程取消转换请求,(3)请求模式与授予队列中最严格模式都兼容且排在队列之前的申请都已经授予或者取消。

     

    在模式从高等级向低等级转换时,转换过程非常简单,由于新申请模式必然和授予队列中最严格模式兼容,所以锁管理器不去判断转换队列中是否有其他锁,而是直接修改授予队列中对应锁的模式。之后,锁管理器会按照先进先出的原则处理转换队列和阻塞队列中的锁,检查是否可以授予(由于优先级关系,一旦队列中遇到一个锁不可以授予,那么检查过程中止)。

     

    如果请求转换的目标模式与授予队列中所有锁中最严格模式不兼容,那么请求被阻塞,锁管理器把阻塞的锁从授予队列移入阻塞队列的队尾。一把锁进入阻塞状态后,如果发生下列三种情况之一,会停止等待:(1)申请该锁的进程中止,(2)原申请进程取消转换请求,(3)请求模式与授予队列中最严格模式都兼容,转换队列没有排队的锁,阻塞队列内没有排在该锁之前的锁。

     

    2、Paxos锁

     

    Paxos协议是Leslie Lamport于1990年提出的一种针对分布式系统,基于消息传递的一致性算法。Leslie Lamport 在1985年到2001年间曾就职于开发VMS系统的Digitial公司和Compaq公司。Paxos协议是高可用且去中心化的,系统被视作由一组完全对等的节点组成,这组节点各自就某一事件做出决议,如果某个决议获得了超过半数节点的同意则生效。也就是说Paxos协议中只要有超过一半的节点正常,就可以工作,所以能够应对宕机、网络分化等异常情况。

     

    Paxos算法中把专门节点按照角色分为三类:提议者(Proposer)、接收者(Acceptor)和学习者(Learner)。提议者提出提案值(ProposalValue),告诉系统接下来处理什么指令。接收者批准提案,半数接收者批准才算通过,通过之后的提案称为决议(AgreedValue)。学习者获取并使用已经通过的决议,学习者也必须至少读取半数以上接收者的数据才算学习到一个决议。这三类角色只是逻辑上的,系统中的一个物理节点可以同时充当这三类角色。需要满足三个条件就能保证整个系统数据的一致性:(1)决议只有在提议者提出后才能批准(2)每次只批准一个决议(3)只有决议确定被批准后学习者才能获取这个决议。在批准决议时,采用少数服从多数的原则,也就是大多数接收者接收的决议才能成为最终正式的决议。这里隐含着集合论的一个原则,两组多数派至少有一个公共的接收者。

     

    Paxos算法被分成了两个阶段:准备阶段和批准阶段。准备阶段中,提议者选择一个提案并把编号(Proposal Number)设为n,然后发给接收者。接收者收到后,如果提案的编号大于它已经回复的所有消息,则接收者将自己上次的批准消息,也称为承诺(Promise)消息,回复给提议者,并不再批准小于n的提案。批准阶段中,当提案者接收到接收者中的多数节点的回复后,就向回复承诺消息的接收者发送接收(Accept)请求消息。在符合接收者一方的约束条件下,接收者收到这个接收请求消息后即批准这个请求,并发回一个接收响应(Accepted)消息。为了减少决议发布过程中的消息量,接收者将这个通过的决议发送给学习者的一个子集,然后由这个子集中的学习者去通知所有其他的学习者。有一种特殊情况,如果两个提议者在遇到编号更大的提案后,都转而提出一个编号更大的提案,那么就有可能进入“活锁”。此时,需要选举出一个总统(President),仅允许总统提出提案。

     

    (1)Chubby

     

    与1982年即现雏形的DLM不同,Chubby通过Paxos协议对节点进行主从选择。一旦某个节点获得了超过半数的节点认可,该节点成为主(Primary)节点,其余节点成为从(Secondary)节点。在选举出主节点后,所有读写操作都由主节点控制,系统从一个完全对等的去中心化状态变为一个主从的中心化状态。这样,可以在一个无中心的分布式系统上,实现中心化的副本控制协议。

     

    Chubby除了实现Paxos协议之外,还采用了租约(Lease)机制。1989年斯坦福大学的CaryG.Gray 和 David R.Cheriton提出了利用租约来维护缓存一致性的方法,通过使用它,可以确保非拜占庭式的失效只会影响到性能,但是不会破坏正确性,同时通过使用短租约可以将这种影响减少到最低。租约机制中定义租约以及租约的颁发者和持有者,在颁发者和持有者之间建立约定的一致承诺。特别要注意的是,持有者在约定的有效期满后,一定不能继续使用颁发者的承诺。

     

    租约机制考虑了分布系统中复杂的网络故障与节点故障场景。即便持有者由于网络分化没有收到租约,颁发者也会在约定时间内执行自己的承诺。如果在持有者收到租约后出现网络分化,也不影响双方对承诺理解的一致性。如果颁发者宕机,持有者可以继续使用租约。在颁发者恢复后,如果颁发者能够恢复之前的租约,双方可继续遵守承诺。如果颁发者无法恢复之前的租约,那么只需要等待租约到期,收回之前的承诺。

     

    租约机制从早期设计中用来维护缓存一致性持续发展,在分布式系统设计中得到广泛应用。承诺的内容也多种多样,可以是缓存中数据的正确性,还可以是某种权限。Cubby中,从节点向主节点发送租约,承诺的内容就是权限,指在约定时间内,不选举其他节点成为主节点。只要主节点持有超过半数节点的租约,那么剩余的节点就不能选举出新的主节点。一旦主节点宕机,剩余的从节点由于不能向主节点发送租约,将发起新的一轮paxos 选举,选举出新的主节点。

     

    同时,Chubby还用租约机制来控制主节点和客户(Client)节点。主节点向每个客户节点颁发租约,用来判断客户节点的状态。一个客户节点只有拥有合法的租约,才能与主节点进行读写操作。一个客户节点如果占有Chubby中的一个节点锁后租约超时,那么这个客户节点占有的锁会被自动释放,从而实现对节点状态进行监控的功能。

     

    综上所述,通过Chubby,用户可以确保数据操作过程中的一致性。但是,与DLM不同的是,这种锁只是建议性的锁(Advisory Lock),不是强制性的锁(Mandatory Lock)。Google通过Chubby构造了丰富的分布应用,包括Google文件系统GFS(Google FileSystem)、BigTable等。

     

    (2)ZooKeeper

     

    Zookeeper使用的是修改后的Paxos协议。Zookeeper中的角色名称为:领导者(Leader)、跟随者(Follower)、观察者(Observer)。领导者对应Chubby中的主节点(Primary),跟随者对应Chubby中的从节点(Secondary)。状态为Leading、Following、Observing和Looking。与Paxos协议相比,观察者是新增的,在大多数情况下,行为与跟随者一致,不过其不参加选举和投票,只是观察(oberving)选举和投票的结果。引入观察者的目的是为了解决系统规模扩大后,网络复杂性可能带来的拜占庭问题。

     

    Zookeeper把系统中状态的转换分为两个阶段,一个阶段是领导者激活阶段(Leaderactivation),此时系统中没有领导者,通过一个类似Paxos协议的过程选出领导者,选出领导者并同步数据后,转入另外一个阶段-活跃消息阶段(Activemessaging)。处在活跃消息阶段时,领导者接收客户端发送的更新操作,在各个跟随者节点上进行更新操作。如果活跃消息阶段领导者发生异常,系统转入领导者激活阶段,重新选举领导者。

     

    Zookeeper定义了一个全局版本号-zxid。zxid由epoch和count两部分组成。 epoch是选举编号,每次提议进行新的领导者选举时epoch都会增加,每一个领导者对应一个唯一的epoch。count是领导者为每个更新操作决定的序号,每个 领导者任期内产生的更新操作对应一个唯一的有序的count。这样的方式,使得整个分布系统有一个代表了更新操作的全局序号。

     

    每个节点都有各自最后提交的zxid。在领导者激活阶段,每个节点都以自己的 zxid与节点编号nodeid组合后一起作为 Paxos 中的提案值发起 Paxos协议,设置自己作为领导者。每个节点既是提案者又是接受者,所以每个节点只会接受提案编号大于自身zxid的提案。通过paxos协议过程,某个超过半数的节点中持有最大的zxid的节点会成为新的领导者。虽然zxid可能相同,但是由于节点编号不同,这个选举过程不会出现所有节点都以相同b参数作为提案的场景,从而避免了无法选出领导者的可能。成为新领导者的节点与跟随者进行数据同步,当与至少半数节点完成数据同步后,领导者更新epoch,各个跟随者更新领导者信息,以(epoch + 1, 0)为 zxid 写一条NEW_LEADER 消息。 当领导者收到超过半数的跟随者对 NEW_LEADER 的确认后,领导者发起对 NEW_LEADER 的提交操作,并进入活跃消息状态。数据同步过程可能会涉及删除跟随者的最后一条脏数据。

     

    进入活跃消息状态的领导者会接收从客户端发来的更新操作,为每个更新操作生成递增的count,组成递增的zxid。领导者将更新操作以zxid的顺序发送给各个跟随者和自身,当收到超过半数的更新者的确认后,领导者发送针对该更新操作的提交消息给各个跟随者。这个更新操作的过程与船艇的两阶段提交很类似,但是进行了简化,领导者不会对更新操作做废弃(abort)操作。

     

    如果领导者不能更新超过半数的跟随者,说明这个领导者处在分裂的网络中偏小的部分,最后一条更新操作处于“中间状态”,其是否生效取决于另外一部分节点选举出的新领导是否有该条更新操作。

     

    总之,由于同一时刻只有一个节点能获得超过半数的跟随者,所以同一时刻最多只存在唯一的领导者,每个领导者顺序更新各个跟随者,只有成功完成前一个更新操作的才会进行下一个更新操作,使得在同一个领导者“任期”内,读超过半数的节点一定可以读到最新已提交的数据,每个成功的更新操作都至少被超过半数的节点确认,故障发生后新选举的领导者一定可以包括最新的已成功提交的数据。

     

    与Chubby中从节点向主节点发送租约不同,Zookeeper 中的跟随者并不向领导者发送租约,Zookeeper中的跟随者发现没有领导者则发起新的Paxos 选举。与 Chubby 类似的是,Zookeeper的领导者向客户发送租约,当一个客户超时,那么这个客户创建的临时节点会被自动删除。


    转自:http://blog.sina.com.cn/s/blog_63a7714301017elo.html

    展开全文
  • 分布式系统设计

    千次阅读 2018-07-16 22:32:55
    高可用分布式系统应该要考虑以下几个大点和一些小点,大点与小点之间其实有包含、有交集,只是为了方便分点论述才分类。 四个大点: 容错能力(服务隔离、异步调用、请求幂等性、分布式锁) 可伸缩性(有 / 无...

     

    高可用分布式系统应该要考虑以下几个大点和一些小点,大点与小点之间其实有包含、有交集,只是为了方便分点论述才分类。

    四个大点:

    • 容错能力(服务隔离、异步调用、请求幂等性、分布式锁)
    • 可伸缩性(有 / 无状态的服务)
    • 一致性(补偿事务、重试)
    • 应对大流量的能力(熔断、降级)

    两个小点:

    • 解耦(MQ)
    • 线程池

    四个大点论述:

    1、容错能力(服务隔离、异步调用、请求幂等性、分布式锁)

    何为容错能力:系统在不健康、不顺,甚至出错的情况下有能力 hold 得住,挺得住,还有能在这种逆境下力挽狂澜的能力;我们都知道,故障是必然会发生的,是正常的,是常见的,我们应该把处理故障的代码当成正常的功能做在架构里写在代码里。

    • 请求幂等:幂等并不是每次请求的结果都一样,而是一次和多次请求某一个资源应该具有同样的副作用,f(x) = f(f(x)),要做到幂等性的交易接口,需要有一个唯一的标识,来标志交易是同一笔交易,这个标识要能做到全局唯一。幂等还是比较容易实现的,比如对于转账交易(支付系统一定要保证幂等),最简单的就是每次请求都带有一个唯一标识id,比如一个请求(id,money),此id标识在数据库中能唯一确定一条记录;若因为网络故障客户端多次请求同一个转账交易(id,money),服务端必须保证只能有一次记录成功,对于其他重复请求应该给客户端明确的解析语义。我们一般保证幂等的判断是从数据库查询有没有相同id的记录,但是在分布式系统环境下,可能有问题,主从问题:两个相同的请求request1、request2时间间隔很短,request1请求过来的时候,查询从库发现没有对应记录,则request1开始操作插入主库record1,但是还没有同步到从库;此时request2查询从库(主从还未同步)也发现没有相同id的记录,准备插入有相同id的记录record2,这个时候request1成功插入record1,request2开始插入record2,数据库报错:唯一约束被破坏相关的异常日志;解决这个问题有两种方法:1、读写都强制走主库;2、采用分布式锁,考虑性能问题,一般都选2
    • 分布式锁:分布式系统一般都有多台机器,常规的多线程锁已经无法解决问题;最简单用redis实现:思路很简单,主要用到的redis函数是setnx()。首先是将某一任务标识名UniqueKey(能唯一识别一个请求的标识)作为键存到redis里,并为其设个过期时间,如果是同样的请求过来,先是通过setnx()看看是否能将UniqueKey插入到redis里,可以的话就返回true,不可以就返回false。
      • 分布式锁设计原则:
      •    互斥性,同一时间只有一个线程持有锁
      •    容错性,即使某一个持有锁的线程,异常退出,其他线程仍可获得锁
      •    隔离性,线程只能解自己的锁,不能解其他线程的锁
    • 服务隔离:是为了在系统发生故障时能限定传播范围和影响范围,即发生故障后不会出现滚雪球效应,从而保证只有出问题的服务不可用,其他服务还是可用的。
    • 异步调用:发送方发送请求后,接收方直接返回正在处理,通过轮询或者回调的方式返回结果;异步调用相比于同步调用的最大好处是可以快速相应客户端的请求,至于具体的请求结果,通过异步回调的方式发送给客户端,这种方式在服务端平均处理请求时间过长的业务场景下很好用,避免在高请求流量下的超时、阻塞等问题;

    2、可伸缩性(有 / 无状态的服务)

    • 无状态服务在程序 Bug 上和水平扩展上有非常优秀的表现,但是在一致性上却有劣势,事物总是相对的(个人此点能力不足,不足以论述)

    3、一致性(补偿事务、重试)

    • ACID :大家在买同一本书的过程中,每个用户的购买请求都需要把库存锁住,等减完库存后,把锁释放出来,后续的人才能进行购买。同一时间不可能有多个用户下单,订单流程需要有排队的情况,这样一来,我们就不可能做出性能比较高的系统来。
    • BASE :大家都可以同时下单,这个时候不需要去真正地分配库存,然后系统异步地处理订单,而且是批量的处理。因为下单的时候没有真正去扣减库存,所以,有可能会有超卖的情况。而后台的系统会异步地处理订单时,发现库存没有了,于是才会告诉用户你没有购买成功。
    • 强一致性(ACID)和高可用性(BASE)是对立,顾此失彼;因此,为了可用性,我们要讲业务中需要强一致性的动作和不需要强一致性的动作剥离开,对于非强一致性需求的动作,可以做补偿事务;我们应尽量设计更多非强一致性的业务
    • 由于网络等问题,一些请求无法确定是否成功,这个时候需要重试,即在此发送请求给服务端,希望服务端能确定的交易结果,重试一般通过定时任务扫表,将不是终态的记录查询出在此发请求。

    4、应对大流量的能力(熔断、降级、限流)
     

    • 熔断(慎用):如果系统中,某个目标服务调用慢或者有大量超时,此时,熔断该服务的调用,对于后续调用请求,不在继续调用目标服务,直接返回,快速释放资源。如果目标服务情况好转则恢复调用;熔断主要是应对流量引起的问题,弃卒保车,使服务处于关闭、半关闭状态,以保证部分业务成功,或者舍弃次要业务使主业务运行通畅;
    • 限流:熔断的一种,半开状态,只允许少部分的请求,其他的都拒绝,如果设计得当,被拒绝的请求,客户端会通过重试、补偿操作来完成;

      限流策略:
       1)计数器算法:设置一个计数器统计单位时间内某个请求的访问量,在进入下一个单位时间内把计数器清零,对于单位时间内超过计数器的访问,可以放入等待队列、直接拒接访问等策略
       2)漏斗算法:一个固定容量的漏桶,按照常量固定速率流出水滴;可以以任意速率流入水滴到漏桶;如果流入水滴超出了桶的容量,则流入的水滴溢出了,而漏桶容量是不变的。
       3)令牌桶算法:令牌将按照固定的速率被放入令牌桶中。比如每秒放10个。每次请求调用需要先获取令牌,只有拿到令牌,才有机会继续执行,否则选择选择等待可用的令牌、或者直接拒绝。当令牌桶满时,新添加的令牌被丢弃或拒绝

      漏桶算法与令牌桶算法在表面看起来类似,很容易将两者混淆。但事实上,这两者具有截然不同的特性,且为不同的目的而使用。漏桶算法与令牌桶算法的区别在于,漏桶算法能够强行限制数据的传输速率,令牌桶算法能够在限制数据的平均传输速率的同时还允许某种程度的突发传输。需要注意的是,在某些情况下,漏桶算法不能够有效地使用网络资源,因为漏桶的漏出速率是固定的,所以即使网络中没有发生拥塞,漏桶算法也不能使某一个单独的数据流达到端口速率。因此,漏桶算法对于存在突发特性的流量来说缺乏效率。而令牌桶算法则能够满足这些具有突发特性的流量。通常,漏桶算法与令牌桶算法结合起来为网络流量提供更高效的控制。
      使用:
             单机限流:google的guava令牌桶算法实现;
             集群限流:Redis的计数实现精确;

    • 降级:暂时牺牲掉一些服务,保障整个系统的服务。比如:如果服务器已经高负载,这个时候可以将一些不重要的操作给关闭,比如重试操作,因为服务器已经承受不住,这个时候再重试也没有多大效果,反而更加重服务端的压力,这个时候可以设置一个开关,将重试功能关闭;

    两个小点:

    1、解耦(MQ)

    在软件工程中,对象之间的耦合度就是对象之间的依赖性。对象之间的耦合越高,维护成本越高,因此对象的设计应使模块之间的耦合度尽量小。在软件架构设计中,模块之间的解耦或者说松耦合有两种,假设有两个模块A、B,A依赖B:

    1. 第一种是,模块A和模块B只通过接口交互,只要接口设计不变,那么模块B内部细节的变化不影响模块A对模块B服务能力的消费。 
      • 面向接口设计下真正实现了将接口契约的定义和接口的实现彻底分离,实现变化不影响到接口契约,自然不影响到基于接口的交互。
      • 模块A和B之间的松耦合,主要通过合理的模块划分、接口设计来完成。如果出现循环依赖,可以将模块A、B共同依赖的部分移除到另一个模块C中,将A、B之间的相互依赖,转换为A、B同时对C的依赖。
    2. 第二种是,将同步调用转换成异步消息交互。 
      • 比如在买机票系统中,机票支付完成后需要通知出票系统出票、代金券系统发券。如果使用同步调用,那么出票系统、代金券系统宕机是会影响到机票支付系统,如果另一个系统比如专车系统也想要在机票支付完成后向用户推荐专车服务,那么同步调用模式下机票支付系统就需要为此而改动,容易影响核心支付业务的可靠性。
      • 如果我们将同步调用替换成异步消息,机票支付系统发送机票支付成功的消息到消息中间件,出票系统、代金券系统从消息中间件订阅消息。这样一来,出票系统、代金券系统的宕机也就不会对机票支付系统造成任何影响了。专车系统想要知道机票支付完成这一事件,也只需要从消息中间件订阅消息即可,机票支付系统完全不需要做任何改动。
      • 异步消息解耦,适合那些信息流单向流动(类似发布-订阅这样的),实时性要求不高的系统。常见的开源消息队列框架有:Kafka、RabbitMQ、RocketMQ。

    2、线程池

    • 线程池:如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间,且创建过多的线程会耗费系统资源,频繁的上下文切换也影响系统的性能。

     

     

    实战分析:

    1、降级

    分析项目一:三个系统,A、B、C;A负责下单菜品:aaa,B负责接受A的下单请求,比根据A传递过来的aaa查询C得到菜品的详情,然后在继续下单;我们可以看出如果C系统挂了,整个下单流程就挂了,无法下单了;如何降级?B将C的菜品数据也存一份,正常情况下用C的菜品查询(C的菜品数据实时性更好一点),如果C挂了,B就用自己存的菜品,作为一种临时方案,达到可继续下单的目的,毕竟不能因为一个查询系统挂了导致整个下单功能不能用,这就是一种降级处理~

    分析项目二:关闭重试机制;很多项目都有超时重试的机制,这种机制在服务已经负载过大、无法及时处理时,应当将重试逻辑关闭,避免因重试导致更严重的服务负担。

    分析项目三:控制入口访问权限,白名单可用,当服务负载过大,可以通过在入口(比如拦截器中进行控制)控制流量,比如只允许vip客户访问,通过添加白名单方式来实现。

    降级的手段总结:
    a. 拒绝部分请求:如下三种方式

    • 拒绝部分老请求:优先处理新请求
    • 优先级请求方式:下单的请求咱继续收,退款、查询等先拒绝吧
    • 随机丢弃

     

    2、高可用设计注意事项

    分析项目一:缓存,不要放在业务侧,要在数据源头设置,业务方只负责增删改;否则可能造成多个业务方的数据不一致;

    展开全文
  • #资源达人分享计划#
  • 如何设计分布式系统

    2020-03-05 16:41:37
    设计分布式系统主要考虑以下四个大点,两个小点: 四个大点: 容错能力(服务隔离、异步调用、请求幂等性、分布式锁) 可伸缩性(有 / 无状态的服务) 一致性(补偿事务、重试) 应对大流量的能力(熔断、降级) ...

    设计分布式系统主要考虑以下四个大点,两个小点:
    四个大点

    • 容错能力(服务隔离、异步调用、请求幂等性、分布式锁)
    • 可伸缩性(有 / 无状态的服务)
    • 一致性(补偿事务、重试)
    • 应对大流量的能力(熔断、降级)

    两个小点

    • 解耦(MQ)
    • 线程池

    四个大点的具体论述

    1、容错能力(服务隔离、异步调用、请求幂等性、分布式锁)

    我们都知道,系统出现故障是非常常见的,所以我们应该把处理故障的代码当成正常的功能做在架构里写在代码里,使得故障真正发生时,系统还可以运行

    • 服务隔离:是为了在系统发生故障时缩小其影响范围从而保证只有出问题的服务不可用,其他服务还是可用的。

    • 异步调用:发送方发送请求后,接收方直接返回正在处理,通过轮询或者回调的方式返回结果;异步调用相比于同步调用的最大好处是可以快速响应客户端的请求,至于具体的请求结果,通过异步回调的方式发送给客户端,这种方式在服务端平均处理请求时间过长的业务场景下很好用,避免在高请求流量下的超时、阻塞等问题;

    • 请求幂等:幂等并不是每次请求的结果都一样,而是一次和多次请求某一个资源应该具有同样的副作用,f(x) = f(f(x)),要做到幂等性的交易接口,需要有一个唯一的标识,来标志交易是同一笔交易,这个标识要能做到全局唯一。我们通常保证幂等的判断是从数据库查询有没有相同id的记录,但是在分布式系统环境下,可能有主从同步问题:两个相同的请求request1、request2时间间隔很短,request1请求过来的时候,查询从库发现没有对应记录,则request1开始操作插入主库record1,但是还没有同步到从库;此时request2查询从库(主从还未同步)也发现没有相同id的记录,准备插入有相同id的记录record2,这个时候request1成功插入record1,request2开始插入record2,数据库报错:唯一约束被破坏相关的异常日志;解决这个问题有两种方法:1、读写都强制走主库;2、采用分布式锁,考虑性能问题,一般都选2

    • 分布式锁:分布式系统一般都有多台机器,常规的多线程锁已经无法解决问题;最简单用redis实现:思路很简单,主要用到的redis函数是setnx()。首先是将某一任务标识名UniqueKey(能唯一识别一个请求的标识)作为键存到redis里,并为其设个过期时间,如果是同样的请求过来,先是通过setnx()看看是否能将UniqueKey插入到redis里,可以的话就返回true,不可以就返回false。

    分布式锁设计原则:

    • 互斥性,同一时间只有一个线程持有锁
    • 容错性,即使某一个持有锁的线程,异常退出,其他线程仍可获得锁
    • 隔离性,线程只能解自己的锁,不能解其他线程的锁

    2、可伸缩性(有 / 无状态的服务)

    什么是系统的可伸缩性?
    如果你的系统对一个用户来说是快的,但是在用户不断增长的高访问量下就慢了
    伸缩性方案
    垂直伸缩: 升级到更强大的服务器(多CPU 昂贵大中型机)。
    水平伸缩:

    • 状态的扩展(内存或数据库):读写分离、分库分表,静态页面缓存,NOSQL
    • 无状态的扩展(计算可伸缩性):侧重行为计算方面,类似提升CPU处理能力。

    3、一致性(补偿事务、重试)

    • 强一致性:数据高度统一,无需担心同一时刻会获得不同的数据。就好像交易系统,存取钱的+/-操作必须是马上一致的。
    • 弱一致性:数据经过一个时间窗口之后,只要多尝试几次,最终的状态是一致的,是最新的数据。比如发了一条微博,改了某些配置,可能不会马上生效,但刷新几次后就可以看到了
    • 重试:由于网络等问题,一些请求无法确定是否成功,这个时候需要重试,即在此发送请求给服务端,处理逻辑是将请求包在一个重试循环里

    4、应对大流量的能力(熔断、降级、限流)

    • 熔断(慎用):如果系统中,某个目标服务调用慢或者有大量超时,此时,熔断该服务的调用,对于后续调用请求,不在继续调用目标服务,直接返回,快速释放资源。如果目标服务情况好转则恢复调用;熔断主要是应对流量引起的问题,使服务处于关闭、半关闭状态,以保证部分业务成功,或者舍弃次要业务使主业务运行通畅;

    • 限流:熔断的一种,半开状态,只允许少部分的请求,其他的都拒绝,如果设计得当,被拒绝的请求,客户端会通过重试、补偿操作来完成;

    • 降级:暂时牺牲掉一些服务,保障整个系统的服务。比如:如果服务器已经高负载,可以拒绝老的请求,先处理新的请求,或者关闭部分操作以减轻服务器压力;

    两个小点:

    1、解耦(MQ)
    在软件工程中,对象之间的耦合度就是对象之间的依赖性。对象之间的耦合越高,维护成本越高,因此对象的设计应使模块之间的耦合度尽量小。

    在软件架构设计中,模块之间的解耦有两种,假设有两个模块A、B,A依赖B:

    第一种:模块A和模块B只通过接口交互,只要接口设计不变,那么模块B内部细节的变化不影响模块A对模块B服务能力的消费。
    面向接口设计下真正实现了将接口契约的定义和接口的实现彻底分离,实现变化不影响到接口契约,自然不影响到基于接口的交互。
    模块A和B之间的松耦合,主要通过合理的模块划分、接口设计来完成。如果出现循环依赖,可以将模块A、B共同依赖的部分移除到另一个模块C中,将A、B之间的相互依赖,转换为A、B同时对C的依赖。
    第二种:将同步调用转换成异步消息交互。
    比如在买机票系统中,机票支付完成后需要通知出票系统出票、代金券系统发券。如果使用同步调用,那么出票系统、代金券系统宕机是会影响到机票支付系统,如果另一个系统比如专车系统也想要在机票支付完成后向用户推荐专车服务,那么同步调用模式下机票支付系统就需要为此而改动,容易影响核心支付业务的可靠性。
    如果我们将同步调用替换成异步消息,机票支付系统发送机票支付成功的消息到消息中间件,出票系统、代金券系统从消息中间件订阅消息。这样一来,出票系统、代金券系统的宕机也就不会对机票支付系统造成任何影响了。专车系统想要知道机票支付完成这一事件,也只需要从消息中间件订阅消息即可,机票支付系统完全不需要做任何改动。
    异步消息解耦,适合那些信息流单向流动(类似发布-订阅这样的),实时性要求不高的系统。常见的开源消息队列框架有:Kafka、RabbitMQ、RocketMQ。
    2、线程池
    线程池:如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间,且创建过多的线程会耗费系统资源,频繁的上下文切换也影响系统的性能。

    展开全文
  • http://blog.sina.com.cn/s/articlelist_1671917891_0_1.html
    展开全文
  • #资源达人分享计划#
  • 准备工作 搭建一套量化系统并非一件容易的事,如果你是一位初出茅庐的程序猿...本系统采用分布式架构,涉及到的编程语言有:C++、C#、NodeJS,数据库采用MySql,通讯中间件采用ZeroMQ。选择C++的原因有二点,首先考...
  • 分布式系统 在计算机领域,当单机性能达到瓶颈时,一般有两种方式解决性能问题: ...而分布式系统设计说白了就是: 如何合理将一个系统拆分成多个子系统部署到不同机器上。 讲设计方法前,先介绍分...
  • 分布式系统设计理念

    千次阅读 2017-09-26 17:05:34
    如果最终设计出来的分布式系统占用了10台机器才勉强达到单机系统的两倍性能,那么这个分布式系统还有存在的价值吗?另外,即使采用了分布式架构,也仍然需要尽力提升单机上的程序性能,使得整体性能达到最高。所以,...
  • #资源达人分享计划#
  • #资源达人分享计划#
  • 分布式系统设计(经典书籍)

    热门讨论 2009-06-17 00:15:49
    本书较为全面地介绍了分布式系统领域的一些基本概念,提出了分布式系统的各种问题,如互斥问题、死锁的预防和检测、...本书适用于学习分布式系统设计的高年级本科生、研究生和从事分析、设计分布式系统的计算机专业人员
  • 分布式系统设计原理与方案

    千次阅读 2018-12-02 17:30:56
    分布式系统设计原理与方案  一直在思考分布式系统设计的问题,业务对象原封不动的情况下部署在客户端和服务器端,可以根据配置文件选择是连接服务器还是连接本地的数据库,这个问题让我绞尽脑汁,我总是设想的...
  • 制定符合中国政策的分布式能源区块链支付模式,提出包括系统架构、交易流程在内的基于区块链技术的分布式能源交易方案,对比传统电力交易系统和基于区块链的分布式能源交易系统的性能,思考如何改进目前区块链的不足之...
  • #资源达人分享计划#
  • #资源达人分享计划#
  • 现如今我们的系统大多拆分为分布式SOA,或者微服务,一套系统中包含了多个子系统服务,而一个子系统服务往往会去调用另一个服务,而服务调用服务无非就是使用RPC通信或者restful,既然是通信,那么就有可能再服务器...
  • 分布式系统设计的求生之路

    千次阅读 2016-06-24 14:07:49
    面对日益复杂的需求,分布式系统的理念也逐渐深入到后台开发者的骨髓。2013年,借着手游热潮我对分布式系统开始尝试。在近三年的摸爬滚打中,踩过不少坑,也从业界技术发展中吸取一些经验,逐渐形成了目前的设计思路...
  • 第1章 分布式系统的特征 1.1 简介 1.2 分布式系统的例子 1.2.1 Web搜索 1.2.2 大型多人在线游戏 1.2.3 金融交易 1.3 分布式系统的趋势 1.3.1 泛在联网和现代互联网 1.3.2 移动和无处不在计算
  • 开源分布式量化交易系统——开篇

    千次阅读 2019-04-08 16:45:16
    架构设计 后端模块 交易中心 行情中心 算法工人 前端模块 策略编辑 策略回测 策略仿真 系统部署 策略分享 回顾与后记 前言 本人是一名计算机专业毕业的普通程序员,机缘巧合踏入了这个...
  • 分布式系统概述

    千次阅读 2018-06-17 19:08:00
    分布式系统概述 总结自:A Thorough Introduction to Distributed Systems 分布式系统概述 概述 什么是分布式系统 为什么需要分布式系统? 扩展数据库 继续扩展 陷阱 去中心化与分布式 分布式系统类型 分布式...
  • 分布式设计

    千次阅读 2014-02-11 10:15:14
    分布式设计与开发在IDF05(Intel Developer Forum 2005)上,Intel首席执行官Craig Barrett就取消4GHz芯片计划一事,半开玩笑当众单膝下跪致歉,给广大软件开发者一个明 显的信号,单纯依靠垂直提升硬件性能来提高...
  • 分布式系统入门

    千次阅读 2018-06-16 18:38:42
    一、什么是分布式系统分布式系统是一个硬件或软件组件分布在不同的网络计算机上,彼此之间仅仅是通过消息传递进行通信和协调的系统。 首先分布式系统一定是由多个节点组成的系统,一般来说一个节点就是我们的一...
  • 分布式系统设计理念

    千次阅读 2018-09-03 11:17:11
    如果最终设计出来的分布式系统占用了10台机器才勉强达到单机系统的两倍性能,那么这个分布式系统还有存在的价值吗?另外,即使采用了分布式架构,也仍然需要尽力提升单机上的程序性能,使得整体性能达到最高。所以,...
  • 分布式系统架构设计

    千次阅读 2016-05-08 16:30:33
    一个完整的电商系统,分为前台交易系统与后台作业系统,前后台共库是传统企业在设计电商项目时的一个常见做法。但这个做法引发了上线后的诸多麻烦。在前台交易系统处于峰值情况下,数据库本身已存在很大的压力,此时...
  • 分布式系统设计“系列第一篇文章,这篇文章主要介绍一些入门的概念和原理,后面带来一些高可用、数据分布的实践方法!! 各位亲,如果你们觉得本文有还不错的地方,请点击“投一票”支持本文,多谢! ...
  • 分布式系统设计”系列第一篇文章,这篇文章主要介绍一些入门的概念和原理,后面带来一些高可用、数据分布的实践方法!!   ==> 分布式系统中的概念 ==> 分布式系统与单节点的不同 ==> ...
  • 实时高频交易特有的高频率、短时长特征,对出清计算、决策评估等提出了较高的技术要求。为解决当前实时高频交易领域所面临...最后结合当前电力现货市场建设实际,设计了基于分布式计算的电力现货市场交易平台系统架构。
  • 分布式系统的网络异常

    千次阅读 2017-08-24 18:19:07
    分布式系统
  • #资源达人分享计划#

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 58,002
精华内容 23,200
关键字:

交易系统设计分布式