<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12
      Fork me on GitHub

      SpringBoot實現輕量級動態定時任務管控及組件化

      關于動態定時任務

      關于在SpringBoot中使用定時任務,大部分都是直接使用SpringBoot的@Scheduled注解,如下:

      @Component
      public class TestTask
      {
          @Scheduled(cron="0/5 * *  * * ? ")   //每5秒執行一次
          public void execute(){
              SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 
              log.info("任務執行" + df.format(new Date()));
          }
      }

      或者或者使用第三方的工具,例如XXL-Job等。就XXL-Job而言,如果說是大型項目,硬件資源和項目環境都具備,XXL-Job確實是最佳的選擇,可是對于項目體量不大,又不想過多的引入插件;使用XXL-Job多少有點“殺雞用牛刀”的意思;

      之所以這樣說,是因為在SpringBoot中集成使用XXL-Job的步驟如下:

      1. 引入依賴,配置執行器Bean
      2. 在自己項目中編寫定時任務代碼
      3. 部署XXL-Job
      4. 登錄XXL-Job調度中心的 Web 控制臺,創建一個新的任務,選擇剛才配置的執行器和任務處理器,設置好觸發條件(如 Cron 表達式)和其他選項后保存
      5. 生產環境下,還要把配置好任務的XXL-Job和項目一起打包
      @Component
      public class SampleXxlJob {
      
          @XxlJob("sampleJobHandler")
          public void sampleJobHandler() throws Exception {
              // 業務邏輯
              System.out.println("Hello XXL-JOB!");
          }
      }

      這一套步驟在中小型項目中,明顯成本大于效果,而使用XXL-Job無非就是想動態的去管理定時任務,可以在運行狀態下隨意的執行、中斷、調整執行周期、查看運行結果,而不是像基于@Scheduled注解實現后,無法改變。

      所以,這里就基于SpringBoot實現動態調度定時任務,之前針對這個問題,我寫過一篇CSDN文章(連接放在下方),最近博主將相關的代碼進行了匯總,并且在易用性和擴展性上進行了加強,基于COLA架構理論,封裝到了組件層

      這次加強主要包括:

      1. 剝離了任務的持久化,使其依賴更簡潔,真正以starter的形式開箱即用
      2. 擴展了方法級的定時任務注冊,能夠像xxl-job一樣,一個注解搞定動態定時任務的注冊及管理

      動態定時任務實現思路

      關于動態定時任務的核心思路是反射+定時任務模板,這兩部分的核心框架不變,相關內容可以回顧我之前的博客:

      輕量級動態定時任務調度

      這里主要是針對強化的第二點進行思路解釋,第二點的強化是加入了類掃描機制,通過掃描,實現了自動注冊,規避了之前每新增一個定時任務都必須得預制SQL的步驟:

      類級別定時任務實現思路:在原模板模式的基礎下,基于AbstractBaseCronTask類自定義的定時任務子類作為類級別定時任務,即一個類為一個定時任務,初始時由包掃描所有的子類,并使用反射將其實例化,逐一加入到進程管理中,并激活定時調度。

      基于@MethodJob的方法級別任務實現思路:以 AbstractBaseCronTask類為基礎,定義一個固定的子類BaseMethodLevelTask并在其內部限定任務的執行方式掃描所有標注了@MethodJob的方法及其所屬的Bean,連同Bean及方法的反射類作為構造函數,生成BaseMethodLevelTask對象因為BaseMethodLevelTask也是AbstractBaseCronTask的子類,則可以以類級別定時任務的方式,將其生成定時任務,并進行管理。

      本質還是管理的AbstractBaseCronTask子類在線程池中的具體對象,不同的地方是類級別定時任務是一個具體的任務類僅生成一個對象,class路徑即是唯一的標識,而方法級別的定時任務均基于BaseMethodLevelTask生成無數個對象,具體標識則是構造函數傳入的Bean的反射對象和方法名。

      對此部分感興趣的可以一起參與開發,該項目地址:Gitee源碼,主要為其中task-component模塊。

      組件使用方法

      根據Git地址,將源碼down下,編譯安裝到本地私倉中,以Maven的形式引入即可,該組件遵循Spring-starter規范,開箱即用。

              <dependency>
                  <groupId>com.gcc.container.components</groupId>
                  <artifactId>task-component</artifactId>
                  <version>1.0.0</version>
              </dependency>

      Yaml配置說明

      給出一個yaml模板:

      gcc-task:
        task-class-package : *
        data-file-path: /opt/myproject/task_info.json

      對于task-component的配置只有兩個,task-class-packagedata-file-path

      屬性 說明 是否必須 缺省值
      task-class-package

      指定定時任務存放的包路徑,不填寫或無此屬性則默認全項目掃描,填寫后可有效減少初始化時間

      *
      data-file-path

      定時任務管理的數據存放文件及其路徑,默認不填寫則存放項目運行目錄下,如自行擴展實現,則該配置自動失效

      class:db/task_info.json

      此處的兩個配置項都包含默認值,所以不進行yml的gcc-task仍舊可以使用該組件

      新建定時任務

      對于該組件在進行定時任務的創建時,有兩種,分別是類級定時任務和方法級定時任務,兩者的區別在于是以類為基本的單位還是以方法為一個定時任務的單位,對于運行效果則并無區別,根據個人喜好,選擇使用哪一種即可

      類級定時任務

      新增一個定時任務邏輯,則需要實現基類AbstractBaseCronTask ,并加入注解 @ClassJob

      ClassJob的參數如下

      參數說明樣例
      cron 定時任務默認的執行周期,僅在首次初始化該任務使用(必填 10 0/2 * * * ?
      desc 任務描述,非必填 這是測試任務
      bootup 是否開機自啟動,缺省值為 false false

      cron 屬性僅在第一次新增該任務時提供一個默認的執行周期,必須填寫,后續任務加載后,定時任務相關數據會被存放在文件或數據庫中,此時則以文件或數據庫中該任務的cron為主,代碼中的注解則不會生效,如果想重置,則刪除已經持久化的任務即可。

      一個完整的Demo如下:

      @TaskJob(cron = "10 0/2 * * * ?" ,desc = "這是一個測試任務",bootup = true)
      public class TaskMysqlOne extends AbstractBaseCronTask {
      
          public TaskMysqlOne(TaskEntity taskEntity) {
              super(taskEntity);
          }
      
          @Override
          public void beforeJob() {
          }
      
          @Override
          public void startJob() {
          }
      
          @Override
          public void afterJob() {
          }
      }

      繼承AbstractBaseCronTask 必須要實現攜帶TaskEntity參數的構造函數beforeJob()startJob()afterJob() 三個方法即可。原則上這三個方法是規范定時任務的執行,實際使用,只需要把核心邏輯放在三個方法中任何一個即可

      因定時任務類是非SpringBean管理的類,所以在自定義的定時任務類內無法使用任何Spring相關的注解(如@Autowired)但是卻可以通過自帶的getServer(Class<T> className)方法來獲取任何Spring上下文中的Bean

      例如,你有一個UserService的接口及其Impl的實現類,想在定時任務類中使用該Bean,則可以:

      @TaskJob(cron = "10 0/2 * * * ?" ,desc = "這是一個測試任務",bootup = true)
      public class TaskMysqlOne extends AbstractBaseCronTask {
      
          public TaskMysqlOne(TaskEntity taskEntity) {
              super(taskEntity);
          }
      
          @Override
          public void beforeJob() {
          }
      
          @Override
          public void startJob() {
              List<String> names = getServer(UserService.class).searchAllUserName();
              //后續邏輯……
              //其他邏輯
          }
      
          @Override
          public void afterJob() {
      
          }
      }

      方法級定時任務

      如果不想新建類,或者不想受限于AbstractBaseCronTask的束縛,則可以像xxl-job定義定時任務一樣,直接在某個方法上標注@MethodJob注解即可。

      @MethodJob的參數如下:

      參數說明樣例
      cron 定時任務默認的執行周期,僅在首次初始化該任務使用(必填 10 0/2 * * * ?
      desc 任務描述,非必填 這是測試任務
      bootup 是否開機自啟動,缺省值為 false false

      通ClassJob一樣,cron 屬性僅在第一次新增該任務時提供一個默認的執行周期,必須填寫,后續任務加載后,定時任務相關數據會被存放在文件或數據庫中,此時則以文件或數據庫中該任務的cron為主,代碼中的注解則不會生效,如果想重置,則刪除已經持久化的任務即可。

      下面是一個例子:

      //正常定義的Service接口
      public interface AsyncTestService {
          void  taskJob();
      
      }
      
      //Service接口實現類
      @Service
      public class AsyncTestServiceImpl implements AsyncTestService {
      
          @MethodJob(cron = "11 0/1 * * * ?",desc = "這是個方法級任務")
          @Override
          public void taskJob() {
              log.info("方法級別任務查詢關鍵字為企業的數據");
              QueryWrapper<ArticleEntity> query = new QueryWrapper<>();
              query.like("art_name","企業");
              List<ArticleEntity> data = articleMapper.selectList(query);
              log.info("查出條數為{}",data.size());
          }
      }

      注:該注解僅支持SpringBoot中標注為@Component@Service@Repository的Bean

      動態調度任務接口說明

      定時任務相關的管理操作均封裝在TaskScheduleManagerService接口中,接口內容如下:

      public interface TaskScheduleManagerService {
          /**
           * 查詢在用任務
           * @return
           */
          List<TaskVo> searchTask(SearchTaskDto dto);
          /**
           * 查詢任務詳情
           * @param taskId 任務id
           * @return TaskEntity
           */
          TaskVo searchTaskDetail(String taskId);
          /**
           * 運行指定任務
           * @param taskId 任務id
           * @return TaskRunRetDto
           */
          TaskRunRetVo runTask(String taskId);
          /**
           * 關停任務
           * @param taskId 任務id
           * @return TaskRunRetDto
           */
          TaskRunRetVo shutdownTask(String taskId);
          /**
           * 開啟任務
           * @param taskId 任務id
           * @return TaskRunRetDto
           */
          TaskRunRetVo openTask(String taskId);
          /**
           * 更新任務信息
           * @param entity 實體
           * @return TaskRunRetDto
           */
          TaskRunRetVo updateTaskBusinessInfo(TaskEntity entity);
      }

      可直接在外部項目中注入使用即可:

      @RestController
      @RequestMapping("/myself/manage")
      public class TaskSchedulingController {
          
          //引入依賴后可直接使用注入
          @Autowired
          private TaskScheduleManagerService taskScheduleManagerService;
      
          @GetMapping("/detail")
          @Operation(summary = "具體任務對象")
          public Response searchDetail(String taskId){
              return Response.success(taskScheduleManagerService.searchTaskDetail(taskId));
          }
      
          @GetMapping("/shutdown")
          @Operation(summary = "關閉指定任務")
          public Response shutdownTask(String taskId){
              return Response.success(taskScheduleManagerService.shutdownTask(taskId));
          }
      
          @GetMapping("/open")
          @Operation(summary = "開啟指定任務")
          public Response openTask(String taskId){
              return Response.success(taskScheduleManagerService.openTask(taskId));
          }
      }

      接口效果:

       

      可使用該接口,進行UI頁面開發。

      關于任務持久化的擴展

      在實現思路中提到過,task-component的執行原理需要將注冊后的任務持久化,下次再啟動項目時,則直接使用持久化的TaskEntity來加載定時任務。

      考慮到定時任務的數量不大,且對于交互要求不高,另外考慮封裝成組件的獨立性普適性,不想額外引入數據庫依賴和ORM框架,所以組件默認的是以JSON文件的形式進行存儲,實際使用中,考慮到便利性,可以自行對持久化部分進行擴展。

      所謂持久化,其實本制是持久化TaskEntity對象,TaskEntity對象如下:

      @Data
      public class TaskEntity implements Serializable {
      
          /**
          * 任務ID(唯一)
          */
          private String taskId;
      
          /**
          * 任務名稱
          */
          private String taskName;
      
          /**
          * 任務描述
          */
          private String taskDesc;
      
          /**
          * 遵循cron 表達式
          */
          private String taskCron;
      
          /**
           * 類路徑
           */
          private String taskClass;
      
           /**
           * 任務級別  CLASS_LEVEL 類級別,METHOD_LEVEL 方法級別
           */
          private TaskLevel taskLevel;
      
          /**
           * 任務注冊時間
           */
          private String taskCreateTime;
          /**
           * 是否啟用,1啟用,0不啟用
           */
          private Integer taskIsUse;
      
          /**
           * 是否系統啟動后立刻運行 1是。0否
           */
          private Integer taskBootUp;
      
          /**
           * 上次運行狀態 1:成功,0:失敗
           */
          private Integer taskLastRun;
          /**
           * 任務是否在內存中 1:是,0:否
           */
          private Integer taskRamStatus;
      
           /**
           * 外部配置 (擴展待使用字段)
           */
          private String taskOutConfig;
      
          /**
           * 加載配置
           */
          private String loadConfigure;
      }

      擴展的主要操作為兩步:

      1. 新建存放taskEntity的表
      2. 實現TaskRepository接口 

      首先新建數據庫表,這里以Mysql為例給出建表語句:

      DROP TABLE IF EXISTS `tb_task_info`;
      CREATE TABLE `tb_task_info` (
        `task_id` varchar(100) NOT NULL PRIMARY KEY ,
        `task_name` varchar(255) DEFAULT NULL,
        `task_desc` text,
        `task_cron` varchar(20) DEFAULT NULL,
        `task_class` varchar(100) DEFAULT NULL COMMENT '定時任務類路徑',
        `task_level` varchar(50) DEFAULT NULL COMMENT '任務級別:類級別(CLASS_LEVEL)、方法級別(METHOD_LEVEL)',
        `task_is_use` tinyint DEFAULT NULL COMMENT '是否啟用該任務,1:啟用,0禁用',
        `task_boot_up` tinyint DEFAULT NULL COMMENT '是否為開機即運行,1:初始化即運行,0,初始化不運行',
        `task_out_config` text CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci COMMENT '定時任務額外配置項,采用json結構存放',
        `task_create_time` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '定時任務追加時間',
        `task_last_run` tinyint DEFAULT NULL COMMENT '任務上次執行狀態;1正常,0執行失敗,null未知',
        `task_ram_status` tinyint DEFAULT NULL COMMENT '任務當前狀態;1內存運行中,0內存移除',
        `loadConfigure` text  COMMENT '加載相關配置',  
      PRIMARY KEY (`task_id`) USING BTREE
      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 ROW_FORMAT=DYNAMIC;

      實現TaskEntityRepository 接口,接口內容為:

      public interface TaskEntityRepository {
          /**
           * 新增數據
           * @param entity
           * @return int
           */
          int save(TaskEntity entity);
          /**
           * 刪除任務,根據TaskId
           * @param id id
           * @return int
           */
          int removeByTaskId(String id);
          
           /**
           * 更新任務(除taskId外,所有字段必須更新)
           * @param entity 實體
           * @return int
           */
          int update(TaskEntity entity);
          
          /**
           * 查詢全部任務
           * @return List<TaskEntity>
           */
          List<TaskEntity> queryAllTask();
      
      }

       

      只需要新建類,然后實現該接口即可,如下是基于Mybatis-Plus進行的實現樣例:

      @Mapper
      public interface TaskDataMapper extends BaseMapper<TaskEntity> {
      }
      
      @Repository
      //此處一定要用@Primary注解進行固定
      @Primary
      public class TaskEntityReponsitoryImpl implements TaskEntityRepository {
      
          @Autowired
          private TaskDataMapper taskDataMapper;
      
          @Override
          public int save(TaskEntity entity) {
              entity.setTaskCreateTime(DateUtil.now());
              return taskDataMapper.insert(entity);
          }
          @Override
          public int removeByTaskId(String id) {
              QueryWrapper<TaskEntity> query = new QueryWrapper<>();
              query.eq("task_id",id);
              return taskDataMapper.delete(query);
          }
          @Override
          public int update(TaskEntity entity) {
              UpdateWrapper<TaskEntity> update = new UpdateWrapper<>();
              update.eq("task_id",entity.getTaskId());
              return taskDataMapper.update(entity,update);
          }
          @Override
          public List<TaskEntity> queryAllTask() {
              return taskDataMapper.selectList(new QueryWrapper<>());
          }
      }

       

      至此,項目中則可以以數據庫表的形式來管理定時任務:

       

      任務運行日志:

      [main] com.web.test.Application                 : Started Application in 3.567 seconds (JVM running for 4.561)
      [main] .g.c.c.t.c.InitTaskSchedulingApplication : 【定時任務初始化】 容器初始化
      [main] o.s.s.c.ThreadPoolTaskScheduler          : Initializing ExecutorService
      [main] .g.c.c.t.c.InitTaskSchedulingApplication : 【定時任務初始化】定時任務初始化任務開始
      [main] c.g.c.c.task.compont.TaskScheduleImpl    : 【定時任務初始化】裝填任務:TaskTwoPerson [ 任務執行周期:15 0/2 * * * ? ] [ bootup:0]
      [main] c.g.c.c.task.compont.TaskScheduleImpl    : 【定時任務初始化】裝填任務:TaskMysqlOne [ 任務執行周期:10 0/2 * * * ? ] [ bootup:1]
      [main] c.g.c.c.task.compont.TaskScheduleImpl    : 【定時任務初始化】裝填任務:taskJob [ 任務執行周期:11 0/1 * * * ? ] [ bootup:0]
      [main] .g.c.c.t.c.InitTaskSchedulingApplication : 【定時任務初始化】定時任務初始化任務完成
      [main] c.g.c.c.task.service.AfterAppStarted     : 【定時任務自運行】運行開機自啟動任務
      [main] TaskMysqlOne                             : ---------------------任務 TaskMysqlOne 開始執行-----------------------
      [main] TaskMysqlOne                             : 任務描述:這是一個測試任務
      [main] TaskMysqlOne                             : 我是張三
      [main] TaskMysqlOne                             : 任務耗時:約 0.0 s
      [main] TaskMysqlOne                             : ---------------------任務 TaskMysqlOne 結束執行-----------------------
      
      [task-thread-1] taskJob                                  : ---------------------任務 taskJob 開始執行-----------------------
      [task-thread-1] taskJob                                  : 任務描述:這是個方法級任務
      [task-thread-1] c.w.t.service.impl.AsyncTestServiceImpl  : 方法級別任務查詢關鍵字為企業的數據
      [task-thread-1] c.w.t.service.impl.AsyncTestServiceImpl  : 查出條數為1
      [task-thread-1] taskJob                                  : 任務耗時:約 8.45 s
      [task-thread-1] taskJob                                  : ---------------------任務 taskJob 結束執行-----------------------

       

      posted @ 2024-11-22 21:29  糖拌西紅柿  閱讀(1519)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 国产高跟黑色丝袜在线 | 中文有无人妻VS无码人妻激烈| 国产精欧美一区二区三区| 丁香五月亚洲综合在线国内自拍| 久久99九九精品久久久久蜜桃| 狠狠五月深爱婷婷网| 40岁大乳的熟妇在线观看| 色老头在线一区二区三区| 五月婷婷开心中文字幕| 亚洲日韩精品无码一区二区三区| 伊人久久大香线蕉网av| 亚洲色成人一区二区三区人人澡人人妻人人爽人人蜜桃麻豆 | 日韩精品一区二区三区激情| 亚洲精品熟女一区二区| 日韩亚洲精品中文字幕| 美女一区二区三区亚洲麻豆| 成人爽A毛片在线视频淮北| 激情在线网| 成人国产精品三上悠亚久久| 欧美亚洲高清日韩成人| 99热门精品一区二区三区无码| 国产精品区一区第一页| 成人做受120秒试看试看视频 | 国产福利姬喷水福利在线观看 | 日本一区二区不卡精品| 东京热一精品无码av| 狠狠噜天天噜日日噜视频麻豆| 中文文字幕文字幕亚洲色| 老熟妇老熟女老女人天堂| 精品成在人线av无码免费看| 国产精品国产三级国产午| 国产国拍亚洲精品永久软件| 日韩国产精品中文字幕| 蜜桃臀av一区二区三区| 在线观看视频一区二区三区| 亚洲中文字幕人妻系列| 内乡县| 亚洲女人的天堂在线观看| 国产一卡2卡三卡4卡免费网站| 五月婷婷激情视频俺也去淫| 国产精品色哟哟成人av|