架构杂谈《六》

超时处理模式

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

一、微服务的交互模式

  1、同步调用模式

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

640?wx_fmt=png

(同步调用模式图)

  2、接口异步调用模式

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

640?wx_fmt=png

 

(接口异步调用模式)

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

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

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

640?wx_fmt=png

 

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

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

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

二、同步于异步的抉择

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

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

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

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

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

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

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

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

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

    1)两状态的同步接口

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

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

640?wx_fmt=png

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

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

 640?wx_fmt=png

    

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

   2)三状态的同步接口    

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

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

640?wx_fmt=png

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

640?wx_fmt=png

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

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

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

    1)异步调用接口超时

  640?wx_fmt=png

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

    2)异步调用内部超时

 640?wx_fmt=png

 

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

    3)异步调用回调超时

640?wx_fmt=png

 

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

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

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

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

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

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

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

640?wx_fmt=png

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

640?wx_fmt=png

    对于消息队列的处理机与消息队列之间的超时或者网络问题,通常可以通过消息队列提供的机制来解决。
    一般消息队列会提供如下两种方式来消费消息。
      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、如有不合适的地方请反馈。综合后更改。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/314787.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【BZOJ4543】Hotel加强版【神仙树形dp】【长链剖分】

题意:给一棵 nnn 个点的树,求两两距离相等的三元组个数。 n≤105n\leq 10^5n≤105 显然相当于是找一个点到这三个点距离相等。子树内和子树外到当前点的距离为某个值的点的个数可以长链剖分快速得到,但统计答案非常棘手。 接下来是个鬼才想…

基于surging 的stage组件设计,谈谈我眼中的微服务

一、前言surging 开源地址:https://github.com/dotnetcore/surging随着业务的发展,并发量的增多,业务的复杂度越来越大,对于系统架构能力要求越来越高,这时候微服务的设计思想应运而生,但是对于微服务需要引…

HDU - 6971 K - I love max and multiply sosdp

传送门 文章目录题意:思路:题意: 思路: 直接求i&j>ki\And j>ki&j>k不是很好求,所以转换成i&jki\And jki&jk的情况。 考虑对a,ba,ba,b求一遍超集,让后从[0,n−1][0,n-1][0,n−1]扫…

推荐10个技术圈优质的公众号大号

公众号有很多但需要什么只有自己知道本次筛选了一批技术圈优质的公众号,主要与python、人工智能、机器学习、技术人生相关希望对你有所帮助!▼★长按二维码,选择“识别二维码”进行关注。▲长按二维码,识别关注简介:Python爱好者社…

使用Kubeadm创建k8s集群之部署规划(三十一)

前言 上一篇我们讲述了使用Kubectl管理k8s集群,那么接下来,我们将使用kubeadm来启动k8s集群。部署k8s集群存在一定的挑战,尤其是部署高可用的k8s集群更是颇为复杂(后续会讲)。因此本教程会在部署的过程中穿插讲…

HDU - 6967 G I love data structure 线段树维护矩阵 + 细节

传送门 文章目录题意:思路:题意: 给你两个长度为nnn的数组a,ba,ba,b,你需要完成如下四种操作: 思路: 思路还是比较简单的,首先建一颗线段树,线段树中维护a,b,a2,b2,aba,b,a^2,b^…

荐读|属性与可直接访问的数据成员之间应该如何选

写在前面在书写C#代码的时候你是否有过这样的经历:经常混用属性以及公有的数据成员。毕竟他们的用法基本一致,对于使用来说好像没什么区别啊。其实我也经常使用类的公有的数据成员来定义一些常量,为了简单,在一些仅仅需要对外暴露…

2021牛客暑期多校训练营3 I Kuriyama Mirai and Exclusive Or 差分 + 二进制分治

传送门 文章目录题意:思路:题意: 给你一个数组aaa,让你实现以下两个操作之后输出数组aaa。 n≤6e5,ai≤230−1n\le6e5,a_i\le2^{30}-1n≤6e5,ai​≤230−1 思路: 下面介绍的思路清奇,反正我想不到。 对…

Lock VS Monitor

介绍介绍对开发人员来说,处理关键代码部分的多线程应用程序是非常重要的。Monitor和lock是c#语言中多线程应用程序中提供线程安全的方法(lock关键字的本质就是对Monitor的封装)。两者都提供了一种机制来确保只有一个线程同时执行代码,以避免代码功能被其…

程序员修神之路--做好分库分表其实很难之二

菜菜哥,上次听你给我讲了分库的情况后,我明白了很多,能再给我讲讲分表吗有收获就好,分表其实有很多情况和分库类似还有不一样的情况吗?有呀,本来数据库和表是不同层面的东西,肯定有差异那你给讲…

2021牛客暑期多校训练营3 B Black and white 最小生成树 + 思维

传送门 文章目录题意:思路:题意: 思路: 对于每个数的位置(i,j)(i,j)(i,j),如果将这个位置染黑,那么我们连一个i−>jni->jni−>jn的边,可以发现我们的操作不影响连通性。如果想要全部染…

关于WinForms的跨显示器DPI自适应

点击上方蓝字关注“汪宇杰博客”导语WinForms 是运行在Windows上的传统.NET桌面应用技术框架。由于历史原因,它对高DPI以及跨不同DPI屏幕的支持有些问题,本文将探索尽可能的解决方案。Windows 的“黑历史”Windows 系统的默认DPI(更确切的说法…

.NET开发框架(九)-NLB网络负载平衡配置实战(视频)

(NLB配置实战教程-有声视频-第二节)请持续关注公众号,第三节(NLBARR)正在录制中~第六章IIS负载均衡教程,至今共有37人参与学习尚未学习第六章-IIS负载均衡-视频教程的童靴,赶紧跟上进度,别掉队了…

C#各版本新增加功能

本系列文章主要整理并介绍 C# 各版本的新增功能。C#8.0 于 2019年4月 随 .NET Framework 4.8 与 Visual Studio 2019 一同发布,但是当前处于预览状态。预计在2019年9月正式发布。目前提供以下功能可供试用:Readonly 成员默认接口成员【*重要,…

宣告推出.NET Core 3.0 Preview 7

译:艾心0626今天,我们宣布推出.NET Core 3.0 Preview 7。我们已经从创建新特性阶段过渡到了完善版本阶段。对于接下来的预览版,我们将把重点放在质量(改进)上。在Windows,macOS和Linux上下载.NET Core 3.0 Preview 7。.NET Core 3…

基于Masstransit实现Eventbus的功能

Masstransit 是一个非常优秀的基于消息进行通信的分布式应用程序框架,详情参考官网。在介绍AA.ServiceBus之前,先介绍下几个概念.分布式分布式系统如何定义?这里引用一下Distributed Systems Concepts and Design(Third Edition)中的一句话&a…

微软解释为什么Rust是系统编程的最佳选择

上周,MSRC(微软安全响应中心)透露出拥抱 Rust 的打算,随后他们将这个话题扩展为一个系列,进一步阐述了使用安全的系统编程语言的的必要性,以及选择 Rust 的原因。在该系列最新一篇文章中,MSRC 团…

你可能不知道的Docker资源限制

本篇内容涉及Docker的内存与CPU限制,可以用于在实际开发中为指定容器设置限制最大使用的资源量,预计阅读时间为5分钟。01—What is 资源限制?默认情况下,容器是没有资源限制的,它会尽可能地使用宿主机能够分配给它的资…

东南亚的IT公司,我劝你善良!

来源公众号:半佛仙人(ID:banfoSB)“真的救我一条‘狗命’,可以吗,谢谢您了,我真的撑不住了。”5月3日,小巴在朋友圈看到这条信息,附着一张长图,定位&#xff…

小白开学Asp.Net Core 《九》

小白开学Asp.Net Core 《九》 — — 前端篇(不务正业)在《小白开学Asp.Net Core 三》中使用了X-admin 2.x 和 Layui将管理后端的界面重新布局了,里面简单的介绍了layui table 的使用以及页面table所需的数据做了简单的封装。今天扩展学习下。…