【Lilishop商城】No2-7.确定软件架构搭建六(本篇包括延时任务,会用到rocketmq、redis)

news2024/11/17 7:28:30

  仅涉及后端,全部目录看顶部专栏,代码、文档、接口路径在:

【Lilishop商城】记录一下B2B2C商城系统学习笔记~_清晨敲代码的博客-CSDN博客


 全篇只介绍重点架构逻辑,具体编写看源代码就行,读起来也不复杂~

谨慎:源代码中有一些注释是错误的,有的注释意思完全相反,有的注释对不上号,我在阅读过程中就顺手更新了,并且在我不会的地方添加了新的注释,所以在读源代码过程中一定要谨慎啊!

目录

A1.延时任务模块

 B1.延时任务模块的逻辑

PS:怕忘记的内容并且也是重点

 B2.延时任务执行模块基本搭建

C1.业务系统的延时任务模块搭建(产生延时任务)

C2.消费系统的延时任务模块搭建(执行延时任务)

B2.测试

C1.测试产生延时任务

C2.测试执行延时任务

剩余内容:暂时没有了


A1.延时任务模块

延时任务的需求,例如:

1、生成订单30分钟未支付,则自动取消订单
2、快递签收后未点击订单的确认收货,则7天后默认确认收货

和定时任务的区别在于:

1、定时任务有明确的触发时间,延时任务没有;
2、定时任务有执行周期,而延时任务在某事件触发后一段时间内执行,没有执行周期;
3、定时任务一般执行的是批处理操作是多个任务,而延时任务一般是单个任务

说明:shop系统中使用的是多个技术框架结合实现的延时逻辑(redis+rocketmq),并没有使用专门的延时框架。我在网上搜索延时框架也没有找到合适的,几乎都是在现有技术框架基础上搭建的。

其他延时实现方式可看这篇:你可见过如此细致的延时任务详解_Java程序V的博客-CSDN博客

【rocketmq本身也支持延时消费哦,但延迟时长不支持随意时长的延迟【RocketMQ 二十】RocketMQ应用之延时消息】 

 B1.延时任务模块的逻辑

我们来说一下shop里面实现的延时模块,逻辑是有点复杂的,用到了redis+rocketmq,redis主要是存储延时任务,rocketmp主要是用来监听任务并执行的。

如果项目规模中不使用到消息中间件,也可以不用rocketmq。rocketmp在该项目中一部分功能是降低耦合、提高性能(我是这样理解的)。


我一开始看帮助文档的延时框架介绍:延时任务架构 · GitBook

里面的介绍比较简单,就对重点的类进行了说明,有点不太容易理解,我就又重新画了一个类关系图,格式不是很严谨(图3)...然后发现根据类名不容易理解,于是就用文字描述了一版(图2)...最终,为了更容易理解又画了一个简版逻辑(图1)

(图1)--> (图2)--> (图3),当然可以直接看图3源码,就是源码的类名字有点混淆了(对于我来说是混淆,也可能是我的问题...)

 

上三图解释的就已经很简单了,流程就不再文字重复了,下面记录一下我怕忘记的内容并且也是重点:

PS:怕忘记的内容并且也是重点

1.框架中是将同一类延时任务放到了同一个队列里面,等待执行,所以队列监听工厂类是获取的任务列表~所以本质上还是拿到一批任务进行执行。

2.框架中的队列监听工厂类是每秒轮询,不断获取在当秒执行的任务列表,然后开启异步线程执行;

3.因为延时任务类型不只一种,所以在执行框架中涉及到的核心类(除延时任务触发消费者类外),其余的核心类都需要实现类,每类延时任务各干各的事儿互不影响~

4.图中,以rocketmq和redis为连线,左半部分是属于业务系统的(在framework模块),右半部分是属于延时/消费系统的(在consumer模块,但也依赖framework,因为消费中需要业务类)。这样系统耦合度就降低了,例如,当consumer模块出故障后,不影响framwork模块添加任务,当consumer模块正常后就会从任务队列中继续获取任务进行执行~

5.任务类会从创建后保存到redis里面,然后被拿出来后又转化成mq的消息存储到mq里面,最后被mq监听到然后交给执行器执行。执行器里面是具体执行任务的逻辑,而流转的任务类里面的是需要到的参数~

6.延时执行接口是专门负责管理任务的,本来任务队列的管理也可以交给他,但是他又把延时队列生产工厂抽象出来了,应该是怕耦合。

7.RocketmqTimerTrigger具体延时任务实现,这个类的名字 RocketmqTimerTrigger 很迷惑,我的理解是促销类型的任务队列实现类,而这个名字范围太大了。如果我理解错了,那这个类里面又是只针对于 PromotionDelayQueue 队列的操作。所以最终我把他定位为促销类型的任务队列实现类,感觉这个理解是对的。

8.任务的修改逻辑是先删除任务然后又新增任务;任务的删除逻辑是先只删除任务的唯一标识,然后在消费端从队列中拿到并清除任务的消息时会判断任务唯一标识是否存在,存在则执行,不存在则跳过,所以并不是直接通过任务是否存在而判断的。


接下来就开始搭建 

 B2.延时任务执行模块基本搭建

消费系统的模块我们还在上篇新创建的consumer-test-xxl-job中添加,业务模块我们创建一个新的模块 framework-test 来搭建,这样更清晰。(以后用起来也方便,但要注意由于是我们自己创建的模块,有些工具类、参数类是没有的,到时候会自行添加的~)

C1.业务系统的延时任务模块搭建(产生延时任务)

创建新的模块framework-test,因为会用到 redis和rocketmq,我们按照之前文章里的的逻辑搭建就可以啦,在这就不重复了。

下面看开始搭建业务类的延时任务模块搭建:

1.延时任务消息类,专门存放任务信息;添加具体任务信息;

2.延时队列生产工厂抽象类,管理延时任务队列模块,向队列增加任务;管理延时任务队列模块实现类

3.延时执行接口,管理延时执行模块,增删改任务、执行任务;管理延时执行模块的实现类;

4.会使用到枚举类、常量接口、util工具类;

5.yml文件配置,添加rocketmq主题类型;(记得修改本模块pom类)

// 1.延时任务消息类,专门存放任务信息;添加具体任务信息;
//详见:cn.lili.trigger.model.TimeTriggerMsg

@Data
@AllArgsConstructor
@NoArgsConstructor
public class TimeTriggerMsg implements Serializable {


    private static final long serialVersionUID = 8897917127201859535L;

    /**
     * 执行器beanId,用于获取对应执行器对象
     */
    private String triggerExecutor;

    /**
     * 执行时间
     */
    private Long triggerTime;

    /**
     * 执行器参数,就是任务内容
     */
    private Object param;

    /**
     * 唯一KEY
     */
    private String uniqueKey;

    /**
     * 信息队列主题,用与mq发送和接收消息
     */
    private String topic;

}

/**
 * @author QingChen
 * @Description 具体任务信息,一般是所需要的参数属性,这里就随便测试了
 * @date 2022-12-02 15:43
 * @Version 1.0
 */
@Data
@NoArgsConstructor
public class Test1Message {

    /**
     * name
     */
    private String name;
    /**
     * id
     */
    private String id;

    /**
     * 开始时间
     */
    private Date startTime;

    /**
     * 结束时间
     */
    private Date endTime;
}
//2.延时队列生产工厂抽象类,管理延时任务队列模块,向队列增加任务;管理延时任务队列模块实现类
//抽象类,详见:cn.lili.trigger.delay.AbstractDelayQueueMachineFactory
//实现类,详见:cn.lili.trigger.delay.queue.Test1DelayQueue

@Slf4j
public abstract class AbstractDelayQueueMachineFactory {

    @Autowired
    private Cache cache;

    /**
     * 插入任务到redis里面,记住 jodid 是任务对象json转string
     *
     * @param jobId       任务id(队列内唯一)
     * @param triggerTime 执行时间 时间戳(毫秒)
     * @return 是否插入成功
     */
    public boolean addJob(String jobId, Long triggerTime) {

        /**
         * 设置在 redis 中的排序 score,设置为任务执行时间/1000(也就是秒级别的,别忘了在延时队列里面也/1000)
         * 因为延时任务按照疫每秒轮询判断,所以队列的 score 按照秒级别存放,方便获取
         */
        long delaySeconds = triggerTime / 1000;
        //增加延时任务到 ZSet 里面,参数依次为:队列名称、执行时间score、任务id
        boolean result = cache.zAdd(this.setDelayQueueName(), delaySeconds, jobId);
        log.info("增加延时任务, 缓存key {}, 执行时间 {},任务id {}", setDelayQueueName(), DateUtil.toString(triggerTime), jobId);
        return result;

    }


    /**
     * 要实现延时队列的名字,实现类需要实现
     * @return 延时队列的名字
     */
    public abstract String setDelayQueueName();

}



/**
 * @author QingChen
 * @Description 管理延时任务队列模块实现类
 * @date 2022-12-02 17:10
 * @Version 1.0
 */
@Component
public class Test1DelayQueue extends AbstractDelayQueueMachineFactory {
    @Override
    public String setDelayQueueName() {
        return DelayQueueEnums.TEST_1_DELAYQUEUE.name();
    }
}
//3.延时执行接口,管理延时执行模块,增删改任务、执行任务;管理延时执行模块的实现类;
//接口类,详见:cn.lili.trigger.interfaces.TimeTrigger
//实现类,详见:cn.lili.trigger.interfaces.impl.Test1RocketmqTimerTrigger

public interface TimeTrigger {

    /**
     * 添加延时任务
     *
     * @param timeTriggerMsg 延时任务信息
     */
    void addDelay(TimeTriggerMsg timeTriggerMsg);


    /**
     * 执行延时任务
     *
     * @param timeTriggerMsg 延时任务信息
     */
    void execute(TimeTriggerMsg timeTriggerMsg);

    /**
     * 修改延时任务
     *
     * @param executorName   执行器beanId
     * @param param          执行参数
     * @param triggerTime    执行时间 时间戳 秒为单位
     * @param oldTriggerTime 旧的任务执行时间
     * @param uniqueKey      添加任务时的唯一凭证
     * @param delayTime      延时时间(秒)
     * @param topic          rocketmq topic
     */
    void edit(String executorName, Object param, Long oldTriggerTime, Long triggerTime, String uniqueKey, int delayTime, String topic);

    /**
     * 删除延时任务
     *
     * @param executorName 执行器
     * @param triggerTime  执行时间
     * @param uniqueKey    添加任务时的唯一凭证
     * @param topic        rocketmq topic
     */
    void delete(String executorName, Long triggerTime, String uniqueKey, String topic);
}


@Component
@Slf4j
public class Test1RocketmqTimerTrigger implements TimeTrigger {
    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    @Autowired
    private Cache<Integer> cache;
    @Autowired
    private Test1DelayQueue delayQueue;
    
    @Override
    public void addDelay(TimeTriggerMsg timeTriggerMsg) {
        //拿到执行器唯一key
        String uniqueKey = timeTriggerMsg.getUniqueKey();
        if (StringUtils.isEmpty(uniqueKey)) {
            uniqueKey = StringUtils.getRandStr(10);
        }
        //生成执行任务key
        String generateKey = DelayQueueTools.generateKey(timeTriggerMsg.getTriggerExecutor(), timeTriggerMsg.getTriggerTime(), uniqueKey);
        //在redis中添加一个唯一标识的来标识当前延时任务的唯一性,该标识根据任务key生成的
        this.cache.put(generateKey, 1);
        //设置延时任务,注意哦,这里将延时消息本身(json)作为了 jobid
        if (Boolean.TRUE.equals(delayQueue.addJob(JSONUtil.toJsonStr(timeTriggerMsg), timeTriggerMsg.getTriggerTime()))) {
            log.info("延时任务标识: {}", generateKey);
            log.info("定时执行在【" + DateUtil.toString(timeTriggerMsg.getTriggerTime(), "yyyy-MM-dd HH:mm:ss") + "】,消费【" + timeTriggerMsg.getParam().toString() + "】");
        } else {
            log.error("延时任务添加失败:{}", timeTriggerMsg);
        }
    }

    @Override
    public void execute(TimeTriggerMsg timeTriggerMsg) {
        this.addExecute(timeTriggerMsg.getTriggerExecutor(),
                timeTriggerMsg.getParam(),
                timeTriggerMsg.getTriggerTime(),
                timeTriggerMsg.getUniqueKey(),
                timeTriggerMsg.getTopic()
        );
    }

    /**
     * 将任务添加到mq,mq异步队列执行。
     * <p>
     * 本系统中redis相当于延时任务吊起机制,而mq才是实际的业务消费,执行任务的存在
     *
     * @param executorName 执行器beanId
     * @param param        执行参数
     * @param triggerTime  执行时间 时间戳 秒为单位
     * @param uniqueKey    如果是一个 需要有 修改/取消 延时任务功能的延时任务,<br/>
     *                     请填写此参数,作为后续删除,修改做为唯一凭证 <br/>
     *                     建议参数为:COUPON_{ACTIVITY_ID} 例如 coupon_123<br/>
     *                     业务内全局唯一
     * @param topic        rocketmq topic
     */
    private void addExecute(String executorName, Object param, Long triggerTime, String uniqueKey, String topic) {
        TimeTriggerMsg timeTriggerMsg = new TimeTriggerMsg(executorName, triggerTime, param, uniqueKey, topic);
        Message<TimeTriggerMsg> message = MessageBuilder.withPayload(timeTriggerMsg).build();
        log.info("延时任务发送信息:{}", message);
        this.rocketMQTemplate.asyncSend(topic, message, RocketmqSendCallbackBuilder.commonCallback());
    }

    @Override
    public void edit(String executorName, Object param, Long oldTriggerTime, Long triggerTime, String uniqueKey, int delayTime, String topic) {
        this.delete(executorName, oldTriggerTime, uniqueKey, topic);
        this.addDelay(new TimeTriggerMsg(executorName, triggerTime, param, uniqueKey, topic));
    }

    @Override
    public void delete(String executorName, Long triggerTime, String uniqueKey, String topic) {
        String generateKey = DelayQueueTools.generateKey(executorName, triggerTime, uniqueKey);
        log.info("删除延时任务{}", generateKey);
        this.cache.remove(generateKey);
    }
}
//4.会使用到枚举类、常量接口、util工具类
//都有:
延时任务工具类,专门生成key:cn.lili.trigger.util.DelayQueueTools
延时任务执行器常量:cn.lili.trigger.model.TimeExecuteConstant
队列枚举:cn.lili.trigger.enums.DelayQueueEnums
延时任务类型:cn.lili.trigger.enums.DelayTypeEnums

cn.lili.util.DateUtil
cn.lili.util.StringUtils
//5.yml文件配置,添加rocketmq主题类型;
//详见:/lilishop-master/framework-test/src/main/resources/application.yml

lili:
  data:
    rocketmq:
      test1Topic: lili_test1Topic
      test1Group: lili_test1Group

C2.消费系统的延时任务模块搭建(执行延时任务)

在上次创建的consumer-test-xxl-job模块中,搭建延时任务框架,由于要搭配新创建的framework模块使用,所以需要将 pom 文件中的framework依赖注释掉,并且引入framework-test模块;

下面看开始搭建消费类的延时任务模块搭建

1.延时队列工厂监听抽象类,延时队列工厂监听实现类;

2.延时任务事件消息触发消费者;

3.延时任务执行器接口,延时任务执行器实现类;

4.会使用到枚举类、常量接口、util工具类;(记得修改本模块pom类)

//1.延时队列工厂监听抽象类,延时队列工厂监听实现类;
//抽象类,详见:cn.lili.trigger.listen.queue.AbstractDelayQueueListen
//实现类,详见:cn.lili.trigger.listen.queue.impl.Test1DelayQueueListen

/**
 * @author QingChen
 * @Description 延时队列工厂监听抽象类
 *      springBoot项目启动时候,有时候需要再启动之后直接执行某一段代码。这个时候就用到了 ApplicationRunner 这个类。
 * @date 2022-12-05 11:34
 * @Version 1.0
 */
@Slf4j
public abstract class AbstractDelayQueueListen implements ApplicationRunner {

    @Autowired
    private Cache cache;


    /**
     * 延时队列机器开始运作
     */
    private void startDelayQueueMachine() {
        log.info("延时队列机器{}开始运作", setDelayQueueName());

        //监听redis队列
        while (true) {
            try {
                //获取当前时间的时间戳,并拿到秒,与
                long now = System.currentTimeMillis() / 1000;
                //获取当前监听任务类型中,当前时间前需要执行的任务列表,(score是以时间秒设置的)
                //【不会移除任务哦】
                Set<DefaultTypedTuple> tuples = cache.zRangeByScore(setDelayQueueName(), 0, now);

                //如果任务不为空
                if (!CollectionUtils.isEmpty(tuples)) {
                    log.info("执行任务:{}", JSONUtil.toJsonStr(tuples));

                    for (DefaultTypedTuple tuple : tuples) {
                        //循环拿到jobid
                        String jobId = (String) tuple.getValue();
                        //移除缓存,如果移除成功则表示当前线程处理了延时任务,则执行延时任务
                        //【在这里移除任务哦】
                        Long num = cache.zRemove(setDelayQueueName(), jobId);
                        //如果移除成功, 则执行
                        if (num > 0) {
                            //创建新线程并run,run里面执行 invoke 方法
                            ThreadPoolUtil.execute(() -> {this.invoke(jobId);});

                        }
                    }
                }

            } catch (Exception e) {
                log.error("处理延时任务发生异常,异常原因为{}", e.getMessage(), e);
            } finally {
                //间隔一秒钟搞一次
                try {
                    TimeUnit.SECONDS.sleep(5L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }

    }

    /**
     * 最终执行的任务方法
     *
     * @param jobId 任务id
     */
    public abstract void invoke(String jobId);


    /**
     * 要实现延时队列的执行器名字
     * @return
     */
    public abstract String setDelayQueueName();


    /**
     * 初始化监听队列
     */
    public void init() {
        ThreadPoolUtil.getPool().execute(this::startDelayQueueMachine);
    }


}

@Component
public class Test1DelayQueueListen extends AbstractDelayQueueListen {

    @Autowired
    private Test1RocketmqTimerTrigger timeTrigger;

    /**
     * @Description: 调用具体的 TimeTrigger 执行任务的方法
     * @param: [jobId]
     * @return: void
     **/
    @Override
    public void invoke(String jobId) {
        //Json转对象
        timeTrigger.execute(JSONUtil.toBean(jobId, TimeTriggerMsg.class));
    }

    /**
     * 要实现延时队列的名字
     * @return 促销延时队列名称
     */
    @Override
    public String setDelayQueueName() {
        return DelayQueueEnums.TEST_1_DELAYQUEUE.name();
    }

    /**
     * @Description: ApplicationRunner 的重写方法
     * @param: [args]
     * @return: void
     **/
    @Override
    public void run(ApplicationArguments args) throws Exception {
        this.init();
    }
}
//2.延时任务事件消息触发消费者;
//详见:cn.lili.trigger.listen.trigger.TimeTriggerConsumer


/**
 * @author QingChen
 * @Description 延时任务事件消息触发消费者
 * 从rocketmq拦截的topic类型来讲,这个是只针对指定主题promotion-topic的消息监听器。
 * 但是由于延时任务消息的执行器只跟 TimeTriggerMsg 的属性有关,所以这个消费类可以作为所有延时任务消息的监听器。
 * (由于这个监听的 topic 指定了促销类型,所以我不确定系统开发者的逻辑,而且这个类的名字看起来也是总得消费类,所以比较迷惑)
 * (如果是我的话,会将这个作为所有延时任务的消费中心)
 *
 * @date 2022-12-05 13:32
 * @Version 1.0
 */
@Component
@Slf4j
@RocketMQMessageListener(topic = "${lili.data.rocketmq.test1Topic}", consumerGroup = "${lili.data.rocketmq.test1Group}")
public class TimeTriggerConsumer implements RocketMQListener<TimeTriggerMsg> {
    @Autowired
    private Cache<Integer> cache;

    @Override
    public void onMessage(TimeTriggerMsg timeTriggerMsg) {
        try {
            //生成执行任务key
            String key = DelayQueueTools.generateKey(timeTriggerMsg.getTriggerExecutor(), timeTriggerMsg.getTriggerTime(), timeTriggerMsg.getUniqueKey());

            if (cache.get(key) == null) {
                log.info("执行器执行被取消:{} | 任务标识:{}", timeTriggerMsg.getTriggerExecutor(), timeTriggerMsg.getUniqueKey());
                return;
            }

            log.info("执行器执行:" + timeTriggerMsg.getTriggerExecutor());
            log.info("执行器参数:" + JSONUtil.toJsonStr(timeTriggerMsg.getParam()));

            cache.remove(key);

            //拿到任务所指定执行器的bean进行执行
            TimeTriggerExecutor executor = (TimeTriggerExecutor) SpringContextUtil.getBean(timeTriggerMsg.getTriggerExecutor());
            executor.execute(timeTriggerMsg.getParam());
        } catch (Exception e) {
            log.error("mq延时任务异常", e);
        }

    }

}
//3.延时任务执行器接口,延时任务执行器实现类;
//接口类,详见:cn.lili.trigger.executor.TimeTriggerExecutor
//实现类,详见:Test1TimeTriggerExecutor

/**
 * @author QingChen
 * @Description 延时任务执行器接口
 * @date 2022-12-05 13:37
 * @Version 1.0
 */

public interface TimeTriggerExecutor {


    /**
     * 执行任务
     *
     * @param object 任务参数
     */
    void execute(Object object);

}


/**
 * @author QingChen
 * @Description 延时任务执行器实现类
 * @date 2022-12-05 13:38
 * @Version 1.0
 */
@Slf4j
@Component(TimeExecuteConstant.TEST_1_EXECUTOR)
public class Test1TimeTriggerExecutor implements TimeTriggerExecutor {
    @Override
    public void execute(Object object) {

        Test1Message message = JSONUtil.toBean(JSONUtil.parseObj(object), Test1Message.class);

        log.info("执行器:{"+TimeExecuteConstant.TEST_1_EXECUTOR+"}");
        log.info("执行内容:{"+message+"}");
    }
}
//4.会使用到枚举类、常量接口、util工具类;

cn.lili.util.SpringContextUtil
cn.lili.util.ThreadPoolUtil

注意,这个模块只是我用来测试的,里面没有任何和shop系统业务相关的,最终的目录是这样的:

B2.测试

C1.测试产生延时任务

我们直接就在framework-test模块添加controller接口,来创建任务,一个是创建任务接口,通过入参的延时任务类型,来判断具体调用哪个任务调度器添加任务。

生产代码的时候,记得要清楚的添加各个类型哦~

//我这里就添加了两个接口,一个添加任务,一个删除任务
详见:cn.lili.controller.TestTriggerController

@RestController
@RequestMapping("/trigger")
public class TestTriggerController {

    /**
     * 延时任务1
     */
    @Autowired
    private Test1RocketmqTimerTrigger test1RocketmqTimerTrigger;

    /**
     * 延时任务2
     */
    @Autowired
    private Test2RocketmqTimerTrigger test2RocketmqTimerTrigger;

    /**
     * rocketMq配置
     */
    @Autowired
    private RocketmqCustomProperties rocketmqCustomProperties;

    /**
     * @Description: type 是指延时任务类型,正式开发时是不会这样用的哦,我仅仅是测试
     * @param: [type]
     * @return: java.lang.String
     **/
    @PostMapping(value = "/trigger")
    public String addTrigger(String type) {

        //获取时间偏移(向前或向后)
        long startTime = DateUtil.offsetMinute(new Date(), 2).getTime();

        switch (type){
            case "1":
                Test1Message test1Message = new Test1Message();
                test1Message.setId(StringUtils.getRandStr(10));
                test1Message.setName("任务name");
                test1Message.setStartTime(new Date());
                test1Message.setEndTime(new Date());
                TimeTriggerMsg timeTriggerMsg = new TimeTriggerMsg(TimeExecuteConstant.TEST_1_EXECUTOR,
                        startTime,
                        test1Message,
                        DelayQueueTools.wrapperUniqueKey(DelayTypeEnums.TEST_1_DELAYTYPE.name(), (test1Message.getId())),
                        rocketmqCustomProperties.getTest1Topic());

                test1RocketmqTimerTrigger.addDelay(timeTriggerMsg);
                break;
            case "2":
                Test2Message test2Message = new Test2Message();
                test2Message.setNum(StringUtils.getRandStr(10));
                test2Message.setSize("大小size");
                test2Message.setStartTime(new Date());
                timeTriggerMsg = new TimeTriggerMsg(TimeExecuteConstant.TEST_2_EXECUTOR,
                        startTime,
                        test2Message,
                        DelayQueueTools.wrapperUniqueKey(DelayTypeEnums.TEST_2_DELAYTYPE.name(), (test2Message.getNum())),
                        rocketmqCustomProperties.getTest1Topic());

                test2RocketmqTimerTrigger.addDelay(timeTriggerMsg);
                break;
            default:
                break;
        }

        return "SUCCESSFUL";
    }

    /**
     * @Description: TODO
     * @param: executorName 执行器名字, triggerTime 执行时间, idORnum 唯一标识(我将test1 2写一起的), topic 任务主题
     * @return: java.lang.String
     **/
    @DeleteMapping(value = "/trigger")
    public String deleteTrigger(String executorName, Long triggerTime, String idORnum, String topic) {
        switch (executorName){
            case TimeExecuteConstant.TEST_1_EXECUTOR:
                test1RocketmqTimerTrigger.delete(executorName,
                        triggerTime,
                        DelayQueueTools.wrapperUniqueKey(DelayTypeEnums.TEST_1_DELAYTYPE.name(), idORnum),
                        rocketmqCustomProperties.getTest1Topic());
                break;
            case TimeExecuteConstant.TEST_2_EXECUTOR:
                test2RocketmqTimerTrigger.delete(executorName,
                        triggerTime,
                        DelayQueueTools.wrapperUniqueKey(DelayTypeEnums.TEST_2_DELAYTYPE.name(), idORnum),
                        rocketmqCustomProperties.getTest1Topic());
                break;
            default:
                break;
        }

        return "SUCCESSFUL";
    }
}

 

C2.测试执行延时任务

这个直接运行consumer,搭配上面的C1就是测试了呀,嘿嘿,可以看到就是在执行时间的范围内执行的

 

剩余内容:暂时没有了

后面就开始详细设计了,再详细设计中有可能会在涉及到系统架构改动,到时会再度说明。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/63567.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用python进行贝叶斯统计分析

本文讲解了使用PyMC3进行基本的贝叶斯统计分析过程. 最近我们被客户要求撰写关于贝叶斯统计分析的研究报告&#xff0c;包括一些图形和统计输出。 # 导入 import pymc3 as pm # python的概率编程包 import numpy.random as npr # numpy是用来做科学计算的 import matplotlib…

cubeIDE开发,结合图片取模工具,stm32程序在LCD显示图片

一、图片取模工具&#xff08;imag2lcd&#xff09; 我们前面将汉字显示时说过&#xff0c;嵌入式LCD屏显示就是通过LCD屏幕数据接口给每个屏幕像素给出一个颜色值实现实时渲染显示出来。只不过文字显示时&#xff0c;给出的是一个二进制点阵&#xff0c;然后根据二进制中的“1…

Java文件输入输出(简单易懂版)

文章目录写在前面文件输入文件输出写在前面 在Java中不论文件输入还是输出都会用到File类&#xff0c;参考这篇文章Java File类&#xff08;文件操作类&#xff09;详解这里会涉及到绝对路径、相对路径、路径中的斜杠“/”和反斜杠“\”&#xff0c;有些小伙伴可能不熟悉&…

数据结构—B树、B+树

文章目录B树B树的特点为什么要有B树&#xff1a;红黑树和B树比较B树B树的特点B树构建过程查询时数据提供数据磁盘向cpu推送数据B树的优点总结为什么要有B树&#xff1a;B树用途&#xff1a;为什么要有B树&#xff1a;B树用途&#xff1a;———————————————————…

WorkTool企微机器人APP分享自定义小程序

移动端应用怎么分享自定义小程序到企业微信 前言 什么是自定义小程序&#xff0c;就是我们可以通过业务逻辑或代码来动态修改每次发出的小程序所附带的路径(path)和参数(params)&#xff0c;以此来控制每次发出的小程序都是有不同含义的&#xff0c;但企业微信并不让我们这样…

[附源码]Python计算机毕业设计Django体育器材及场地管理系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

【Spring】事务管理

目录项目目录spring-dao.xml测试项目目录 UserMapper.xml写sql语句UserMapperImpl继承sqlSessionDaoSupport然后在里面做一个addUser和一个delete我们delete语句故意写错 但是我们想把add和delete当成一个事务delete错了&#xff0c;add也不应该成功所以我们需要进行事务管理配…

FLStudio2023水果中文版本使用操作心得与技巧

FL Studio2023是一款强大的音乐创作编辑软件&#xff0c;因软件LOGO为水果标志&#xff0c;故我们国人都习惯称之为水果。它让你的计算机就像是全功能的录音室&#xff0c;漂亮的大混音盘&#xff0c;先进的创作工具&#xff0c;让你的音乐突破想象力的限制。因此它又有"水…

手撕ThreadLocal源码

首先&#xff0c;创建MyThreadLocal类&#xff0c;区分开java.lang.ThreadLocal package com.huhu.threadlocal;import java.util.HashMap; import java.util.Map;public class MyThreadLocal<T> {/*** 所有需要和当前线程绑定的数据要放到这个容器当中*/private Map<…

WIFI的传输距离介绍

WIFI的传输距离介绍 WiFi模块在智能家居&#xff0c;智能驾舱等各行业各领域应用极广&#xff0c; 但有多少人了解他的传输距离是多少呢&#xff1f;是受什么影响呢&#xff1f; 一&#xff1a;WiFi模块的传输距离 WiFi模块的传输距离普遍在100-200米之间&#xff0c;其中也有支…

乐观锁思想在JAVA中的实现——CAS

前言 生活中我们看待一个事物总有不同的态度&#xff0c;比如半瓶水&#xff0c;悲观的人会觉得只有半瓶水了&#xff0c;而乐观的人则会认为还有半瓶水呢。很多技术思想往往源于生活&#xff0c;因此在多个线程并发访问数据的时候&#xff0c;有了悲观锁和乐观锁。 悲观锁认…

React+ts学习文档

1.项目中遇到的困难解决以及方案 1.顶部的查询按钮 点击查询如果在该组件去进行axios请求&#xff0c;这样再该组件下获得返回的列表还需要传回父组件&#xff0c;父组件再把列表发给下面的table组件&#xff0c;不太方便。 解决方案&#xff1a;将用户的选择项传给顶级组件i…

Kubernetes v1.25 搭建一个单节点集群用于Debug K8S源码

参考说明 参考自&#xff1a;v1.25.0-CentOS-binary-install-IPv6-IPv4-Three-Masters-Two-Slaves.md&#xff0c;按照自己的理解修改了下。 搭建好的单节点v1.25.4版本集群 1. 集群环境准备 1.1. 主机规划 IP主机名主机角色操作系统安装组件192.168.11.71k8s-master1maste…

在Python中使用LSTM和PyTorch进行时间序列预测

顾名思义&#xff0c;时间序列数据是一种随时间变化的数据类型。例如&#xff0c;24小时内的温度&#xff0c;一个月内各种产品的价格&#xff0c;一年中特定公司的股票价格。 去年&#xff0c;我们为一位客户进行了短暂的咨询工作&#xff0c;他正在构建一个主要基于时间序列…

【✨十五天搞定电工基础】正弦交流电路的分析

本章要求 1. 理解正弦量的特征及其各种表示方法&#xff1b; 2. 理解电路基本定律的相量形式及阻抗&#xff1b;熟练掌握计算正弦交流电路的相量分析法&#xff0c; 会画相量图&#xff1b; 3. 掌握有功功率和功率因数的计算,了解瞬时功率、无功功率和视在功率的概…

项目管理(如何进行团队管理)

团队构建与管理6步走 各项工作概述: 获取团队成员需要考虑的因素: 可用性。确认资源能否在项目所需时段内为项目所用。 成本。确认增加资源的成本是否在规定的预算内。 能力。确认团队成员是否提供了项目所需的能力。 有些选择标准对团队资源来说是独特的,包括: 经验。…

HTML人物介绍、个人设计web前端大作业、贝聿铭人物介绍(带报告3000字)

&#x1f389;精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

Allegro如何使用快捷键快速切换走线线宽操作指导

Allegro如何使用快捷键快速切换走线线宽操作指导 Allegro可以用快捷键快速切换走线线宽,比如在command下方输入数字5,可以切换到5mil的线宽 具体操作如下 打开系统属性,选择环境变量 找到home的环境变量的路径是哪里 找到路径下的pcbenv文件夹 找到env文件 用记事本打…

Android NDK初识

Android NDK是Android软件开发包(SDK)的相关工具集&#xff0c;用来扩展Android SDK的功能&#xff0c;从而使开发人员能够使用机器代码生成的编程语言(如C、C和汇编语言)实现一些对代码性能要求较高的模块&#xff0c;并将这些模块嵌入到Android应用程序中使用。 什么是Andro…

工业物联网案例:智能工厂设备无人值守系统方案

智能工厂又称“黑灯工厂”&#xff0c;具备智能化、自动化、无人化等特点&#xff0c;可以实现全天候高效的生产工作&#xff0c;是制造业的先进典范。通过工厂设备无人值守系统&#xff0c;可以集中采集、传输、处理、显示各类工业设备数据&#xff0c;可以对空压机、变电站、…