Quartz叢集增強版_00.How to use?(如何使用)

funnyZpC發表於2024-11-11

Quartz叢集增強版_00.How to use?(如何使用)

轉載請著名出處 https://www.cnblogs.com/funnyzpc/p/18540378

開源地址 https://github.com/funnyzpc/quartz

表的基本結構

總的來說任務的配置及開發基本遵從上圖的表的基本關係,除 app 以及 node 之外均需要手動手動配置,appnode 在執行端啟動的時候會自動生成對應 app 以及 node 的資料 ~

後管配置

先看一下後管的基本頁面~
因為 appnode 是一對多的關係,這裡就放到一個page下:

  • 這裡需要說明的是appnode一般無需新增,如果特殊情況下請參照下圖:

app新增

node新增

因為node必須關聯已有的app才可新增,新增入口在app列表

另外,需要說明的是:

  • 如果執行端獲取不到宿主機IP以及主機名稱會隨機生成一個同名的 主機IP以及主機名稱,此時在管理端手動新增就毫無意義了

    刪除

  • 刪除應用必須先刪除應用關聯的節點(node),節點被刪除則節點對應的執行端無法執行其任務,刪除應用也是

  • 刪除應用或節點不會變更任務及執行項的狀態,也不會刪除任務及執行項,沒有節點的執行項不會執行也會定期被清理

    啟用/關閉

啟用與關閉只操作節點或應用,關閉節點則節點下的所有任務均不會執行,關閉應用則應用關聯的所有結點都不會執行任務,同時這個操作也不會變更任務或執行項~

再看看節點任務及執行配置:

任務/執行配置是管理端主要任務,執行配置使用關聯任務配置(PID)關聯相應的任務(job),執行項(execute)是不可獨立存在的!

新增任務配置

  • 應用名稱/排程名稱就是自動或手動配置的應用資訊
    任務狀態在配置時僅可有 初始化(INIT)/正常執行(EXECUTING) 這兩種狀態,如果只是配置不想立即執行就選 初始化(INIT)

    新增執行配置-CRON時間任務

  • 任務型別僅可為簡單任務(SIMPLE)或表示式(CRON)的時間項的任務,兩種型別的執行配置(execute)填寫的欄位會有區別
    CRON任務CRON表示式是必填項,時區現階段預設是Asia/Shanghai ,後續會改成從系統獲取預設
    開始時間一般不填則預設就是-1,新增提交後是按當前時間補充
    結束時間也是非必填的,結束時間預設也是-1,結束時間如果是-1則在執行完最後一次任務之後會補充為最後一次執行時間

    新增執行配置-SIMPLE時間任務

  • 圖中圈出的為必填項,需要說明的是:如果執行結束時間執行次數均設定,具體任務執行時會依限制範圍最小的為實際執行,比如設定的結束時間較長但是執行次數只有幾次,那最終大機率只會以執行次數為限制執行

    另外,對於執行配置,當執行完成後,對應的執行配置僅可刪除不可 修改或啟停,已經完成的對此類操作是沒有意義的,不如新增一個執行配置

管理端開發配置及整合

這裡僅以springboot為例:

  • 新增依賴,如果有maven私服建議放到私服
    <dependency>
        <groupId>org.quartz-scheduler.internal</groupId>
        <artifactId>quartz-client</artifactId>
        <version>2.3.2</version>
        <!-- 這是本地引入,建議放到私服-->
        <scope>system</scope>
        <systemPath>${pom.basedir}/src/main/resources/lib/quartz-client-2.3.2.jar</systemPath>
    </dependency>
  • 啟動類需要排除自動裝配
// 這一行是重點!
@SpringBootApplication(exclude = {QuartzAutoConfiguration.class})
public class MeeAdminApplication {
	/**
	 * 日誌
	 */
	private static final Logger LOG= LoggerFactory.getLogger(MeeAdminApplication.class);

	public static void main(String[] args)throws Exception {
		ConfigurableApplicationContext application = SpringApplication.run(MeeAdminApplication.class, args);
		Environment env = application.getEnvironment();
		String ip = InetAddress.getLocalHost().getHostAddress();
		String port = env.getProperty("server.port");
		String path = env.getProperty("server.servlet.context-path");
		LOG.info("\n\t----------------------------------------------------------\n\t" +
				"Application MeeAdminApplication is running!\n\t" +
				"Local: \t\thttp://localhost:" + port + path + "/\n\t" +
				"External: \thttp://" + ip + ":" + port + path + "/\n\t" +
				"----------------------------------------------------------");
	}

}
  • 需要配置一個例項以使用
@Service
public final class QrtzJobServiceImpl implements QrtzJobService {

    /**
     *   日誌
     */
    private static final Logger LOG = LoggerFactory.getLogger(QrtzJobServiceImpl.class);
    
    /**
     * quartz定時任務api
     */
    private final Scheduler scheduler;

    public QrtzJobServiceImpl(DataSource dataSource) {
        this.scheduler = new StdScheduler(dataSource);
    }
}
  • 呼叫sdk
    @Override
    public MeeResult<Integer> updateJobState(String job_id,String state) {
        Object[] result = scheduler.updateJobStateInAll(job_id,state);
        int updateCount = (int)result[0];
        if(updateCount>0){
            return ResultBuild.build(updateCount);
        }else{
            return ResultBuild.fail((String)result[1]);
        }
    }

Scheduler 提供了多種多樣的api,注意部分介面的區別:

如果管理端執行端一體 則無需引入client依賴(quartz-client),也無需在啟動類中排除自動裝配(QuartzAutoConfiguration),使用sdk也無需使用構造方式傳入database,僅此即可:

    @Autowired
    private Scheduler scheduler;

執行端開發配置及整合

  • 引入依賴同時排除原生Quartz
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
            <version>${spring-boot-current.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>org.quartz-scheduler</groupId>
                    <artifactId>quartz</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.quartz-scheduler.internal</groupId>
            <artifactId>quartz-core</artifactId>
            <version>2.3.2</version>
            <!-- 這是本地引入,建議放到私服-->
            <scope>system</scope>
            <systemPath>${pom.basedir}/src/main/resources/lib/quartz-core-2.3.2.jar</systemPath>
        </dependency>
  • 新增依賴配置項
### ----------- quartz ------------------
spring.quartz.job-store-type=jdbc
spring.quartz.properties.org.quartz.jobStore.class=org.springframework.scheduling.quartz.LocalDataSourceJobStore
spring.quartz.properties.org.quartz.jobStore.clusterCheckinInterval=6000
spring.quartz.properties.org.quartz.jobStore.driverDelegateClass=org.jdbcjobstore.impl.org.quartz.StdJDBCDelegate
spring.quartz.properties.org.quartz.jobStore.isClustered=true
# 表名字首
spring.quartz.properties.org.quartz.jobStore.tablePrefix=QRTZ_
spring.quartz.properties.org.quartz.scheduler.instanceName=${spring.application.name}
#spring.quartz.properties.org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
spring.quartz.properties.org.quartz.threadPool.class=org.quartz.impl.MeeThreadPool
# 執行緒數配置
spring.quartz.properties.org.quartz.threadPool.threadCount=10
spring.quartz.properties.org.quartz.threadPool.threadPriority=5
# 綫程繼承初始化執行緒的上下文類載入器
spring.quartz.properties.org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true
#Whether to enable pessimistic lock to control trigger concurrency in the cluster 是否啟用悲觀鎖來控制叢集中的觸發併發
spring.quartz.properties.org.quartz.jobStore.acquireTriggersWithinLock=true

配置項裡面 要注意執行緒數的配置,如果使用的 MeeThreadPoolthreadCount為最大執行緒數,核心執行緒數 threadCount-2 ,最少為2,具體多少按實際CPU核心個數以及是否是IO密集型還是CPU密集型來配置即可~
其次要注意 tablePrefix 如果表名有變更則按照變更後的表名字首配置即可

  • 定義一個任務
    • 如果使用的是spring提供的QuartzJobBean來開發:
      import com.mee.quartz.util.DateUtil;
      import org.quartz.JobExecutionContext;
      import org.quartz.JobExecutionException;
      import org.quartz.impl.QrtzExecute;
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.scheduling.quartz.QuartzJobBean;
      
      import javax.sql.DataSource;
      
      
        public class ATestJob extends QuartzJobBean {
      
        private static final Logger log = LoggerFactory.getLogger(ATestJob.class);
      
          @Override
          protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
              try {
                  log.info("===>ATestJob::executeInternal {}-{} : {}-{}<===" ,context.getJobId(),context.getExecuteId(),context.getJobType(),context.getJobClassName());
              } catch (Exception e) {
                  throw new JobExecutionException(e);
              }
          }
      
      }
    
    • 如果使用的是Quartz提供的Job介面來開發,也可:
      import org.quartz.Job;
      import org.quartz.JobExecutionContext;
      import org.quartz.JobExecutionException;
      import org.quartz.impl.QrtzExecute;
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      
      import java.util.concurrent.TimeUnit;
      
      public class Job01TestService  implements Job {
      
      private static final Logger LOGGER = LoggerFactory.getLogger(Job01TestService.class);
      
      @Override
      public void execute(JobExecutionContext context) throws JobExecutionException {
          LOGGER.info("=>>{}-{}.{}-{}",context.getJobId(),context.getExecuteId(),context.getJobType(),context.getJobClassName());
      }
    }
    
    

以上兩種方式皆可,需要注意的是,不管是繼承 QuartzJobBean 還是實現的 ``Job,均無需將類著名為spring bean類(@Service or @Component),Quartz內部自會建立任務類為spring bean ~

開發注意事項

  • 使用 quartz-client 新增的任務一般最晚會在 5秒 之後執行,因為任務輪詢是 5秒 一輪詢
  • 執行端執行異常(Quartz內的非業務的)的任務最晚在15S之後恢復任務執行,因為叢集/缺火處理是 15秒 一輪詢
  • 新增的任務如果不執行首先則要注意 spring.quartz.properties.org.quartz.scheduler.instanceName 配置項是否有配置,這個配置項對應 app 表中的 application 欄位
  • 實際任務如有日誌出現 任務延遲,建議排查宿主機資源是否佔滿,或者執行緒數配置是否合理

相關文章