在开发过程中,应用程序通常会和其他的应用进行交互,应用系统之间的交互往往离不开网络通信。然而,网络环境是不稳定的,网络超时是我们需要考虑的问题。
交互模式
同步
同步调用
同步调用接口返回两种状态,这两种状态都是终态,成功S或者失败F。同步调用会阻塞等待返回结果,如果长时间没有结果返回则会等待超时。
异步
异步调用
异步调用会返回两次结果,一次是同步返回一次异步返回。同步返回告知调用方请求已经受理,异步返回告诉调用方请求成功或失败。
消息队列
消息队列交互
为什么要使用消息队列?
接口异步化;
服务之间解藕;
消峰。
超时问题的解决方案
下面来分析在上面提到的三种交互模式中出现调用超时的情况,并提出相应的解决方案。我们将从两个角度:服务调用方(客户端)和服务提供方(服务端),分析出现超时,客户端和服务端应该如何做。
同步超时
超时可能发生的点
同步调用超时发生的点
在同步调用的模式下,超时可能发生的节点有以下三处:
请求超时,客户端给服务端发送请求时超时,此时服务端没有收到客户端的请求;
服务端内部超时,服务端可能存在DB操作、IO操作、调用其他服务超时;
响应超时,服务端给客户端返回响应时超时,此时服务端已经处理了请求。
客户端
无论是何种超时,对于客户端来说都是透明的,即客户端无法知道具体发生超时的点。客户端对于超时的处理,有如下两种常见方法:
查询,通过主动查询去拉取超时请求的状态。这种方法需要服务端提供查询接口,并且是根据客户端生成的请求流水号作为查询的条件,因为同一个服务或者接口可能会存在多个调用方,这就需要服务端能够唯一标识某一个客户端请求。关注顶级架构师
如何唯一标识客户端请求,有以下两种方案可供参考.
1. 全局流水号(traceId),全局流水号需要在所有调用方之间唯一,这可能需要在全公司层面有分布式发号器的应用。
2. 如果在调用方的应用有不属于公司的应用,那么全局流水号可能就不好控制了,这个时候可以使用:productId+productSeqId的组合形式。productId表示接入方的系统号,productSeqId表示接入方的流水号,productSeqId需要保证在同一个productId内是唯一的。这样服务端就可以通过
productId + productSeqId
来唯一标识客户端的请求。
重试,需要设置重试梯度(5s,30s,1min…),以及重试次数的阈值(最多重试的次数)。另外,客户端的重试需要服务端支持幂等(多次执行和只执行一次的效果一样)。
服务端
对于①.请求超时
和③.响应超时
服务端是无法感知的,也就没法进行处理。而在②.服务端内部超时
时服务端应该快速失败,立即响应客户端。如果是服务端调用其他服务(例如,服务C)超时,服务端除了快速失败之外,还需要调用服务C的冲正操作。
服务端内部调用其他服务超时
服务C的冲正接口需要能够判断之前是否接收过服务端超时的请求,如果接收过请求并做了处理,则应该执行反向的回滚操作,如果没有接收过,则忽略冲正请求。另外搜索公众号程序员小乐后台回复“赚钱”,获取一份惊喜礼包。
异步超时
超时可能发生的点
异步调用模式下超时发生的点
在异步调用的模式下,超时可能发生的节点有以下四处:
请求超时,客户端给服务端发送请求时超时,此时服务端没有收到客户端的请求;
服务端内部超时,服务端可能存在DB操作、IO操作、调用其他服务超时;
同步响应超时,服务端同步返回响应给客户端超时,此时服务端已经接收了请求。
异步响应超时,服务端异步返回响应给客户端超时,此时服务端已经处理完了请求。
客户端
此时客户端的处理方式和同步调用时客户端的方式一样。
服务端
服务端对于请求超时
和同步响应超时
无能为力,不过对于异步响应超时
、服务端内部超时
是可以处理的,具体如下:
对于异步通知超时
可以采用最大努力通知
,服务端要求客户端在收到异步通知时明确回应服务端接收成功,如果服务端没有收到客户端的回应,服务端重发异步结果。关于异步结果通知超时处理具体可以参考微信支付中的支付结果通知文档
服务端内部超时
,我们应该尽最大努力使得用户的请求处理成功。如果是服务端调用其他服务超时,可以通过查询其他服务,根据查询到的结果再进行后续的操作,并将最终的结果通过异步通知反馈给客户端。
消息队列超时
# 超时可能发生的点
消息队列模式下超时发生的点
在消息队列模式下,超时可能发生的点有两处:
生产者投递消息超时,对应上图的①,②;
消费者消费消息超时。
生产者超时
生产者超时一般都采用可靠消息服务来解决,具体请参考后续的文章。
消费者超时
一般在开发过程中,基本上都可以认为只要生产者将消息投递到了MQ中间件的服务端,那么该消息就一定会被消费者所消费,这主要是基于对消息中间件的信赖。一般而言,各大MQ中间件都有一定的机制来保障其到消费者之间的消息不会丢失。
不同MQ中间件的消费者机制有所不同,大概可以概括成以下两类:
一旦消费者从消息中间件取走消息(无论是推模式或者拉模式都一样),不管消费者是否成功处理,消息中间件都会将该条消息删除;
消费者从消息中间件取走消息之后,消息中间件不会立马将该消息删除,必须要等到消费者告知消息中间件已经处理完了该消息后,消息中间件才会将消息进行删除。
所以在使用消息中间件的时候,我们必须得清楚这个消息中间件产品,它消息消费的具体逻辑是怎样的。
往期推荐
下次遇到需要搭建公司秒杀架构,打开这套架构直接用...,细节都考虑到了!
Spring Boot 实现通用 Auth 认证的 4 种方式!
回复【干货】获取精选干货视频教程
回复【加群】加入疑难问题攻坚交流群
回复【mat】获取内存溢出问题分析详细文档教程
回复【赚钱】获取用java写一个能赚钱的微信机器人
回复【副业】获取程序员副业攻略一份
好文请点赞+分享