關於TbSchedule任務調度管理框架的整合部署

一、前言

任務調度管理作爲基礎架構通常會出現於我們的業務系統中,目的是讓各種任務能夠按計劃有序執行。比如定時給用戶發送郵件、將數據表中的數據同步到另一個數據表都是一個任務,這些相對耗時的操作通過任務調度系統來異步並行執行,既能提高任務的執行效率又能保障任務執行的可靠性。

實現的方式也是多種多樣,比如使用Timer進行簡單調度或者使用Quartz類似的框架,本文基於淘寶開源框架TbSchedule實現,其設計目的是讓批量任務或者不斷變化的任務能夠被動態的分配到多個主機的JVM中,在不同的線程組中並行執行,所有的任務能夠被不重複,不遺漏的快速處理,目前被應用於阿里巴巴衆多業務系統。

請參照http://code.taobao.org/p/tbschedule/wiki/index/,相關內容不再重複介紹,本文記錄了詳細的部署整合操作步驟

 

二、Zookeeper部署

1、TbSchedule依賴於Zookeeper,實現任務的分佈式配置及各服務間的交互通信,Zookeeper以TreeNode類型進行存儲,支持Cluster形式部署且保證最終數據一致性,關於ZK的資料網上比較豐富,相關概念不再重複介紹,本文以zookeeper-3.4.6爲例,請從官網下載http://zookeeper.apache.org

2、創建ZookeeperLab文件夾目錄,模擬部署3臺Zookeeper服務器集羣,目錄結構如下。

     

3、解壓從官網下載的zookeeper-3.4.6.tar文件,並分別複製到三臺ZkServer的zookeeper-3.4.6文件夾。

     

4、分別在三臺ZkServer的data目錄下創建myid文件(注意沒有後綴),用於標識每臺Server的ID,在Server1\data\myid文件中保存單個數字1,Server2的myid文件保存2,Server3的myid保存3。

5、創建ZkServer的配置文件,在zookeeper-3.4.6\conf文件夾目錄下創建zoo.cfg,可以從示例的zoo_sample.cfg 複製重命名。因爲在同一臺機器模擬Cluster部署,端口號不能重複,配置文件中已經有詳細的解釋,修改後的配置如下,其中Server1端口號2181,Server2端口號2182,Server3端口號2183。


# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial 
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between 
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just 
# example sakes.
dataDir=E:/ZookeeperLab/server1/data
dataLogDir=E:/ZookeeperLab/server1/logs
# the port at which the clients will connect
clientPort=2181
# the maximum number of client connections.
# increase this if you need to handle more clients
#maxClientCnxns=60
#
# Be sure to read the maintenance section of the 
# administrator guide before turning on autopurge.
#
# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
#
# The number of snapshots to retain in dataDir
#autopurge.snapRetainCount=3
# Purge task interval in hours
# Set to "0" to disable auto purge feature
#autopurge.purgeInterval=1
server.1=127.0.0.1:2888:3888
server.2=127.0.0.1:2889:3889
server.3=127.0.0.1:2890:3890

6、通過zookeeper-3.4.6\bin文件夾zkServer.bat文件啓動ZKServer,由於Cluster部署需要選舉Leader和Followers,所以在3個ZKServer全部啓動之前會提示一個WARN,屬正常現象。

      

7、Zookeeper啓動成功後可以通過zookeeper-3.4.6\bin文件夾的 zkCli.bat驗證連接是否正常,比如創建節點“create /testnode helloworld”,查看節點“get /testnode”,連接到組羣中其它ZkServer,節點數據應該是一致的。更多指令請使用help命令查看。

     

8、對於Linux環境下部署基本一致,zoo.cfg配置文件中data和datalog文件夾路徑改爲linux格式路徑,使用“./zkServer.sh start-foreground”命令啓動ZkServer,注意start啓動參數不能輸出異常信息。

      

9、至此Zookeeper的配置完畢。

 

三、SVN從TaoCode獲取並Import TbSchedule源碼(請先配置Maven環境)

 

四、TbSchedule控制檯的部署

1、TbSchedule Console是基於web頁面對調度任務配置、部署、監控的終端。

2、將源碼目錄下的console\ScheduleConsole.war包及depend-lib庫部署到Web應用服務器,本文以Tomcat 7.0.54爲例,相關步驟不再描述。

3、首次打開頁面會跳轉到“基礎信息配置”Config.jsp頁面,其中前2項爲Zookeeper服務的連接配置,請正確填寫前面部署的Zookeeper服務器地址和端口號,多個ZKServer可以使用逗號分隔。後3項是用於標識ZkServer中調度任務配置的根節點和節點權限,無嚴格要求

     

 4、點擊保存後,會提示 “錯誤信息:Zookeeper connecting ......localhost:2181”,如果ZKServer配置正確可以不用理會,直接點擊“管理主頁”,若不能正常跳轉到Index.jsp頁面請重新檢查Zookeeper的配置,建議關閉防火牆。

     

5、TbSchedule Console Web站點對應的兩個地址

     [監控頁面]       http://localhost:8081/ScheduleConsole/schedule/index.jsp

     [管理頁面]       http://localhost:8081/ScheduleConsole/schedule/index.jsp?manager=true

     如果以上地址能正常訪問則TbSchedule Console的部署配置完成

 

五、Task場景設計

1、假設場景:任務需要將訂單表tbOrder中制單日期在20141201--20141208內(共8天)的數據同步到備份tbOrder_copy 表,其中每2天分爲一個任務組並行同步(每次提取500條),關於任務組的劃分和TbSchedule中TaskItem的相關概念請先參考wiki,後續也會有部分解釋。

2、數據環境使用MySql數據庫,創建tbOrder和tbOrder_Copy數據表,結構相同,同時在tbOrder事先生成好測試數據,建議每天的數據量在1000條以上。

      

     

 

六、數據同步任務實現

1、創建TaskCenter Demo Project,添加對tbschedule project的依賴,並集成Spring Framework。

     

2、創建OrderInfo實體類,屬性對應tbOrder表,用於映射從數據表取的數據。

3、創建DataSyncABean任務類,實現IScheduleTaskDealSingle<T>泛型接口的selectTasks和execute方法,其中selectTasks方法用於取數,execute方法用於執行selectTasks返回的Result,關於代碼中任務片段的劃分和TbSchedule中TaskItem的相關概念後續再解釋,代碼參考如下。

package Task;

import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

import com.taobao.pamirs.schedule.IScheduleTaskDealSingle;
import com.taobao.pamirs.schedule.TaskItemDefine;

import DBHelper.*;

public class DataSyncABean implements IScheduleTaskDealSingle<OrderInfo> {

    public List<OrderInfo> selectTasks(String taskParameter, String ownSign,
            int taskItemNum, List<TaskItemDefine> queryCondition,
            int eachFetchDataNum) throws Exception {

        List<OrderInfo> result = new ArrayList<OrderInfo>();
        if (queryCondition.size() == 0) {
            return result;
        }

        StringBuffer condition = new StringBuffer();
        for (int i = 0; i < queryCondition.size(); i++) {
            if (i > 0) {
                condition.append(",");
            }
            condition.append(queryCondition.get(i).getTaskItemId());
        }

        /* 場景A:將tbOrder表中的數據分8個任務項,每次取200條數據, 同步到tbOrder_copy表中。 */
        String sql = "select * from tbOrder " + "where "
                + " BillNumber not in (select BillNumber from tbOrder_copy) "
                + " and RIGHT(BuildDate,1) in (" + condition + ") " + "limit "
                + eachFetchDataNum;

        System.out.println("開始執行SQL:" + sql);

        ResultSet rs = MySQLHelper.executeQuery(sql);
        while (rs.next()) {
            OrderInfo order = new OrderInfo();
            order.BillNumber = rs.getString("BillNumber");
            order.BuildDate = rs.getString("BuildDate");
            order.Customer = rs.getString("Customer");
            order.GoodsName = rs.getString("GoodsName");
            order.Amount = rs.getFloat("Amount");
            order.SaleMoney = rs.getFloat("SaleMoney");
            result.add(order);

            if (rs.isLast()) {
                break;
            }
        }
        MySQLHelper.free(rs, rs.getStatement(), rs.getStatement()
                .getConnection());

        return result;
    }

    public Comparator<OrderInfo> getComparator() {

        return null;
    }

    public boolean execute(OrderInfo task, String ownSign) throws Exception {
        String sql = "insert into tbOrder_copy values('" + task.BillNumber
                + "','" + task.BuildDate + "','" + task.Customer + "','"
                + task.GoodsName + "'," + task.Amount + "," + task.SaleMoney
                + ")";

        MySQLHelper.executeNonQuery(sql);

        System.out.println("execute:" + sql);

        return true;
    }
}

4、在Spring容器中註冊數據同步任務Bean。


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
 

    <bean id="dataSyncABean" class="Task.DataSyncABean" />
     
</beans>

5、Main函數初始化Spring容器和TbSchedule 任務管理Factory,連接ZKServer,代碼如下,也可以參照TbSchedule源碼中通過Spring進行初始化TBScheduleManagerFactory 。

import java.util.Properties;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
 

import com.taobao.pamirs.schedule.strategy.TBScheduleManagerFactory;

public class TaskCenter {

    public static void main(String[] args) throws Exception {

        // 初始化Spring
        ApplicationContext ctx = new FileSystemXmlApplicationContext(
                "src\\applicationContext.xml");

        // 初始化調度工廠
        TBScheduleManagerFactory scheduleManagerFactory = new TBScheduleManagerFactory();

        Properties p = new Properties();
        p.put("zkConnectString", "localhost:2181");
        p.put("rootPath", "/tbSchedule/Test");
        p.put("zkSessionTimeout", "60000");
        p.put("userName", "zookeeper");
        p.put("password", "zookeeper");
        p.put("isCheckParentPath", "true");

        scheduleManagerFactory.setApplicationContext(ctx);
        
        scheduleManagerFactory.init(p);  
    }
}

6、如果配置正確應該可以成功啓動該TaskDeal服務。

 

七、在TbSchedule Console創建調度任務(請事先仔細閱讀wiki中的概念解釋)

1、創建調度策略,其中“最大線程組數量”設置爲4,表示在機器上的通過4個線程組並行執行數據同步任務。

     

2、創建調度任務

     任務名稱:對應調度策略中的任務名稱,標識任務和策略的關聯關係;

     任務處理的SpringBean:對應Demo TaskDeal服務Spring容器中的任務對象ID;

<bean id="dataSyncABean" class="Task.DataSyncABean" />

每次獲取數據量:對應於bean任務類selectTasks方法參數 eachFetchDataNum;

     執行開始時間:“0 * * * * ?” 表示每分鐘的0秒開始,表達式同Quartz設置的Crontab格式,有工具可以生成,詳細解釋參照http://dogstar.iteye.com/blog/116130

2.字段 允許值 允許的特殊字符  
3.秒 0-59 , - * /  
4.分 0-59 , - * /  
5.小時 0-23 , - * /  
6.日期 1-31 , - * ? / L W C  
7.月份 1-12 或者 JAN-DEC , - * /  
8.星期 1-7 或者 SUN-SAT , - * ? / L C #  
9.年(可選) 留空, 1970-2099 , - * /  
10.表達式意義  
11."0 0 12 * * ?" 每天中午12點觸發  
12."0 15 10 ? * *" 每天上午10:15觸發  
13."0 15 10 * * ?" 每天上午10:15觸發  
14."0 15 10 * * ? *" 每天上午10:15觸發  
15."0 15 10 * * ? 2005" 2005年的每天上午10:15觸發  
16."0 * 14 * * ?" 在每天下午2點到下午2:59期間的每1分鐘觸發  
17."0 0/5 14 * * ?" 在每天下午2點到下午2:55期間的每5分鐘觸發  
18."0 0/5 14,18 * * ?" 在每天下午2點到2:55期間和下午6點到6:55期間的每5分鐘觸發  
19."0 0-5 14 * * ?" 在每天下午2點到下午2:05期間的每1分鐘觸發  
20."0 10,44 14 ? 3 WED" 每年三月的星期三的下午2:10和2:44觸發  
21."0 15 10 ? * MON-FRI" 週一至週五的上午10:15觸發  
22."0 15 10 15 * ?" 每月15日上午10:15觸發  
23."0 15 10 L * ?" 每月最後一日的上午10:15觸發  
24."0 15 10 ? * 6L" 每月的最後一個星期五上午10:15觸發  
25."0 15 10 ? * 6L 2002-2005" 2002年至2005年的每月的最後一個星期五上午10:15觸發  
26."0 15 10 ? * 6#3" 每月的第三個星期五上午10:15觸發  
27.每天早上6點  
28.0 6 * * *  
29.每兩個小時  
30.0 */2 * * *  
31.晚上11點到早上8點之間每兩個小時,早上八點  
32.0 23-7/2,8 * * *  
33.每個月的4號和每個禮拜的禮拜一到禮拜三的早上11點  
34.0 11 4 * 1-3  
35.1月1日早上4點  
36.0 4 1 1 *

 任務項:前面假設的任務場景中需要把1-8號數據按2天爲1個任務組並行數據同步,所以可以把任務劃分爲1,2,3,4,5,6,7,8一共8個任務碎片,8個任務碎片被分配到4個線程組,那麼每個線程組對應2個任務碎片,運行時任務項參數又被傳遞到bean任務類selectTasks方法的List<TaskItemDefine> queryCondition參數,例如第1個線程組調用selectTasks方法是queryCondition參數條件爲1,2 ,第2個線程組執行參數條件爲3,4,bean任務類中根據參數生成對應的BuildDate條件取數,並將結果提交到execute方法執行,從而實現並行計算。

     任務項的劃分:可以有非常多的分配策略和技巧,例如將一個數據表中所有數據的ID按10取模,就將數據劃分成了0、1、2、3、4、5、6、7、8、9供10個任務項;將一個目錄下的所有文件按文件名稱的首字母(不區分大小寫),就劃分成了A、B、C、D、E、F、G、H、I、J、K、L、M、N、O、P、Q、R、S、T、U、V、W、X、Y、Z供26個任務項 。

     

3、新增任務配置保存後,Task會按指定的時間段自動開始執行,可以從TbScheduleConsole監視到對應的線程組和任務項。

     

4、同時可以從任務TaskDeal服務控制檯監視到Debug輸出的取數SQL和 Insert語句,以及TbSchedule的Heartbeat、TaskItem Debug信息。

17:01:00.000 [DataSyncATask-4-HeartBeat] DEBUG c.t.p.s.t.TBScheduleManager - 恢復調度:DataSyncATask$192.168.56.1$4354D7079D6144AAB33F650B6FF7E532$0000000003
17:01:00.000 [DataSyncATask-2-HeartBeat] DEBUG c.t.p.s.t.TBScheduleManager - 恢復調度:DataSyncATask$192.168.56.1$FCC86240694749FD9CE84DB791448D93$0000000001
17:01:00.000 [DataSyncATask-3-HeartBeat] DEBUG c.t.p.s.t.TBScheduleManager - 恢復調度:DataSyncATask$192.168.56.1$D4D821EBFCFA47C1865A7E42278F45A7$0000000002
17:01:00.002 [DataSyncATask-0-HeartBeat] DEBUG c.t.p.s.t.TBScheduleManager - 恢復調度:DataSyncATask$192.168.56.1$D3CED88695434526B8C9220FFCD9B584$0000000000
開始執行SQL:select * from tbOrder where  BillNumber not in (select BillNumber from tbOrder_copy)  and RIGHT(BuildDate,1) in (7,8) limit 500
開始執行SQL:select * from tbOrder where  BillNumber not in (select BillNumber from tbOrder_copy)  and RIGHT(BuildDate,1) in (5,6) limit 500
開始執行SQL:select * from tbOrder where  BillNumber not in (select BillNumber from tbOrder_copy)  and RIGHT(BuildDate,1) in (3,4) limit 500
開始執行SQL:select * from tbOrder where  BillNumber not in (select BillNumber from tbOrder_copy)  and RIGHT(BuildDate,1) in (1,2) limit 500
17:01:00.289 [DataSyncATask-4-exe3] DEBUG c.t.p.s.t.TBScheduleManager - 暫停調度 :DataSyncATask$192.168.56.1$4354D7079D6144AAB33F650B6FF7E532$0000000003:FetchDataCount=147,FetchDataNum=9600,DealDataSucess=9600,DealDataFail=0,DealSpendTime=687968,otherCompareCount=0
17:01:00.291 [DataSyncATask-0-exe1] DEBUG c.t.p.s.t.TBScheduleManager - 暫停調度 :DataSyncATask$192.168.56.1$D3CED88695434526B8C9220FFCD9B584$0000000000:FetchDataCount=130,FetchDataNum=180,DealDataSucess=180,DealDataFail=0,DealSpendTime=23783,otherCompareCount=0
17:01:00.295 [DataSyncATask-3-exe0] DEBUG c.t.p.s.t.TBScheduleManager - 暫停調度 :DataSyncATask$192.168.56.1$D4D821EBFCFA47C1865A7E42278F45A7$0000000002:FetchDataCount=161,FetchDataNum=18000,DealDataSucess=18000,DealDataFail=0,DealSpendTime=1207563,otherCompareCount=0
17:01:00.297 [DataSyncATask-2-exe0] DEBUG c.t.p.s.t.TBScheduleManager - 暫停調度 :DataSyncATask$192.168.56.1$FCC86240694749FD9CE84DB791448D93$0000000001:FetchDataCount=131,FetchDataNum=700,DealDataSucess=700,DealDataFail=0,DealSpendTime=80063,otherCompareCount=0
17:01:02.674 [DataSyncATask-2-HeartBeat] DEBUG c.t.p.s.t.TBScheduleManagerStatic - DataSyncATask$192.168.56.1$FCC86240694749FD9CE84DB791448D93$0000000001:不是負責任務分配的Leader,直接返回
17:01:02.865 [DataSyncATask-3-HeartBeat] DEBUG c.t.p.s.t.TBScheduleManagerStatic - DataSyncATask$192.168.56.1$D4D821EBFCFA47C1865A7E42278F45A7$0000000002:不是負責任務分配的Leader,直接返回
17:01:02.866 [DataSyncATask-4-HeartBeat] DEBUG c.t.p.s.t.TBScheduleManagerStatic - DataSyncATask$192.168.56.1$4354D7079D6144AAB33F650B6FF7E532$0000000003:不是負責任務分配的Leader,直接返回
17:01:04.433 [DataSyncATask-0-HeartBeat] DEBUG c.t.p.s.zk.ScheduleDataManager4ZK - DataSyncATask$192.168.56.1$D3CED88695434526B8C9220FFCD9B584$0000000000:開始重新分配任務......
17:01:04.437 [DataSyncATask-0-HeartBeat] DEBUG c.t.p.s.zk.ScheduleDataManager4ZK - 
TASK_TYPE=DataSyncATask:TASK_ITEM=1:CUR_SERVER=DataSyncATask$192.168.56.1$D3CED88695434526B8C9220FFCD9B584$0000000000:REQ_SERVER=null:DEAL_PARAMETER=
TASK_TYPE=DataSyncATask:TASK_ITEM=2:CUR_SERVER=DataSyncATask$192.168.56.1$D3CED88695434526B8C9220FFCD9B584$0000000000:REQ_SERVER=null:DEAL_PARAMETER=
TASK_TYPE=DataSyncATask:TASK_ITEM=3:CUR_SERVER=DataSyncATask$192.168.56.1$FCC86240694749FD9CE84DB791448D93$0000000001:REQ_SERVER=null:DEAL_PARAMETER=
TASK_TYPE=DataSyncATask:TASK_ITEM=4:CUR_SERVER=DataSyncATask$192.168.56.1$FCC86240694749FD9CE84DB791448D93$0000000001:REQ_SERVER=null:DEAL_PARAMETER=
TASK_TYPE=DataSyncATask:TASK_ITEM=5:CUR_SERVER=DataSyncATask$192.168.56.1$D4D821EBFCFA47C1865A7E42278F45A7$0000000002:REQ_SERVER=null:DEAL_PARAMETER=
TASK_TYPE=DataSyncATask:TASK_ITEM=6:CUR_SERVER=DataSyncATask$192.168.56.1$D4D821EBFCFA47C1865A7E42278F45A7$0000000002:REQ_SERVER=null:DEAL_PARAMETER=
TASK_TYPE=DataSyncATask:TASK_ITEM=7:CUR_SERVER=DataSyncATask$192.168.56.1$4354D7079D6144AAB33F650B6FF7E532$0000000003:REQ_SERVER=null:DEAL_PARAMETER=
TASK_TYPE=DataSyncATask:TASK_ITEM=8:CUR_SERVER=DataSyncATask$192.168.56.1$4354D7079D6144AAB33F650B6FF7E532$0000000003:REQ_SERVER=null:DEAL_PARAMETER=
17:01:07.658 [DataSyncATask-2-HeartBeat] DEBUG c.t.p.s.t.TBScheduleManagerStatic - DataSyncATask$192.168.56.1$FCC86240694749FD9CE84DB791448D93$0000000001:不是負責任務分配的Leader,直接返回
17:01:07.882 [DataSyncATask-3-HeartBeat] DEBUG c.t.p.s.t.TBScheduleManagerStatic - DataSyncATask$192.168.56.1$D4D821EBFCFA47C1865A7E42278F45A7$0000000002:不是負責任務分配的Leader,直接返回
17:01:07.882 [DataSyncATask-4-HeartBeat] DEBUG c.t.p.s.t.TBScheduleManagerStatic - DataSyncATask$192.168.56.1$4354D7079D6144AAB33F650B6FF7E532$0000000003:不是負責任務分配的Leader,直接返回
17:01:09.441 [DataSyncATask-0-HeartBeat] DEBUG c.t.p.s.zk.ScheduleDataManager4ZK - DataSyncATask$192.168.56.1$D3CED88695434526B8C9220FFCD9B584$0000000000:開始重新分配任務......
17:01:09.446 [DataSyncATask-0-HeartBeat] DEBUG c.t.p.s.zk.ScheduleDataManager4ZK - 
TASK_TYPE=DataSyncATask:TASK_ITEM=1:CUR_SERVER=DataSyncATask$192.168.56.1$D3CED88695434526B8C9220FFCD9B584$0000000000:REQ_SERVER=null:DEAL_PARAMETER=
TASK_TYPE=DataSyncATask:TASK_ITEM=2:CUR_SERVER=DataSyncATask$192.168.56.1$D3CED88695434526B8C9220FFCD9B584$0000000000:REQ_SERVER=null:DEAL_PARAMETER=
TASK_TYPE=DataSyncATask:TASK_ITEM=3:CUR_SERVER=DataSyncATask$192.168.56.1$FCC86240694749FD9CE84DB791448D93$0000000001:REQ_SERVER=null:DEAL_PARAMETER=
TASK_TYPE=DataSyncATask:TASK_ITEM=4:CUR_SERVER=DataSyncATask$192.168.56.1$FCC86240694749FD9CE84DB791448D93$0000000001:REQ_SERVER=null:DEAL_PARAMETER=
TASK_TYPE=DataSyncATask:TASK_ITEM=5:CUR_SERVER=DataSyncATask$192.168.56.1$D4D821EBFCFA47C1865A7E42278F45A7$0000000002:REQ_SERVER=null:DEAL_PARAMETER=
TASK_TYPE=DataSyncATask:TASK_ITEM=6:CUR_SERVER=DataSyncATask$192.168.56.1$D4D821EBFCFA47C1865A7E42278F45A7$0000000002:REQ_SERVER=null:DEAL_PARAMETER=
TASK_TYPE=DataSyncATask:TASK_ITEM=7:CUR_SERVER=DataSyncATask$192.168.56.1$4354D7079D6144AAB33F650B6FF7E532$0000000003:REQ_SERVER=null:DEAL_PARAMETER=
TASK_TYPE=DataSyncATask:TASK_ITEM=8:CUR_SERVER=DataSyncATask$192.168.56.1$4354D7079D6144AAB33F650B6FF7E532$0000000003:REQ_SERVER=null:DEAL_PARAMETER=

  

     

5、至此同步任務配置完成,並且實現了模擬場景的要求。

 

八、以上也只是對TbSchedule的初步認識,更多高級應用仍然在探索中,歡迎交流。

 

九、向開源工作者和組織致敬,@xuannan  @kongxuan,感謝對開源事業作出的任何貢獻。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章