目錄:
調度中心端如何進行任務管理及調度
分佈式調度平臺XXL-JOB源碼分析-執行器端
----------------------------------------------------------
調度中心端如何進行任務管理及調度
架構圖:
xxl 上圖是我們要進行源碼分析的2.1版本的整體架構圖。其分爲兩大塊,調度中心和執行器,本文先分析調度中心,也就是xxl-job-admin這個包的代碼。
在application.properties配置正確的數據庫連接信息後,直接啓動XxlJobAdminApplication即可。
配置類XxlJobAdminConfig,裏面維護了一些調度中心端的配置數據。
XxlJobScheduler這個組件實現了InitializingBean接口,所以spring容器在初始化的時候會調用afterPropertiesSet方法,此方法如下:
第一步國際化相關。
第二步監控相關。
第三步失敗重試相關。
第四步啓動admin端服務,接收註冊請求等。
第五步JobScheduleHelper調度器,死循環,在xxl_job_info表裏取將要執行的任務,更新下次執行時間的,調用JobTriggerPoolHelper類,來給執行器發送調度任務的.
JobScheduleHelper
這個類就是死循環從xxl_job_info表中取出未來5秒內要執行的任務,進行調度分發。
啓動了兩個守護線程,先來看scheduleThread。
死循環內的代碼如上圖,首先利用for update語句進行獲取任務的資格鎖定,再去獲取未來5秒內即將要執行的任務。
ringData是以0到59的整數爲key,以jobId集合爲value的Map集合。這個集合數據的處理邏輯,就在我們第二個守護線程ringThread中。
while (!ringThreadToStop) {
2 try {
3 // second data
4 List<Integer> ringItemData = new ArrayList<>();
5 int nowSecond = Calendar.getInstance().get(Calendar.SECOND); // 避免處理耗時太長,跨過刻度,向前校驗一個刻度;
6 for (int i = 0; i < 2; i++) {
7 List<Integer> tmpData = ringData.remove( (nowSecond+60-i)%60 );
8 if (tmpData != null) {
9 ringItemData.addAll(tmpData);
10 }
11 }
12 // ring trigger
13 logger.debug(">>>>>>>>>>> xxl-job, time-ring beat : " + nowSecond + " = " + Arrays.asList(ringItemData) );
14 if (ringItemData!=null && ringItemData.size()>0) {
15 // do trigger
16 for (int jobId: ringItemData) {
17 // do trigger
18 JobTriggerPoolHelper.trigger(jobId, TriggerTypeEnum.CRON, -1, null, null);
19 }
20 // clear
21 ringItemData.clear();
22 }
23 } catch (Exception e) {
24 if (!ringThreadToStop) {
25 logger.error(">>>>>>>>>>> xxl-job, JobScheduleHelper#ringThread error:{}", e);
26 }
27 }
28 // next second, align second
29 try {
30 TimeUnit.MILLISECONDS.sleep(1000 - System.currentTimeMillis()%1000);
31 } catch (InterruptedException e) {
32 if (!ringThreadToStop) {
33 logger.error(e.getMessage(), e);
34 }
35 }
36 }
minTim屬性,作用待明確
jobTimeoutCountMap屬性,計數,key爲jobId,value使用AtomicInteger計數。
helper靜態變量指向自己本身,提供外部靜態方法調用。
重要方法,向兩種線程池其中之一提交調度任務,進行調度,引出XxlJobTrigger這個類,一路跟進去.
分佈式調度平臺XXL-JOB源碼分析-執行器端
XxlJobExecutorApplication爲我們執行器的啓動項,其中有個XxlJobConfig的配置項,發現其中有個屬性爲adminAddresses,這個就是我們調度中心的地址。
XxlJobSpringExecutor
聲明瞭init方法爲start,點進來,
它又實現了ApplicationContextAware接口,用來保存spring的上下文信息。
它還有個父類XxlJobExecutor,暫時未找到其他子類。
程序開始執行start方法:
第一步,調用了本類的私有方法,這個方法就是把JobHandler的實現類取出來,再調用registJobHandler(name, handler)進行註冊。
1.日誌處理器初始化
2.向adminBizList字段中放入XxlRpcReferenceBean返回的代理類,作用之後會單獨開一篇註冊心跳的文章說明。
3.任務日誌清除
4.任務結果回調處理線程
5.啓動另一個執行器的執行線程XxlRpcProviderFactory這個類是XXl其他的開源項目,自研RPC
XxlRpcProviderFactory
看名字就知道這個類是可以返回Rpc調用服務提供端的工廠類,接上文,看他的initRpcProvider方法。
此時的ServiceRegistry爲ExecutorServiceRegistry,調用其start,以30秒的間隔和調度中心進行心跳通知,然後調用server的start方法,此時server爲NettyHttpServer.
從serviceData中拿到我們之前調用addService方法添加的服務實現類,這裏是ExecutorBizImpl,這裏反射調用的方法是run。
如果沒有正在執行此任務的線程,那就調用XxlJobExecutor.registJobThread()啓動一個線程,最後將任務數據推送給這個可能是從jobThreadRepository獲取到的也可能是新創建的線程,如下圖。
至此,執行器完成了啓動,暴露ExecutorBiz服務,接收任務調度數據TriggerParam,並在JobThread線程中完成任務配置的業務handler的執行。