处理有访问频率限制的 API 请求
如果你的应用需要和第三方服务打交道,则很有可能需要处理接口访问频率限制。下面我们就来看看在队列任务中如何处理有访问频率限制的 API 请求。
简单实现
假设某个第三方 API 接口限制的访问频率是每分钟 30 次,我们可以编写队列任务类的 handle
方法代码如下:
如果返回 429 响应(表示超出接口访问频率限制),则延迟 30 秒后再推送该队列任务重试,这样一来,就可以有效避免持续集中地对该 API 接口进行请求,即便是在超出访问频率限制的情况下做的无用功。
此外我们还限定该队列任务最多只能尝试 10 次,超出后就将其丢到失败任务表中。
不发送可以预知失败的请求
不过上面这个实现比较粗糙,因为如果 API 请求集中在 1 分钟的前 30 秒,那么后 30 秒的重试请求也都是超出访问频率限制的。对于这种明知会失败的 API 请求,发送出去也没有任何意义,浪费系统资源不说,还会阻塞队列中其他待执行的正常任务。
最优的解决方案是能将延迟推送时间精确到该接口下次允许的请求时间,这样推送到队列就肯定可以正常执行了,对应的实现代码是:
具体来说,当触发 API 接口请求次数上限时,在缓存中设置一个键 api-limit
,键值有效期为从接口响应头中读取的 Retry-After
值,即下次允许发起请求的时间。这样一来,这个缓存键的过期之日,就是请求任务重试之时(更准确地说是延迟推送到队列的时间)。
然后我们可以将 api-limit
缓存键放到 handle
方法开头去判断,如果已经过期,则立即推送队列任务,否则延迟推送:
完整示例代码如下:
这样一来,就可以做到 API 请求的精细化控制了,几乎不会再有无效的请求出现(对于单个队列处理器进程而言,每分钟内至多一次无效请求,用于获取 Retry-After
响应头)。
通过过期时间替换尝试次数
由于这个任务可能经常触发频率限制条件,所以定义静态的 tries
属性是不合适的,我们可以定义 retryUtil
方法作为任务执行终止条件:
这里我们定义如果一个队列任务 12 小时后还没有尝试成功,则标记为失败。
限制最大异常次数
如果队列任务执行过程中抛出了未处理的异常,我们肯定不想反复尝试 12 个小时,因此,还可以通过 maxExceptions
属性来设置最大异常次数,超出这个次数,也会终止任务执行:
1 Comment
实际开发中,包括一些大型平台提供的api,并没有这个头 Retry-After,所以实际情况更多还是一个固定值