微服务中台技术之延迟中心实践
背景
在电商业务场景中,我们会经常遇到需要使用延迟服务,比如用户下单后需要在一定时间内完成支付,物流开始一段时间后发送通知等,这些场景的特点是请求不是立即生效,而是在延时一段时间后再发出请求执行,即延迟执行。
技术方案
为了达到延迟执行的目的,我们需要暂存请求,然后定时轮询延时是否到期,到期则取出延时任务执行。在设计目标上,要保证延时任务不丢失,同时查询效率要高。为此,我们实现了以redis + kafka
的架构,既保证了延时任务的可靠性,还兼顾了轮询效率,如下图所示:
其中,
redis 主要是使用其 zset 机制,利用其排序功能快速找出延时到期的任务
kafka 主要是用来保存到期任务,确保到期任务能按时执行,不会丢失
下面我们简要介绍下整个执行流程:
用户调用延时注册接口注册延时任务(已提前申请好业务标识与回调地址等,存于配置中心组件)
延时中心收到请求后,将其写入 redis zset(score 为到期时间)
redis worker 定时轮询(e.g. 30s, 取决于延时精度),将到期任务(score 小于当前时间的任务)发送到 kafka 指定 topic
kafka worker 消费指定 topic,取出到期任务,通过 callback 地址回调业务方接口,回调成功则 commit 该消息
注意事项
上面的方案在实施过程中还需要注意以下几点:
在查询 redis 获得到期任务并发送到 kafka 后,需要删除 redis 中对应的延时任务,否则会造成延时任务重复执行。具体方案是增加重试机制,删除 redis 失败的任务会丢到 kafka retry topic,消费后重新执行删除。
为了保证延迟中心服务的高可用,一般是多实例部署,因此在轮询 redis 时会出现争抢,引起并发问题,因此需要引入分布式锁解决并发轮询问题(e.g. redisson 组件或者 zk 实现分布式锁)。
在整个设计方案中,我们保证的延时任务的可靠性与轮询的精度和效率,但不保证延时任务的重复投递(虽然我们通过失败重试机制尽可能保证减少重复投递的可能,但无法完全避免重复投递,我们保证的是最少投递一次),因此业务方回调接口需要保持幂等。
总结与思考
本文基于redis + kafka
实现了一个具备高可用的延迟中心服务,上线后运行情况良好,下一步从反馈中将持续优化该服务,比如支持 dubbo 接口注册延时任务,支持业务方自定义消息队列(方便自行消费),采用异步方式回调业务方接口等。
大家有什么好的想法或者意见,欢迎留言探讨~
版权声明: 本文为 InfoQ 作者【小江】的原创文章。
原文链接:【http://xie.infoq.cn/article/75d09894c5aab3a143936af55】。文章转载请联系作者。
评论