架构杂谈《六》

架构杂谈《六》

超时处理模式

  在服务化或者微服务架构里,传统的整体应用拆分成多个职责单一的微服务,微服务之间通过某种网络通信协议互相通信和交互,完成特定的功能,然而由于网络通信的不稳定,在设计系统时必须考虑到对网络通信的容错,特别是对调用超时问题的处理。

一、微服务的交互模式

  1、同步调用模式

    在同步调用模式中,服务A调用服务B,服务A的线程阻塞等待服务B的处理结果,如果服务B一直不返回处理结果,则服务A一直处于等待状态中一直到超时为止。

(同步调用模式图)

  2、接口异步调用模式

    在接口异步调用模式中,服务A请求服务B处理某项任务,服务B处理完后即刻返回给服务A处理结果,如果处理成功,则服务A继续干其他任务,而服务B异步处理这项任务,直到服务B处理完这项任务后,才反向通知服务A任务已完成,服务A再做后续的工作。

(接口异步调用模式)

    接口异步调用模式适用于非核心链路上负载较高的处理环节,这个环节经常耗时较长并且对时效性要求不高。

  3、消息队列异步处理模式

    消息队列异步处理模式利用消息队列作为通信机制,在这种交互模式中,通常服务A只需将某种事件传递给服务B,而不需要等待服务B返回结果。在这种情况下,服务A与服务B可以充分解耦,并且在大规模、高并发的微服务系统中,消息队列对流量具有消峰的功能。

(消息队列异步处理模式)

    消息队列异步处理模式与接口异步调用模式类似,多应用于非核心链路上负载较高的处理环节中,并且服务的上游不关心下游的处理结果,下游也不需要向上游返回处理结果。

  以上这三种交互模式普遍应用于服务化和微服务架构中,它们之间没有绝对的好坏,只需要在特定场景下做出合适的选择。

二、同步于异步的抉择

  1、尽量使用异步来替换同步操作

  2、能用同步解决的问题,不要引入异步。

  第一条原则是从业务功能的角度出发的,业就是从与用户或者使用方的交互模式出发的。如果业务逻辑允许,用户对产品的交互形态没有异议,则我们可以将一些耗时较长的、用户对响应没有特别要求的操作异步化,以此来减少核心链路的层级,释放系统的压力。

  第二条原则是从技术和架构的角度出发的,这条原则应用的前提是同步能够解决问题,这隐含了一个含义:如果性能不是问题,或者所处理的操作是短小的轻量级处理逻辑,那么同步调用方式是最理想不过的,因为这样不需要引入异步化的复杂处理流程。

三、交互模式下超时问题的解决方案

  1、同步调用模式下的解决方案

    在同步模式下,对外的接口会提供服务契约,契约定义了服务的处理结果会通过返回值返回给对方,对返回的状态定义分为以下两种:

      A)成功和失败(两状态的同步接口)

      B)成功、失败和处理中(三状态的同步接口)

    1)两状态的同步接口

      服务契约中只规定了两种互斥的状态:成功和超时,服务处理结果必须是成功的或者失败的。在这种情况下可能发生两种同步调用超时。

      第一种同步调用超时发生在使用方调用此同步接口的过程中

      针对这个问题,我们需要服务的使用方使用前面架构杂谈中提到的查询模式,异步查询处理结果,在获得明确的处理结果后,得知处理结果是成功还是失败,然后做相应的处理。如果处理结果为成功,那么使用方可以继续下面的操作;如果结果为失败,那么调用方可以发起重试,请求再次进行处理。然而,这里有一个问题,如果查询模式的返回状态是未知请求,那么在这种情况下使用方超时,服务 1 实际上没有接收到或者还没有接收到一开始的处理请求,服务使用方需要使用同一个请求 ID 进行重试,服务 1 也必须实现请求处理的幕等性。

    第二种同步调用超时发生在内部服务1调用服务2的过程中

 

    

   在使用方调用服务 1,且服务 1 接收到请求后,同步调用服务 2,由于通信出现了问题, 所以服务 1得到超时的结果。这时服务 1 应该怎么做呢?是重试、取消还是快速失败?
   我们看到上图的左面服务 1 对外接口的契约中包含两个返回状态 : 成功或者失败,也就是对于使用方来讲,不允许有中间的处理中的状态,对于这种服务内部超时的场景,必须使用快速失败的策略 : 针对这个超时错误,服务快速返回失败,同时在内部调用服务 2 的冲正接口,服务 2 的冲正接口可以判断之前是否接收到请求,如果接收到请求井做了处理,则应该做反向的回攘操作。如果服务 2 之前没有接收到处理请求,则忽略冲正请求,以此来实现服务的幕等性。

   2)三状态的同步接口    

    对于上面的第 2 种定义,服务契约中规定了三种处理结果,状态值为:成功、失败和处理中,对于超时等系统错误的请求,其实可以认为是处理中状态的一个特例,在这种场景的应用里,超时被视为内部暂时的问题,随后可能被修复,因此,可能在一定的时间窗口内告知使用方在处理中,随后修复问题井补偿执行,达到最大化请求处理成功的目标,不至于让使用方重试,以提升用户体验 。
    服务处理结果可能是成功或者失败,也可能是处理中,在这种情况下可能发生两种同步调用超时。

    第1种同步调用超时发生在使用方调用此同步接口过程中,如下图所示:

    这种场景和两状态同步调用的接口超时场景类似,使用方调用服务 1 的接口,由于网络等原因获得超时的结果,这时使用方应该将超时看作处理中的一个特例,使用服务 1 的查询接口后续补齐上一个请求的处理状态,可参照两状态同步调用的接口超时场景的方案。
    第 2 种同步调用超时发生在内部服务 l 调用服务 2 的过程中,如下图所示。

     在使用方调用服务 1, 且服务 1 接收到请求后,同步调用服务 2,由于通信出现了问题,所以服务 l 得到超时的结果,这时服务 1 又应该怎么做呢?
     这和两状态同步调用 的内部超时场景不一样,两状态设计由于与使用方约定了契约,不是成功就是失败,所以必须在同步调用时给予一个明确的结果,然而,在三状态同步调用的内部超时场景下,可以返回给使用方一个中间状态,也就是处理中的结果,变相地把同步接口变成异步接口 ,达到最终一致的效果。
     在这种场景下,我们更倾向于给用户更好的体验,尽最大努力成功处理用户发来的请求 。因此,针对在服务 1 调用服务 2 时超时,我们会返回给用户处理中的状态,随后系统尽最大努力补偿执行出错的部分,服务 1 需要通过服务 2 的查询接口得到最新的请求处理状态,如果服务 2 没有明确回复, 则可以尝试重新发送请求,当然,这里需要服务 2 也实现了操作的幕等性 。

  2. 异步调用模式下的解决方案  

    在异步调用模式下,对外的接口也会提供服务契约,契约定义了服务的处理结果会通过返回值返回给使用方,返回的状态通常为两个:处理和未处理。和三状态同步调用接口不同的是异步调用模式还有异步处理返回结果的通知,状态包括处理成功和处理失败。
    不同阶段的网络通信产生的超时和处理方案如下。

    1)异步调用接口超时

  

      异步调用接口超时发生在使用方调用服务 1 的受理接口时,同两状态同步调用接口超时及三状态同步调用接口超时的场景是一样的,需要通过查询来补齐状态,并根据状态来判断后续的操作,具体的解决方案参考两状态同步调用接口超时和三状态同步调用接口超时的解决方案。

    2)异步调用内部超时

 

      异步调用内部超时发生在服务 1 受理了使用方的请求后 ,服务 1 在处理请求时,在调用服务 2 的过程中超时,这和三状态同步调用内部超时的场景相似,由于异步调用模式使用的是受理模式,所以一旦受理,我们便应该尽最大努力将用户请求的操作处理成功,因此,在服务 1 调用服务 2 超时的场景下,服务 1需要根据服务 2 的查询接口获得最新状态,根据状态补偿后续的操作,这和三状态同步调用内部超时的解决方案一致,不同的是此场景下一旦处理成功,则需要异步回调通知使用方,而在三状态同步调用内部超时的场景下,只需要等待使用方查询,不需要通知,也无法实现通知。

    3)异步调用回调超时

      回调超时的问题在生产中经常出现,通常发生于这样的场景下:服务 1 受理后成功地调用了依赖服务 2,获得了明确的处理结果,但是在将处理结果通知使用方时出现超时。由于使用方有可能是公司内部的也可能是外部的 ,网络环境复杂多变,发生超时的概率很大,因此,大多数公司都会开发一个通知子系统,用来专门处理回调通知。

      由于服务 1通过回调通知使用方,所以服务 1需要保证通知一定可送达,如果遇到超时,则服务 1 负责重新继续补偿,通常会设计一个通知时间按一定间隔递增的策略,例如 :指数回退,直到通知成功为止,通知是否成功以对方的回写状态为准。

  3、消息队列异步处理模式的解决方案  

    消息队列异步处理模式多用于疏松祸合的项目,这些项目通常是在主流程中无法处理耗时的任务,恰好耗时的任务又不是核心流程的一部分,比如 : 电商平台的物流、配送等。

    这类交互使用消息队列进行解耦,电商交易系统成功处理交易后,需要发送消息到消息队列服务器,后续的流程由物流平台处理,也不需要将处理结果反馈给交易平台。

    使用消息队列解耦后,处理流程被分为两个阶段:生产者投递和消费者处理,在不同的阶段会产生不同的超时问题,解决方案如下。

    1)消息队列的生产者超时

    2)消息队列的消费者超时

    对于消息队列的处理机与消息队列之间的超时或者网络问题,通常可以通过消息队列提供的机制来解决。
    一般消息队列会提供如下两种方式来消费消息。
      1)、自动增长消费的偏移量:在一个消费者从消息服务器中取走消息后,消息队列的消息偏移量自动增加,即消息一旦被从消息队列中取走,则不再存在于服务器中,假如消息处理机对此消息处理失败,则也无法从消息服务器中找回。
      2)、手工提交消费的偏移量 :在一个消费者从消息服务器中取走消息后,处理机先把消息持久到本地数据库中,然后告诉消息服务器己经消费消息,消息服务器才会移除消息,如果在没有告诉消息服务器己经消费消息之前,持久失败或者发生了其他问题,则消息仍然存在于消息服务器中,消息处理器下次还可以继续消费消息。
    如果允许丢消息,则我们使用第1种处理方式,这种方式的并发量高、性能好,但是如果我们对消息处理的准确性要求较高,则必须采用第 2 种方式。

四、超时补偿的原则

  1)服务1调用服务2,如果服务2响应服务1并且告诉服务1消息己接收,那么服务1的任务就结束了;如果服务2处理失败,那么服务2应该负责重试或者补偿。在这种情况下,服务2通常接收消息后先持久再告诉服务1接收成功,随后服务2才开始处理持久的消息,避免服务进程被杀掉而导致消息丢失。

  2)服务1调用服务2,如果服务2没有给出明确的接收响应,例如网络超时,那么服务1应该持续进行重试,直到服务2明确表示己经接收消息。在这种情况下容易出现重复的消息,因此在服务2中通常要保证滤重或者幕等性。

说明:

  1、参考书籍:《分布式服务架构:原理、设计与实战》

  2、如有不合适的地方请反馈。综合后更改。

(0)

相关推荐

  • C#后台异步消息队列实现

    简介 基于生产者消费者模式,我们可以开发出线程安全的异步消息队列. 知识储备 什么是生产者消费者模式? 为了方便理解,我们暂时将它理解为垃圾的产生到结束的过程. 简单来说,多住户产生垃圾(生产者)将垃 ...

  • 架构杂谈《五》

    保证最终一致性的模式 在大规模.高并发服务化系统中,一个功能被拆分成多个具有功能单一的子功能,一个流程会有多个系统的多个单一功能的服务组合实现,如果使用两阶段提交协议和三阶段提交协议,确实能解决系统间 ...

  • 面试官对于消息队列的连环炮

    面试官对于消息队列的连环炮

  • 一文详解分布式系统

    分布式系统,顾名思义,就是让多台服务器.多计算单元,协同来完成整体的计算任务.它拥有多种组织方式.在分布式系统中,使用分层模型,路由和代理计算任务.存储任务,将不同的工作,划分到不同业务集群机器中,是 ...

  • 架构杂谈《八》

    Docker 架构 一.Docker 引擎的三大组件 1)Docker 后台服务(Docker Daemon):是长时间运行在后台的守护进程,是Docker的核心服务,可以通过命令dockerd与它进 ...

  • 架构杂谈《七》

    架构杂谈<七> 容器VS虚拟机 一.什么是虚拟机 虚拟机(Virtual Machine)指通过软件模拟的具有完整硬件系统功能的.运行在一个完全隔离环境中的完整计算机系统. 虚拟系统通过生 ...

  • 架构杂谈《四》

    架构杂谈<四> 分布式一致性协议 一.引言 在分布式系统中,为了保证数据的高可用,通常会将数据保留多个副本(replica),这些个副本会放在不同的物理机上,为了对用户提供正确的数据,我们 ...

  • 产品思维学习(三)--产品设计的五个层面_兴国-为梦想而战-CSDN博客_产品架构的五个层面

    产品思维学习(三)--产品设计的五个层面 今天的读书会很碰巧有一位同学分享<用户体验要素-以用户为中心的产品设计>这本书.里面讲述了用户体验要素的五个层面:战略层,范围层,结构层,框架层, ...

  • 理想化学校课程架构的五个维度

    我同意并且一直坚持这样的一个主张或观点:课程是学校教育的最重要的载体,课程应该是学校的最重要的产品. 一所学校的课程状况决定着这所学校教育所能具有的"教育空间"的状况. 学校课程的 ...

  • 占星不求人(杂谈五)古典占星看健康 -by 古典占星老师吴奕清

    Hi,我是Yitsing吴奕清,清清玄学俱乐部和卓越之门权贵(风投)组织创始人及主理人. 酷爱研究古典占星学.八字.紫微斗数.面相.阴阳宅风水当中的格局论断,致力于使用命理学当中的格局论断技法来进行风 ...

  • 杂谈[五]

       这是六一先生制定的____  预测师的准则: [一]要有高深的学问,没有学历的限制,但必须博通五术,有真正的才学, [二]要有宗教家的慈悲,不可敛财骗色,必须本着慈悲心劝人行善布施, [三]要有 ...

  • 股权三部曲:股权分配+股权架构+股权激励 五张系统知识图足够!

    <股权设计风险管理手册><公司股权架构图解手册><股权激励实战手册> 北青博雅 股权三部曲 常坷           pingpu