低延迟的超时中心实现方式

一种低延迟的超时中心实现方式 简介:在很多产品中都存在生命周期相关的设计,时间节点到了之后需要做对应的事情。超时中心(TimeOutCenter,TOC)负责存储和调度生命周期节点上面的超时任务,当超时任务设置的超时时间到期后,超时中心需要立即调度处理这些超时任务。对于一些需要低延迟的超时场景,超时中心调度延迟会给产品带来不可估量的影响。 作者 | 默达 来源 | 阿里技术公众号 一 背景 在很多产品中都存在生命周期相关的设计,时间节点到了之后需要做对应的事情。 超时中心(TimeOutCenter,TOC)负责存储和调度. 阅读详情

简介: 在不少产品中都存在生命周期相关的设计,时间节点到了以后须要作对应的事情。超时中心(TimeOutCenter,TOC)负责存储和调度生命周期节点上面的超时任务,当超时任务设置的超时时间到期后,超时中心须要当即调度处理这些超时任务。对于一些须要低延迟的超时场景,超时中心调度延迟会给产品带来不可估量的影响。sql

做者 | 默达
来源 | 阿里技术公众号数据库

一 背景

在不少产品中都存在生命周期相关的设计,时间节点到了以后须要作对应的事情。并发

超时中心(TimeOutCenter,TOC)负责存储和调度生命周期节点上面的超时任务,当超时任务设置的超时时间到期后,超时中心须要当即调度处理这些超时任务。对于一些须要低延迟的超时场景,超时中心调度延迟会给产品带来不可估量的影响。框架

所以本文提出一种低延迟的超时中心实现方式,首先介绍传统的超时中心的实现方案,以及传统方案中的缺点,而后介绍低延迟的方案,说明如何解决传统方案中的延迟问题。性能

二 传统高延迟方案

1 总体框架

传统的超时中心总体框架以下所示,任务输入后存储在超时任务库中,定时器触发运行数据库扫描器,数据库扫描器从超时任务库中扫描已经到达超时时间的任务,已经到达超时时间的任务存储在机器的内存队列中,等待交给业务处理器进行处理,业务处理器处理完成后更新任务状态。大数据

在大数据时代,超时任务数量确定是很大的,传统的超时中心经过分库分表支持存储海量的超时任务,定时器触发也须要作相应的改变,须要充分利用集群的能力,下面分别从超时任务库和定时器触发两方面详细介绍。阿里云

2 任务库设计

任务库数据模型以下所示,采用分库分表存储,通常可设计为8个库1024个表,具体能够根据业务需求调整。biz_id为分表键,job_id为全局惟一的任务ID,status为超时任务的状态,action_time为任务的执行时间,attribute存储额外的数据。只有当action_time小于当前时间且status为待处理时,任务才能被扫描器加载到内存队列。任务被处理完成后,任务的状态被更新成已处理。url

job_id                        bigint unsigned      超时任务的ID,全局惟一
gmt_create                    datetime             建立时间
gmt_modified                  datetime             修改时间
biz_id                        bigint unsigned      业务id,通常为关联的主订单或子订单id
biz_type                      bigint unsigned      业务类型
status                        tinyint              超时任务状态(0待处理,2已处理,3取消)
action_time                   datetime             超时任务执行时间
attribute                     varchar              额外数据

3 定时调度设计

定时调度流程图以下所示,定时器每间隔10秒触发一次调度,从集群configserver中获取集群ip列表并为当前机器编号,而后给全部ip分配表。分配表时须要考虑好几件事:一张表只属于一台机器,不会出现重复扫描;机器上线下线须要从新分配表。当前机器从所分配的表中扫描出全部状态为待处理的超时任务,遍历扫描出的待处理超时任务。对于每一个超时任务,当内存队列不存在该任务且内存队列未满时,超时任务才加入内存队列,不然循环检查等待。spa

4 缺点

  • 须要定时器定时调度,定时器调度间隔时间加长了超时任务处理的延迟时间;
  • 数据库扫描器为避免重复扫描数据,一张表只能属于一台机器,任务库分表的数量就是任务处理的并发度,并发度受限制;
  • 当单表数据量庞大时,即便从单张表中扫描全部待处理的超时任务也须要花费很长的时间;
  • 本方案整体处理步骤为:先扫描出全部超时任务,再对单个超时任务进行处理;超时任务处理延迟时间须要加上超时任务扫描时间;
  • 本方案处理超时任务的最小延迟为定时器的定时间隔时间,在任务数量庞大的状况下,本方案可能存在较大延迟。

三 低延迟方案

1 总体框架

任务输入后分为两个步骤。第一个步骤是将任务存储到任务库,本方案的任务库模型设计和上面方案中的任务库模型设计同样;第二步骤是任务定时,将任务的jobId和actionTime以必定方式设置到Redis集群中,当定时任务的超时时间到了以后,从Redis集群pop超时任务的jobId,根据jobId从任务库中查询详细的任务信息交给业务处理器进行处理,最后更新任务库中任务的状态。.net

本方案与上述方案最大的不一样点就是超时任务的获取部分,上述方案采用定时调度扫描任务库,本方案采用基于Redis的任务定时系统,接下来将具体讲解任务定时的设计。

2 Redis存储设计

Topic的设计

Topic的定义有三部分组成,topic表示主题名称,slotAmount表示消息存储划分的槽数量,topicType表示消息的类型。主题名称是一个Topic的惟一标示,相同主题名称Topic的slotAmount和topicType必定是同样的。消息存储采用Redis的Sorted Set结构,为了支持大量消息的堆积,须要把消息分散存储到不少个槽中,slotAmount表示该Topic消息存储共使用的槽数量,槽数量必定须要是2的n次幂。在消息存储的时候,采用对指定数据或者消息体哈希求余获得槽位置。

StoreQueue的设计

上图中topic划分了8个槽位,编号0-7。计算消息体对应的CRC32值,CRC32值对槽数量进行取模获得槽序号,SlotKey设计为#{topic}_#{index}(也即Redis的键),其中#{}表示占位符。

StoreQueue结构采用Redis的Sorted Set,Redis的Sorted Set中的数据按照分数排序,实现定时消息的关键就在于如何利用分数、如何添加消息到Sorted Set、如何从Sorted Set中弹出消息。定时消息将时间戳做为分数,消费时每次弹出分数大于当前时间戳的一个消息。

PrepareQueue的设计

为了保障每条消息至少消费一次,消费者不是直接pop有序集合中的元素,而是将元素从StoreQueue移动到PrepareQueue并返回消息给消费者,等消费成功后再从PrepareQueue从删除,或者消费失败后从PreapreQueue从新移动到StoreQueue,这即是根据二阶段提交的思想实现的二阶段消费。

在后面将会详细介绍二阶段消费的实现思路,这里重点介绍下PrepareQueue的存储设计。StoreQueue中每个Slot对应PrepareQueue中的Slot,PrepareQueue的SlotKey设计为prepare_{#{topic}#{index}}。PrepareQueue采用Sorted Set做为存储,消息移动到PrepareQueue时刻对应的(秒级时间戳*1000+重试次数)做为分数,字符串存储的是消息体内容。这里分数的设计与重试次数的设计密切相关,因此在重试次数设计章节详细介绍。

PrepareQueue的SlotKey设计中须要注意的一点,因为消息从StoreQueue移动到PrepareQueue是经过Lua脚本操做的,所以须要保证Lua脚本操做的Slot在同一个Redis节点上,如何保证PrepareQueue的SlotKey和对应的StoreQueue的SlotKey被hash到同一个Redis槽中呢。Redis的hash tag功能能够指定SlotKey中只有某一部分参与计算hash,这一部分采用{}包括,所以PrepareQueue的SlotKey中采用{}包括了StoreQueue的SlotKey。

DeadQueue的设计

消息重试消费16次后,消息将进入DeadQueue。DeadQueue的SlotKey设计为prepare{#{topic}#{index}},这里一样采用hash tag功能保证DeadQueue的SlotKey与对应StoreQueue的SlotKey存储在同一Redis节点。

定时消息生产

生产者的任务就是将消息添加到StoreQueue中。首先,须要计算出消息添加到Redis的SlotKey,若是发送方指定了消息的slotBasis(不然采用content代替),则计算slotBasis的CRC32值,CRC32值对槽数量进行取模获得槽序号,SlotKey设计为#{topic}_#{index},其中#{}表示占位符。发送定时消息时须要设置actionTime,actionTime必须大于当前时间,表示消费时间戳,当前时间大于该消费时间戳的时候,消息才会被消费。所以在存储该类型消息的时候,采用actionTime做为分数,采用命令zadd添加到Redis。

超时消息消费

每台机器将启动多个Woker进行超时消息消费,Woker即表示线程,定时消息被存储到Redis的多个Slot中,所以须要zookeeper维护集群中Woker与slot的关系,一个Slot只分配给一个Woker进行消费,一个Woker能够消费多个Slot。Woker与Slot的关系在每台机器启动与中止时从新分配,超时消息消费集群监听了zookeeper节点的变化。

Woker与Slot关系肯定后,Woker则循环不断地从Redis拉取订阅的Slot中的超时消息。在StoreQueue存储设计中说明了定时消息存储时采用Sorted Set结构,采用定时时间actionTime做为分数,所以定时消息按照时间大小存储在Sorted Set中。所以在拉取超时消息进行只需采用Redis命令ZRANGEBYSCORE弹出分数小于当前时间戳的一条消息。

为了保证系统的可用性,还须要考虑保证定时消息至少被消费一次以及消费的重试次数,下面将具体介绍如何保证至少消费一次和消费重试次数控制。

至少消费一次

至少消费一次的问题比较相似银行转帐问题,A向B帐户转帐100元,如何保障A帐户扣减100同时B帐户增长100,所以咱们能够想到二阶段提交的思想。第一个准备阶段,A、B分别进行资源冻结并持久化undo和redo日志,A、B分别告诉协调者已经准备好;第二个提交阶段,协调者告诉A、B进行提交,A、B分别提交事务。本方案基于二阶段提交的思想来实现至少消费一次。

Redis存储设计中PrepareQueue的做用就是用来冻结资源并记录事务日志,消费者端便是参与者也是协调者。第一个准备阶段,消费者端经过执行Lua脚本从StoreQueue中Pop消息并存储到PrepareQueue,同时消息传输到消费者端,消费者端消费该消息;第二个提交阶段,消费者端根据消费结果是否成功协调消息队列服务是提交仍是回滚,若是消费成功则提交事务,该消息从PrepareQueue中删除,若是消费失败则回滚事务,消费者端将该消息从PrepareQueue移动到StoreQueue,若是由于各类异常致使PrepareQueue中消息滞留超时,超时后将自动执行回滚操做。二阶段消费的流程图以下所示。

消费重试次数控制

采用二阶段消费方式,须要将消息在StoreQueue和PrepareQueue之间移动,如何实现重试次数控制呢,其关键在StoreQueue和PrepareQueue的分数设计。

PrepareQueue的分数须要与时间相关,正常状况下,消费者无论消费失败仍是消费成功,都会从PrepareQueue删除消息,当消费者系统发生异常或者宕机的时候,消息就没法从PrepareQueue中删除,咱们也不知道消费者是否消费成功,为保障消息至少被消费一次,咱们须要作到超时回滚,所以分数须要与消费时间相关。当PrepareQueue中的消息发生超时的时候,将消息从PrepareQueue移动到StoreQueue。

所以PrepareQueue的分数设计为:秒级时间戳*1000+重试次数。定时消息首次存储到StoreQueue中的分数表示消费时间戳,若是消息消费失败,消息从PrepareQueue回滚到StoreQueue,定时消息存储时的分数都表示剩余重试次数,剩余重试次数从16次不断下降最后为0,消息进入死信队列。消息在StoreQueue和PrepareQueue之间移动流程以下:

5 优势

  • 消费低延迟:采用基于Redis的定时方案直接从Redis中pop超时任务,避免扫描任务库,大大减小了延迟时间。
  • 可控并发度:并发度取决于消息存储的Slot数量以及集群Worker数量,这两个数量均可以根据业务须要进行调控,传统方案中并发度为分库分表的数量。
  • 高性能:Redis单机的QPS能够达到10w,Redis集群的QPS能够达到更高的水平,本方案没有复杂查询,消费过程当中从Redis拉取超时消息的时间复杂度为O(1)。
  • 高可用:至少消费一次保障了定时消息必定被消费,重试次数控制保证消费不被阻塞。
设计模式之美91-项目实战一:设计实现一个支持各种算法的限流框架(设计) 上一节课,我们介绍了限流框架产生的项目背景,并且对需求做了分析,这其中包括功能性需求和非功能性需求,算是在正式开始设计之前的一个铺垫。前面提到,我们把项目实战分为分析、设计、实现三部分来讲解。其中,分析环节跟之前讲过的面向对象分析很相似,都是做需求的梳理。但是,项目实战中的设计和实现,跟面向对象设计和实现就不是一回事儿了。这里的“设计”指的是系统设计,主要是划分模块,对模块进行设计。这里的“实现”实际上等于面向对象设计加实现。 阅读详情

相关推荐

六、超时与重试机制——Web容器超时与中间件客户端超时与重试

在高并发和高负载的生产环境中,Web容器的超时配置对系统性能和稳定性至关重要。Tomcat 8.5 是广泛使用的 Java Web 容器,以下是对其主要超时设置参数的详细解析,以帮助优化和调整系统的性能。在中间件系统中,如 Dubbo,超时和重试机制对于确保系统的稳定性和性能至关重要。以下是详细的配置说明及不同场景下的配置建议。以下是针对常见场景的全面配置示例,以及如何根据实际需求调整这些配置参数。Tomcat 的超时设置对不同场景的应用性能和稳定性具有重要影响。

流楚丶格念的博客 1403

架构设计内容分享(二十二):100亿订单超时处理,阿里的方案是啥?

注意:时间刻度为1s,没有再细,比如 10ms、100ms之类的。作为参考下面提供一个一分钟的,以1s为刻度的时间轮,如下图:图中是一个 60s 的时间轮,每一个槽位是一个链表,链表里边的节点,通过TimerLog节点结构来记录不同时刻的消息。所以,RocketMQ 使用 TimerWheel 来描述时间轮,TimerWheel 中每一个时间节点是一个 Slot,Slot 保存了这个延时时间的 TimerLog 信息的链表。

之乎者也·的博客 2499

74LS161/48 交通灯控制系统:30-5-20-5 时序逻辑电路 Multisim 仿真验证

本文详细解析了基于74LS161/48芯片的交通灯控制系统设计,通过Multisim仿真验证30-5-20-5时序逻辑电路。从系统架构搭建、状态机实现到波形分析,提供全流程操作指南与典型问题排查方法,助力数字电路课程设计高效完成。

weixin_26742753的博客 377

阿里 TOC(超时中心)深度解析:设计原理与实现方式

阿里TOC(TimeoutCenter)是集团统一的分布式超时任务中台,面向订单、退款等海量业务场景,解决精准触发、高可用等核心问题。采用"时间分桶+分布式分片+规则引擎"架构,通过多级分桶策略缩小扫描范围,结合一致性哈希分片实现任务并行处理。系统具备完整的任务生命周期管理、动态规则引擎、阶梯式重试等机制,支持亿级任务/天的处理能力,可用性达99.99%。与普通定时任务相比,TOC在中台化设计、精准触发和运维能力等方面具有显著优势,其核心设计思路可为构建分布式任务系统提供重要参考。

weiye2012的博客 1073

SpringBoot项目启动后ShardingSphere5.0首次SQL查询慢?这个预热技巧帮你搞定

本文针对SpringBoot项目集成ShardingSphere 5.0后首次SQL查询慢的问题,深入剖析了其根源在于SQL解析引擎的懒加载与缓存初始化。文章提供了三种有效的预热技巧,包括精准的SQL解析引擎预热、简单查询触发全链路预热以及可配置的生产级方案,帮助开发者将初始化成本提前至应用启动阶段,从而消除性能毛刺,提升用户体验与系统稳定性。

chair的专栏 1062

【鸿蒙南向开发】OpenHarmony——分布式数据对象之功能实践

通过本文的学习可了解到分布式数据对象的主要接口、大致调用方法和功能实现等。结合对应内容,可以进一步对分布式数据对象同步机制做深度学习。下一步,(1) 着重理解同步组网的构建原理。(2) 着重理解同步组网内,监听change和status变化的实现。●如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:●关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。

HarmonyOS_666的博客 1089

订单超时怎么处理?看看阿里的方案

如果对于超时精度比较高,超时时间在 24 小时内,且不会有峰值压力的场景,推荐使用 RocketMQ 的定时消息解决方案。在电商业务下,许多订单超时场景都在 24 小时以上,对于超时精度没有那么敏感,并且有海量订单需要批处理,推荐使用基于定时任务的跑批解决方案。

LoveSummer 1万+

用AI生成React/Vue页面?实测Locofy等5款工具,教你选最适合的

本文深度评测了2024年五大AI生成前端页面工具,包括Locofy、Builder.io等,从代码质量、可视化编辑器成本到企业级落地方案进行全面分析。通过实测数据和实战案例,揭示如何利用AI生成技术高效创建React/Vue页面,提升开发效率并优化性能。

m5n6o7的博客 606

配置中心动态更新的实现方式

客户端发起请求后,服务端若配置无变更则保持连接,直到变更或超时。:建立全双工通信通道,服务端主动推送变更。

刘皇叔说Java的博客 762

Ubuntu22.04 网络图标消失与连接故障排查指南(附实战修复流程)

本文详细介绍了Ubuntu22.04系统中网络图标消失与连接故障的排查与修复方法。通过ifconfig、dhclient等命令诊断问题,提供从紧急恢复网络连接到深度排查的完整解决方案,帮助用户快速恢复网络功能并预防类似问题再次发生。

weixin_28481133的博客 287

手把手教你学AI GPU的UMD驱动软件开发专栏简介

通过本专栏的学习,您将掌握UMD的核心概念、开发环境搭建、API实现、内存管理和同步技术,以及在实际AI场景中的应用。通过本专栏的学习,您将不仅掌握UMD驱动开发的核心技术,还将能够将这些技术应用到实际的AI应用场景中,为您的AI应用提供更高效的硬件加速支持。无论您是想开发自己的AI加速框架,还是希望深入理解现有AI加速库的工作原理,本专栏都将为您提供宝贵的知识和实践经验。:本专栏不仅讲解UMD驱动的核心概念,还通过精心设计的实战案例,引导您从零开始构建UMD驱动,真正掌握AI GPU驱动开发的技能。

MHD0815的博客 113

Spring Cloud实战:OpenFeign超时配置的3种方式及最佳实践

本文详细解析了Spring Cloud中OpenFeign超时配置的三种实现方式,包括全局配置、配置文件配置和配置类局部配置,并提供了生产环境下的最佳实践。通过合理设置openFeign的超时时间,可以有效提升微服务架构的稳定性和性能,避免因网络抖动或服务延迟导致的系统问题。

weixin_29261711的博客 399

华为OD机试真题 新系统2026-04-01 Python&JS 实现【勇攀数字高峰】

请你在给定的数字地形图中寻找登山路径,数字代表当前位置的海拔高度,要求从最低海拔出发,不断攀登,最终到达最高山峰。你需要寻找所有满足条件的登山路径。地图已经保证最低海拔和最高山峰都只有一个。

MISAYAONE的博客 847

NACOS作为注册中心和配置中心的功能及实现方式

步骤 1:首次拉取实例列表消费者启动时,通过 HTTP GET 请求(/nacos/v1/ns/instance/list)向 Nacos 服务端请求目标服务的实例列表,请求参数包含:服务名、分组、命名空间、健康状态过滤条件(如只拉取健康实例)。服务发现是消费者客户端从 Nacos 服务端获取服务实例列表,并实时感知实例变化的过程,Nacos 通过 “拉 + 推” 结合的方式(长轮询)保证实时性,通过本地缓存 保证容错性。若为持久化实例(CP):无需心跳,依赖手动管理生命周期(上线 / 下线)。

weixin_39650169的博客 944

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

GOLANG实现超时对象检测的最好理解的方式

原文:https://gocn.io/article/328依赖于心跳的系统,都需要超时检测。比如P2P系统中客户端每隔120秒向数据服务器发送一次数据汇总,服务器就需要维护一个超时时间。比如一个UDP服务器,在和客户端之间创建Session之后,如果没有数据包,一般会有Ping包,说明这个Session是存活的,服务器在发现Session超时后也需要清理。首先,服务器一般需要维护一个列表,以Pee

OSSRS 3092

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

基于Redis键过期实现订单超时自动关闭:一套优雅的事件驱动方案

用户下单后如果30分钟内未支付,需要自动关闭订单。传统的解决方案是使用定时任务轮询数据库❌ 频繁扫描数据库,性能开销大❌ 存在时间误差,不够实时❌ 大量无效查询,浪费资源今天给大家介绍一种基于Redis键过期事件的优雅解决方案,实现真正的无轮询、低延迟订单超时处理!特性传统定时任务Redis事件方案性能❌ 频繁扫库✅ 无轮询开销实时性❌ 最低扫描间隔✅ 毫秒级响应资源消耗❌ 高CPU/IO✅ 极低开销准确性❌ 存在延迟✅ 精确触发扩展性❌ 单点瓶颈。

qq_38669579的博客 1739

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

使用hiredis实现pipeline方式访问

1.介绍 hiredis: 一个c/c++的访问redis的api库 地址:https://github.com/redis/hiredis pipeline方式: redis中的pipeline方式,指的是把多次请求交互封装到一次完成,只交互一次,类似于多个请求“批处理”成一次交互 好处: 同样是多次请求,用pipeline方式比多次请求的总的延时低,交互次数少, 即低延迟,高吞...

weixin_30722589的博客 736

Go-MySQL-Driver超时控制:读写超时和连接超时的精细配置

在现代分布式系统中,数据库连接超时控制是确保应用稳定性的关键因素。想象一下这样的场景:你的Go应用在生产环境中突然出现数据库连接堆积,导致整个系统响应缓慢甚至崩溃。这正是缺乏合理超时配置可能带来的灾难性后果。 Go-MySQL-Driver作为Go语言中最流行的MySQL驱动之一,提供了三种关键的超时控制机制:连接超时(Connection Timeout)、读取超时(Read Timeout)...

gitblog_00504的博客 851
上一篇: 从 Mybatis 源码中,学习到的 10 种设计模式
下一篇: 延迟任务场景,该如何提高吞吐量和时效性!
wrr-cat
博客等级 码龄9年 128粉丝 302原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值