我们经常遇到这样的场景,比如有两个模块A、B,A模块调用B模块的API,然后等待B模块的响应。
模块A和模块B可以处理不同的进程、节点,API的调用可以是本地调用,也可以是远程调用。
一些请求对于时间是比较敏感的,所以A模块在调用B模块的API的时候,可能会给出一个超时时间,如果达到超时时间,请求没有返回,那么A模块会触发超时事件,A模块会进行一些相应的处理,比如abort请求,通知B模块abort请求,释放相关资源等等。
对于超时场景,我们让A在发送请求的时候,携带一个起始时间(可选)和超时时间。携带起始时间,是为了B模块可以更精确的判断出请求是否超时。
超时处理
当A模块发现超时事件后,A模块的处理方式如下:
- 对于一些简单的请求(视业务而定,比如一些无关紧要的读请求),A模块可以直接abort A模块发起的请求,不关心B模块的处理结果,也不关心请求在B模块是否还会继续执行。
- 对于一些复杂的请求(视业务而定,比如一些可能改变系统状态的写请求),A模块可能需要把请求本身持久化,然后才开始调用B的API发送请求。并且在请求超时后,A模块可以:
2.1 持续重试直到成功(B模块需要有滤重的功能,每个请求有一个唯一的ID)。
2.2 持续abort请求直到成功(B模块需要支持abort)。
B可选的功能:
- 如果B模块可以发现A模块的请求已经超时,那么可以主动abort请求,因为请求超时,这个请求对A可能已经没有意义了,B模块也没有必要再浪费资源去完成了。
- B模块如果自己abort了请求,可以考虑通知一下模块A,虽然这可能是非必须的。因为A模块可能自己也能发现超时。
超时发现
超时由谁来发现呢,原则是谁关心(谁设置的超时),那么由谁来发现。在上面的例子中,就是A必须要有超时发现机制。另外请求接收方,也可以有发现机制,但这不是必须的。如果请求接收方可以发现超时,那么可能可以节约一些资源(对于超时的请求不再处理)。实践上这个是有点难度的,因为A、B可能运行在不同的节点上,系统时间也可能不太一样。当然B可以以B接收到请求的时刻开始算超时时间,虽然不太准确,但是也比没有超时强。