根据上文的启动过程,找到了线程组的实现。com.taobao.pamirs.schedule.taskmanager.TBScheduleManager
/**
* 1、任务调度分配器的目标: 让所有的任务不重复,不遗漏的被快速处理。
* 2、一个Manager只管理一种任务类型的一组工作线程。
* 3、在一个JVM里面可能存在多个处理相同任务类型的Manager,也可能存在处理不同任务类型的Manager。
* 4、在不同的JVM里面可以存在处理相同任务的Manager
* 5、调度的Manager可以动态的随意增加和停止
*
* 主要的职责:
* 1、定时向集中的数据配置中心更新当前调度服务器的心跳状态
* 2、向数据配置中心获取所有服务器的状态来重新计算任务的分配。这么做的目标是避免集中任务调度中心的单点问题。
* 3、在每个批次数据处理完毕后,检查是否有其它处理服务器申请自己把持的任务队列,如果有,则释放给相关处理服务器。
*
* 其它:
* 如果当前服务器在处理当前任务的时候超时,需要清除当前队列,并释放已经把持的任务。并向控制主动中心报警。
*
* @author xuannan
*
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
abstract class TBScheduleManager implements IStrategyTask {
//.......//
}
这个类的构造方法是这样的
TBScheduleManager(TBScheduleManagerFactory aFactory,String baseTaskType,String ownSign ,IScheduleDataManager aScheduleCenter) throws Exception{
//.......//
}
这里发现有两个老朋友,aFactory就是那个调度服务器对象,baseTaskType是设置的平台类型,aScheduleCenter调度配置中心客户端接口,那么ownSign是什么?
以下取自官方文档:
OwnSign环境区域
是对运行环境的划分,进行调度任务和数据隔离。例如:开发环境、测试环境、预发环境、生产环境。
不同的开发人员需要进行数据隔离也可以用OwnSign来实现,避免不同人员的数据冲突。缺省配置的环境区域OwnSign=’BASE’。
例如:TaskType=’DataDeal’,配置的队列是0、1、2、3、4、5、6、7、8、9。缺省的OwnSign=’BASE’。
此时如果再启动一个测试环境,则Schedule会动态生成一个TaskType=’DataDeal-Test’的任务类型,环境会作为一个变量传递给业务接口,
由业务接口的实现类,在读取数据和处理数据的时候进行确定。业务系统一种典型的做法就是在数据表中增加一个OWN_SIGN字段。
在创建数据的时候根据运行环境填入对应的环境名称,在Schedule中就可以环境的区分了。
com.taobao.pamirs.schedule.taskmanager.IScheduleDataManager
这个接口要好好看下,定义的方法有点多,但是一眼看下来更多的方法是为控制台页面提供服务了,例如创建任务。诸如任务和任务相等信息的维护和查询都在这个接口中定义。
那么TBScheduleManager 这个构造函数就用到了一个,继续打开构造函数看
TBScheduleManager(TBScheduleManagerFactory aFactory,String baseTaskType,String ownSign ,IScheduleDataManager aScheduleCenter) throws Exception{
this.factory = aFactory;
//private static int nextSerialNumber = 0; 生成用户标志不同的线程序号,用于区分不同的线程组
this.currentSerialNumber = serialNumber();
this.scheduleCenter = aScheduleCenter;
//按照任务类型加载调度任务信息
this.taskTypeInfo = this.scheduleCenter.loadTaskTypeBaseInfo(baseTaskType);
log.info("create TBScheduleManager for taskType:"+baseTaskType);
//清除已经过期1天的TASK,OWN_SIGN的组合。超过一天没有活动server的视为过期
//为什么要清除?如果任务确实能跑一天怎么办
this.scheduleCenter.clearExpireTaskTypeRunningInfo(baseTaskType,ScheduleUtil.getLocalIP() + "清除过期OWN_SIGN信息",this.taskTypeInfo.getExpireOwnSignInterval());
//通过调度服务器提供的方法获得对应的bean
Object dealBean = aFactory.getBean(this.taskTypeInfo.getDealBeanName());
if (dealBean == null) {
throw new Exception( "SpringBean " + this.taskTypeInfo.getDealBeanName() + " 不存在");
}
//如果没有实现调度器对外的基础接口,肯定不能用啊
if (dealBean instanceof IScheduleTaskDeal == false) {
throw new Exception( "SpringBean " + this.taskTypeInfo.getDealBeanName() + " 没有实现 IScheduleTaskDeal接口");
}
this.taskDealBean = (IScheduleTaskDeal)dealBean;
//任务的配置校验,为什么要大于5倍 出发点是什么
if(this.taskTypeInfo.getJudgeDeadInterval() < this.taskTypeInfo.getHeartBeatRate() * 5){
throw new Exception("数据配置存在问题,死亡的时间间隔,至少要大于心跳线程的5倍。当前配置数据:JudgeDeadInterval = "
+ this.taskTypeInfo.getJudgeDeadInterval()
+ ",HeartBeatRate = " + this.taskTypeInfo.getHeartBeatRate());
}
//这个currenScheduleServer是类"com.taobao.pamirs.schedule.taskmanager.ScheduleServer"的实例,存储了当前调度服务的信息
this.currenScheduleServer = ScheduleServer.createScheduleServer(this.scheduleCenter,baseTaskType,ownSign,this.taskTypeInfo.getThreadNumber());
this.currenScheduleServer.setManagerFactoryUUID(this.factory.getUuid());
//向调度中心客户端注册调度服务信息
scheduleCenter.registerScheduleServer(this.currenScheduleServer);
this.mBeanName = "pamirs:name=" + "schedule.ServerMananger." +this.currenScheduleServer.getUuid();
//又启动了一个定时任务,看这名字就知道是心跳
this.heartBeatTimer = new Timer(this.currenScheduleServer.getTaskType() +"-" + this.currentSerialNumber +"-HeartBeat");
this.heartBeatTimer.schedule(new HeartBeatTimerTask(this),
new java.util.Date(System.currentTimeMillis() + 500),
this.taskTypeInfo.getHeartBeatRate());
//对象创建时需要做的初始化工作,模版方法。
initial();
}
目前能观察到的这个构造函数流程
1.按照任务名称加载任务配置
2.清除过期OWN_SIGN信息
3.检查任务配置是否正确
4.将调度服务信息注册到调度中心客户端
5.其他初始化操作
记录一下tbschedule对任务配置的存储方式(控制台页面”任务管理”)
控制台:
zk节点:
节点内容格式化之后
{
"baseTaskType": "commonSyncAdvertiserTask",
"heartBeatRate": 5000,
"judgeDeadInterval": 60000,
"sleepTimeNoData": 500,
"sleepTimeInterval": 0,
"fetchDataNumber": 500,
"executeNumber": 10,
"threadNumber": 5,
"processorType": "SLEEP",
"permitRunStartTime": "0 * * * * ?",
"expireOwnSignInterval": 1,
"dealBeanName": "commonSyncAdvertiserTask",
"taskParameter": "3",
"taskKind": "static",
"taskItems": [
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9"
],
"maxTaskItemsOfOneThreadGroup": 0,
"version": 0,
"sts": "resume"
}
第4步主要包含以下代码行
//这个currenScheduleServer是类"com.taobao.pamirs.schedule.taskmanager.ScheduleServer"的实例,存储了当前调度服务的信息
this.currenScheduleServer = ScheduleServer.createScheduleServer(this.scheduleCenter,baseTaskType,ownSign,this.taskTypeInfo.getThreadNumber());
this.currenScheduleServer.setManagerFactoryUUID(this.factory.getUuid());
//向调度中心客户端注册调度服务信息
scheduleCenter.registerScheduleServer(this.currenScheduleServer);
当前调度服务信息都包含什么,有什么是状态量,向调度中心客户端注册的意图是什么?打开ScheduleServer.createScheduleServer(this.scheduleCenter,baseTaskType,ownSign,this.taskTypeInfo.getThreadNu