分布式异步回调模型的回调策略


  • Monday, Nov 5, 2018

一、背景

客户端请求Web服务架构中,一般有同步阻塞模型和异步回调两种模型。对于服务端耗时较长,例如音视频转码等重操作的服务,异步回调模型相比同步模型有许多的优势:

  • 不会阻塞客户端的请求线程,可以提高客户端的线程利用率。
  • 服务端根据自身的处理能力进行处理,保证服务端的稳定性。不会由于峰值请求造成服务端过载。

但是,在异步回调模型中,由于多了一次回调的链路,会带来更多的可用性问题。因此,本文主要讨论在异步回调模型中,如何制定有效的回调策略来保证回调链路的成功率,并提高整个异步服务的可用性。

二、异步回调模型

异步回调模型可以参考引用1中的描述,一般有以下两种细分模型:

2.1 Asynchronous Web Service Using a Single Request Queue

2.2 Asynchronous Web Service Using a Request and a Response Queue

其中2.2的模型虽然更复杂,但可以有效的提高服务端的资源使用率。避免由于回调阻塞导致处理能力的下降。同时可以增加一些判重策略,防止回调服务出现故障时,由于客户端重试导致服务端重复处理的资源浪费。

在分布式服务场景下,2.2 模型还可以细分为Inner Response Queue及outer Response Queue两种:

  1. Inner Response Queue为每个服务端app使用内存队列作为Response Queue,由内部的线程作为Callback Client。
  2. Outer Response Queue为使用统一的消息队列中间件作为Response Queue,另外部署一套Callback Client服务来处理这些Response。

Outer模型相比Inner模型部署结构较为复杂,但与处理结构完全解耦,可以针对回调做更多策略,同时可以防止由于处理app宕机造成的Response丢失(不过由于callback client以及消息中间件策略的问题,仍然会存在response丢失的风险)。

以上各个模型各有优劣,应该根据业务场景选择合适的模型。

三、回调策略

对于异步回调模型,callback service一般由业务方提供,无法对可用性做保证。因此callback client必须要制定一些策略尽量的应对callback service失败的情况。尤其是在一个callback client对应多个callback service的场景下,需要尽量防止由于某些service的问题,影响回调其他service的情况。

Callback Service 失败场景:

1. client与service之间出现网络波动,甚至中断。

callback client请求service时,响应域名解析失败或者client请求超时。
快速重试策略可以解决网络波动问题。轮询重试策略可以解决短时间中断问题。

2. service超时。

callback service处理回调超时。这种情况除去网络问题,一般是由于service负载过高,或者设计存在问题导致。这种场景如果过度重试一般会造成service雪崩。解决的方案是周知业务方,由业务方对callback service进行排查。
因此,当出现这种情况时,需要能尽快的感知到。感知的方案可以是记录错误日志以及进行报警,但报警策略需要合理设计,防止由于业务方的原因影响正常的报警。 更为合理的callback service设计是: 收到回调请求时,直接响应202(Accepted)状态码,表示已经收到回调请求。callback client不需要关注service的处理结果。

3. service服务异常响应。

当callback service响应异常的响应码时,表示service此时无法接受回调请求。一般可以通过重试策略解决这类问题,如果多次重试后仍然失败,则需要周知业务方进行排查。

4. service响应时间过长。

callback service 合理的设计不应该等处理完后再响应client,而是收到请求直接响应202状态码。
但对于某些特殊的场景,callback service可能希望如果回调请求处理不成功,client可以重新发起回调,这样可以简化一些callback service的设计。因此client可以通过重试策略解决这种场景。同时要注意防止某个service响应时间过长影响其他service的回调,解决的方案是对不同的callback service使用不同的response queue。或者增加callback client的并发来减少这种情况的影响。

5. ex

通过总结以上回调策略,得出的最大结论是: 合理的设计callback service来应对各种场景,callback client能做的实在有限。

四、重试策略

1. 快速重试策略。

回调失败后,等待毫秒级别的间隔后重试。这种策略的重试次数一般较少,可以解决一些由于服务或网络波动导致的小概率离散型回调失败(离散型回调失败是指同一个时间点的回调,有些成功,有些失败)。是最为常用的重试策略。

2. 轮询重试策略(短延时重试策略)

回调失败后,等待一定间隔后,将该条回调重新写入Response Queue进行重试,重写的次数可以较多。该策略可以解决由于服务或网络波动导致的短时间连续型回调失败(连续型回调失败是指某个时间段,所有的回调请求都失败)。但这种策略会导致回调的请求量大量放大,对callback service/client 造成一定的压力,甚至造成雪崩。

如果Response Queue中有多个callback service,当某些service出现问题,某些是正常的时候,这种策略会对正常的callback service造成一些影响。解决的方案是将失败的回调写入另外一个failover response queue。

3. 长延时重试策略

回调失败后,等待较长的时间(小时及以上级别)再进行重试。这种策略可以在Callback Service出现网络中断或者服务故障的时候,保证一定的回调成功率,防止由于回调失败造成的无效处理。适用于处理资源消耗极高、回调延时不敏感的服务,例如离线计算任务。

该策略的实现方式一般依赖于Outer Response Queue模型,需要额外的消息中间件来保存回调。

4. 记录手动重试策略

回调失败后,记录回调内容。后期再利用工具手动进行重试。这种策略和延时重试策略解决相同的失败情况。这两种策略主要解决由于callback client和service之间较长时间段不可通信的场景,例如网络中断,callback service故障。这种策略相比长延时重试策略更加灵活,因为当出现这类问题的时候,需要等待多久后以及如何重试都是不可控的。

两种策略都会带来较多额外的开发和部署成本。

五、轮询回调模型设计

轮询回调模型可以较好的覆盖各种回调失败场景。整体流程大致如图:

  1. Request MDB 处理完请求后将Response写入Response Queue。
  2. Callback Client 从Response Queue中取出Response。
  3. Callback Client 根据Response执行回调。如果回调失败,则采用快速重试策略进行快速重试。
  4. Callback Client 执行快速重试策略后仍然无法回调成功,则将Response 写入Failover Response Queue。Callback Client 重新执行步骤2。
  5. Failover Callback Client 从Failover Resonse Queue中取出Response。
  6. Failover Callback Client 根据Response 执行回调。如果回调失败,则采用快速重试策略进行快速重试。
  7. Failover Callback Client 执行快速重试策略后仍然无法回调成功,则将Response 写入Failover Response Queue。Failover Callback Client 重新执行步骤5。

该模型有几个需要注意的地方:

  • Failover 流程中,每个Response的二次failover需要增加一个时间间隔。否则可能出现failover callback client不停重试一个callback,对callback service造成雪崩。通过设置这个failover interval以及failover times,则可以基本计算出最长可重试的时间范围。公式为 (interval+x)*times,其中x为平均每次callback消耗的时间。
  • 当超过failover times limit,仍然无法重试成功,则应该对这条Response进行记录。提供之后手动重试的可能性。

六、引用

  1. Apache Developing Asynchronous Web Services

本文地址