作者:小傅哥
博客:https://bugstack.cn - 包含: Java 电脑 基础,面经手册,Netty4.x,手写Spring,用Java实现JVM,重学Java设计模式,SpringBoot中间件开发,IDEA插件开发,DDD开发系统架构项目,字节码编程...
一、前言沉淀、分享、成长,让自己和他人有所收获!
不卷,可以用!
一、前言
不卷,可以用!哈哈哈,说好的不卷,可以凑活用。然而,每次我收到新的需求,我都会感到痒。我想结合上次的架构设计和着陆经验,迭代更新这个需求,或者找到一个更好的解决方案来完全逆转以前。卷完代码的那一刻总是神清气爽事实上,大多数喜欢写代码的纯代码农都是相对卷曲的。例如,实现需求可以使用P5.如果这个功能不仅可以使用,而且非常有用P6.开发成通用组件服务除了使用方便外,还具有浓缩的共性需求P7。每一个成长起来的码农,都是在造轮子的路上一次次验证自己的想法和实践,绝对不是一篇八股文就能累出一头高级技术牛。二、延迟任务场景延迟任务是什么?在我们的实际业务需求场景中,有一些活动开始前状态变化,订单结算后T 对账、贷款单息费的产生,都需要使用延迟任务来触及。通常会有实际操作 Quartz、Schedule 定期扫描和处理您的库表数据,在满足条件后更改数据状态或将新数据插入表中。这样一个简单的需求是延迟任务的初始需求。如果前期需求内容少,用户少,可能只是单台机器在实际开发中直接轮训。但随着业务需求的发展和功能的复杂性,往往反馈到研发设计和实现,不那么简单,如:您需要确保尽可能低的延迟完成大规模的数据扫描处理,否则像贷款利息,第二天用户没有看到他们的利息信息或还款后,可能会产生客户投诉。那么,如何设计这样的场景呢?电脑三、延迟任务设计
通常,任务中心的处理过程主要是通过定时任务扫描任务库表,将即将到来的任务信息扫描到处理队列(内存/MQ新闻),然后由业务系统处理任务,完成后更新库表中的任务状态。
高延迟任务调度
问题
:需要快速扫描大量数据的任务列表数据。任务扫描服务与业务逻辑处理耦合,不具有通用性和重用性。有些细分任务系统需要低延迟处理,不能等太久。
1. 任务表方式除了一些较小的状态变更场景外,例如,在各自业务的库表中,还包含了一个状态字段。一方面,该字段具有程序逻辑处理变更状态,并在达到指定到期时间后由任务服务自动更改。一般来说,这些功能可以直接设计到自己的库表中。
然后还有一些大而频繁使用的场景。如果在每个系统需要的N多个表中添加此类字段进行维护,则非常冗余,不易维护。因此,对于这样的场景,非常适合制作一个通用的任务延迟系统。每个业务系统将需要延迟执行的动作提交给延迟系统,然后延迟系统在指定时间内进行回调。回调动作可以是接口或MQ触摸新闻。例如,可以设计这样的任务调度表:
设计任务调度库表提取的任务调度表主要是什么任务,什么时候启动行动,具体行动处理仍交给业务工程处理。集中处理各自业务的大量任务,需要设计分库分表,以满足后续业务量的增长。门牌号设计,对于一张表的扫描,如果数据量大,不想只是一个任务扫描一张表,可以多个任务扫描一张表,增加扫描量。此时需要一个门牌号来隔离不同任务扫描的范围,避免扫描重复的任务数据。2. 低延迟方式
低延迟处理方案是在任务表模式的基础上控制新的时间。它可以把即将到期的前一段时间的任务放在一边 Redis 在集群中,消费时从队列中 pop 这样可以更快地接近任务的处理时效,避免因扫库间隔大而延迟任务的执行。
任务处理过程在接收业务系统提交的延迟任务时,根据执行时间的长度放置在任务库或同步 Redis 在集群中,一些执行时间较晚的任务可以先放入任务库,然后通过扫描添加到执行队列中。所以这个设计的核心是 Redis 为了保证消费的可靠性,需要引入二阶段消费和注册 ZK 注册中心至少保证一次消费处理。
本文的重点是 Redis 队列设计,其他逻辑处理,可根据业务需求进行扩展和改进Redis 消费队列Redis 消费队列相应数据的槽位按消息体计算 index = CRC32 & 7StoreQueue 采用 Slot 按照 SlotKey = # opic}_#{inde
和 Sorted Set 数据结构按执行任务分数排序,存储任务执行信息。定时消息以时间戳为分数,消费时每次弹出分数小于当前时间戳的消息为了确保每个消息至少可以消费一次,消费者不是直接的 pop 有序地集中元素,而是从 StoreQueue 移动到 PrepareQueue 并将消息返回给消费者。成功消费后再从 PrepareQueue 从删除,如果消费失败,从PreapreQueue 重新移动到 StoreQueue,处理二阶段消费的方式。参考文档:2021 阿里技术人员百宝黑皮书PDF文,实现低延迟超时中心的方法
简单案例
@Testpublic void test_delay_queue() throws InterruptedException{ RBlockingQueue<Object> blockingQueue = redissonClient.getBlockingQueue("TASK"); RDelayedQueue<Object> delayedQueue = redissonClient.getDelayedQueue(blockingQueue); new Thread(() ->{ try{ while (true){ Object take = blockingQueue.take(); System.out.println(take); Thread.sleep(10); }}catch (InterruptedException e){ e.printStackTrace(); }}).start(); int i = 0; while (true){ delayedQueue.offerAsync("测试" i, 100L, TimeUnit.MILLISECONDS); Thread.sleep(1000L); 电脑}}
测试数据2022-02-13 WARN 204760 ---[ Finalizer]i.l.c.resource.DefaultClientResources : io.lettuce.core.resource.DefaultClientResources was not shut down properly, shutdown() was not called before it's garbage-collected. Call shutdown() or shutdown(long,long,TimeUnit) 测试1测试2测试3测试4测试Process finished with exit code -1源码:https://git***.com/fuzhengwei/TimeOutCenter描述:使用 redisson 中的 DelayedQueue 作为消息队列,写入后,等待消费时间 POP 消费。四、总结例如,我们经常在实际场景中使用调度任务 xxl-job,还有一些大型工厂开发的分布式任务调度组件,它们可能是非常小和简单的功能,但经过抽象、集成和精炼,它们已经成为核心和通用的中间部件服务。当我们考虑使用任务调度时,无论设计和实现哪种方式,我们都需要考虑使用此功能时的迭代和维护。如果它只是一个非常小的场景,没有多少人使用它,那么在你自己的机器上扔它。过渡设计和使用有时会将研发资源转化为泥潭事实上,各种技术的知识点就像工具,刀、枪、棍、斧、斧、钩。如何结合各自的特点使用这些武器是程序员成长的过程。如果您想了解更多此类技术内容,可以添加 Lottery 分布式抽奖秒杀系统 学习更有价值、更耐用的实用手段。
电脑