Rocket-api优化方案(四)---定时任务

本文介绍了如何在Rocket-API中集成Quartz框架以支持项目管理和定时任务。通过添加安全设置、用户权限管理、定时任务控制,实现了开发项目与开发人员、数据源的关联。详细步骤包括Quartz的配置、Job实例工厂创建、配置类编写、Job任务类实现以及对外服务层的编写,使得系统能够根据预设的cron表达式动态执行任务。此外,还更新了接口编辑页面以支持定时任务的开启和关闭功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

接上篇Rocket-api优化方案(三)---项目管理

一、设计目标

  1. 解决目前系统尚未做登陆的安全设置,同时不能新增其他的系统用户。

  2. 通过对用户权限的管理解决数据源安全管控的问题。

  3. 支持离线计算分析任务需要的定时任务。

  4. 需要将开发的项目与开发人员、数据源形成关联。

二、实现步骤

  1. 对于开发者来说,会有一些业务需求需要通过定时的去执行某些业务逻辑代码去实现,并且要对这些定时任务加以控制,需要的时候才开启,避免浪费资源。在原有的api开发平台是支持java代码编辑,我们可以利用平台原有的优势加以扩张,使其支持对定时任务的处理和控制。

  2. 对于定时任务的处理我选择在原有的平台上集成Quartz框架来实现对任务的动态管理,Quartz是一个开源的任务调度框架,基于定时、定期的策略来执行任务是它的核心功能。

  3. Quartz有3个核心要素:调度器(Scheduler)、任务(Job)、触发器(Trigger)。

  4. 添加maven依赖

    <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-quartz</artifactId>
    </dependency>
  5.  添加quartz.properties配置文件,放在resource目录下。

    spring.quartz.job-store-type=jdbc
    spring.quartz.auto-startup=true
    spring.quartz.jdbc.initialize-schema=embedded
    spring.quartz.properties.org.quartz.scheduler.instanceName=MyScheduler
    spring.quartz.properties.org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
    spring.quartz.properties.org.quartz.jobStore.isClustered=true
    spring.quartz.properties.org.quartz.jobStore.useProperties=false

  6. 在application.yml中添加以下属性,确定线程池的数量,可以同时执行多少个定时任务。

    org:
      quartz:
        threadPool:
          class: org.quartz.simpl.SimpleThreadPool
          threadCount: 10
          threadPriority: 5
          threadsInheritContextClassLoaderOfInitializingThread: true

  7. 创建job实例工厂(解决Spring注入问题,如果使用默认会导致Spring的@Autowired无法注入问题。)

    @Component
    public class QuartzAdaptableJobFactory extends AdaptableJobFactory {
    
        //AutowireCapableBeanFactory 可以将一个对象添加到SpringIOC容器中,并且完成该对象注入
        @Autowired
        private AutowireCapableBeanFactory autowireCapableBeanFactory;
    
        /**
         * 该方法需要将实例化的任务对象手动的添加到springIOC容器中并且完成对象的注入
         */
        @Override
        protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
            Object obj = super.createJobInstance(bundle);
            // 通过以下方式,解决job任务无法使用spring中的Bean问题
            this.autowireCapableBeanFactory.autowireBean(obj);
            return obj;
        }
    }
  8. QuartzConfig.java配置类(注册调度工厂)

    @Configuration
    public class QuartzConfig {
    
        @Autowired
        private QuartzAdaptableJobFactory jobFactory;
    
        @Bean(name = "SchedulerFactory")
        public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
            //获取配置属性
            PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
            propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
            //在quartz.properties中的属性被读取并注入后再初始化对象
            propertiesFactoryBean.afterPropertiesSet();
            //创建SchedulerFactoryBean
            SchedulerFactoryBean factory = new SchedulerFactoryBean();
            factory.setQuartzProperties(propertiesFactoryBean.getObject());
            factory.setJobFactory(jobFactory);
            return factory;
        }
    
        /*
         * 通过SchedulerFactoryBean获取Scheduler的实例
         */
        @Bean(name = "scheduler")
        public Scheduler scheduler() throws IOException, SchedulerException {
            Scheduler scheduler = schedulerFactoryBean().getScheduler();
            return scheduler;
        }
    }
  9. 编写Job具体任务类,实现Job接口,重写execute()方法,此方法里编写对定时任务进行具体的逻辑处理代码。

    public class QuartzJob implements Job {
    
        @Autowired
        @Lazy
        private IScriptParse scriptParse;
        @Override
        public void execute(JobExecutionContext context) throws JobExecutionException {
            // 获取传入的参数
            String apiStr =(String) context.getJobDetail().getJobDataMap().get("apiInfo");
            JSONObject apiJson = JSON.parseObject(apiStr);
            // JSON字符串转成apiInfo对象
            ApiInfo apiInfo= JSONObject.toJavaObject(apiJson, ApiInfo.class);
            ApiParams apiParams = ApiParams.builder()
                    .header(null)
                    .pathVar(null)
                    .param(null)
                    .body(null)
                    .session(null)
                    .build();
            Object value = null;
            try {
                // 定时任务执行结果
                value = scriptParse.runScript(apiInfo.getScript(),apiInfo,apiParams);
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
            System.out.println(value);
    
        }
    }
  10. 编写Quartz对外服务层代码----开启定时任务(传入需要执行定时任务的ApiInfo对象)

        public void addJob(ApiInfo apiInfo) throws Exception {
            // 启动调度器
            scheduler.start();
    
            // 构建job信息
            JobDetail jobDetail = JobBuilder
                    .newJob(QuartzJob.class)
                    .withIdentity(apiInfo.getId(), "QuartzJob")
                    .usingJobData("apiInfo", JSONObject.toJSONString(apiInfo))
                    .build();
    
            // 构建trigger
            CronTrigger trigger = TriggerBuilder.newTrigger()
                    .withIdentity(apiInfo.getId(), "QuartzJob")
                    // 表达式调度构建器(即任务执行的时间)
                    .withSchedule(CronScheduleBuilder.cronSchedule(apiInfo.getCron()))
                    .build();
    
            // 调度容器设置JobDetail和Trigger
            scheduler.scheduleJob(jobDetail, trigger);
        }

    关闭定时任务

        /**
         * 关闭job
         *
         * @throws SchedulerException
         * @author qianguojie
         * @date 2022/3/30.
         */
        public void deleteJob(ApiInfo apiInfo) throws SchedulerException {
            scheduler.deleteJob(JobKey.jobKey(apiInfo.getId(), "QuartzJob"));
        }
  11. 由于我们编写的脚本都是存入Api_info数据表中,当执行定时任务时也就相当于到了特定时间来执行存入Api_info数据表中的脚本,所以需要对Api_info数据表加以改进,添加两个字段(cron表达式和定时任务状态)。42d9359ea29f6f095b112d3820c0c0ff.png别忘了在实体类中加入这两个属性。

  12. 对于接口的编辑页面也需要加以改进,增加一个编写的cron表达式的输入框和一个开启\关闭按钮。5d7d4a8b1f05e671dd11632fb09f960c.png

  13. 点击保存,将参数传入后台,根据传入的cron表达式和状态进行判断是否开启此定时任务。

     //定时任务判断
                if (apiInfo.getCron() != null || apiInfo.getCron() != ""){
                    if (apiInfo.getCron()==dbInfo.getCron()){
                        if (apiInfo.getCronStatus()==dbInfo.getCronStatus()){
                            // cron表达式和状态都未修改,不做处理
                        }else {
                            if (apiInfo.getCronStatus() == 1 && dbInfo.getCronStatus() ==0){
                                // 开启定时任务
                                quartzService.addJob(apiInfo);
                                logger.info("已开启定时任务");
                            }else if(apiInfo.getCronStatus() == 0 && dbInfo.getCronStatus() == 1){
                                // 关闭定时任务
                                quartzService.deleteJob(apiInfo);
                                logger.info("已关闭定时任务");
                            }
                        }
                    }else {
                        if (apiInfo.getCronStatus() == 1 && dbInfo.getCronStatus() == 1){
                            // 取消之前的定时任务
                            quartzService.deleteJob(dbInfo);
                            logger.info("已取消之前的定时任务");
                            // 开启新的cron定时任务
                            quartzService.addJob(apiInfo);
                            logger.info("开启新的定时任务");
                        }else if (apiInfo.getCronStatus() == 1 && dbInfo.getCronStatus() == 0){
                            // 开启定时任务
                            quartzService.addJob(apiInfo);
                            logger.info("已开启定时任务");
                        }else if (apiInfo.getCronStatus() == 0 && dbInfo.getCronStatus() == 1){
                            // 关闭定时任务
                            quartzService.deleteJob(apiInfo);
                            logger.info("已关闭定时任务");
                        }
                    }
                }

  14. 点击开启定时任务,后台会调用Quartz服务层启动定时任务的方法,并把ApiInfo对象传过去,等cron表达式的时间一到,就会调用Job任务类QuartzJob的execute()方法,执行脚本。

  15. 并在后台管理界面编写定时任务列表页面,方便统一查看所有项目中的定时任务。f3b53df487f99470e1f4de9d34edd076.png

### 如何在Linux系统上部署Rocket-API #### 准备工作 为了成功部署 Rocket-API,在开始之前需确认已安装并配置好必要的环境。这包括但不限于 Docker 和 Git 的安装,因为这些工具对于获取官方示例仓库和容器化应用程序至关重要。 确保Docker已经正确安装于Linux(CentOS)环境中[^2]。可以通过命令`docker --version`验证是否能够返回有效的版本号;同样地,Git也应处于可用状态以便克隆所需的GitHub库文件。 #### 获取源码 利用Git从Apache提供的官方地址下载最新的 `rocketmq-spring-boot-starter` 示例工程: ```bash git clone https://github.com/apache/rocketmq-spring.git cd rocketmq-spring/example/ ``` 此操作会拉取到最接近当前日期的稳定版代码,从而减少因API变动带来的兼容性问题[^1]。 #### 构建镜像 进入项目目录后,依据README文档中的指导完成Maven依赖项编译打包过程,并创建自定义Docker镜像: ```bash mvn clean package -Dmaven.test.skip=true dockerfile:build ``` 上述指令不仅完成了Java项目的构建流程还同步生成了一个基于OpenJDK基础映像的新容器镜像。 #### 启动服务 当一切准备就绪之后,就可以借助Docker Compose或者直接调用`docker run`启动新建立的服务端口了。假设想要以外部可访问的方式暴露HTTP API,则可以这样设置参数: ```bash docker run -d -p 8080:8080 your_custom_image_name ``` 这里的 `-p` 参数指定了主机与容器之间的端口映射关系,即外部请求可通过宿主机上的8080端口转发至内部相同编号的服务监听位置。 #### 验证部署成果 最后一步是测试API接口是否正常运作。打开浏览器或者其他HTTP客户端向刚才指定的URL发送GET请求,如http://localhost:8080/hello/world ,如果能收到预期响应则说明整个部署过程顺利完成。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值