路由策略新增 "忙碌转移" 模式:按照顺序依次进行空闲检测,第一个空闲检测成功的机器选定为目标执行器并发起调度;

This commit is contained in:
xuxueli 2017-06-27 22:02:46 +08:00
parent 1ad6950ed7
commit 7e35088764
6 changed files with 98 additions and 65 deletions

View File

@ -857,6 +857,7 @@ Tips: 历史版本(V1.3.x)目前已经Release至稳定版本, 进入维护阶段
- 1、任务Cron更新逻辑优化改为rescheduleJob同时防止cron重复设置 - 1、任务Cron更新逻辑优化改为rescheduleJob同时防止cron重复设置
- 2、优化API回调服务失败状态码优化方便问题排查 - 2、优化API回调服务失败状态码优化方便问题排查
- 3、XxlJobLogger的日志多参数支持 - 3、XxlJobLogger的日志多参数支持
- 4、路由策略新增 "忙碌转移" 模式:按照顺序依次进行空闲检测,第一个空闲检测成功的机器选定为目标执行器并发起调度;
#### TODO LIST #### TODO LIST
- 1、任务权限管理执行器为粒度分配权限核心操作校验权限 - 1、任务权限管理执行器为粒度分配权限核心操作校验权限
@ -868,7 +869,7 @@ Tips: 历史版本(V1.3.x)目前已经Release至稳定版本, 进入维护阶段
- 7、调度任务优先级 - 7、调度任务优先级
- 8、移除quartz依赖重写调度模块新增或恢复任务时将下次执行记录插入delayqueue调度中心集群竞争分布式锁成功节点批量加载到期delayqueue数据批量执行。 - 8、移除quartz依赖重写调度模块新增或恢复任务时将下次执行记录插入delayqueue调度中心集群竞争分布式锁成功节点批量加载到期delayqueue数据批量执行。
- 9、任务线程轮空30次后自动销毁降低低频任务的无效线程消耗。 - 9、任务线程轮空30次后自动销毁降低低频任务的无效线程消耗。
- 10、路由策略新增 "忙碌转移" 模式,发现执行器运行中,主动转移下一个执行器调度任务;
## 七、其他 ## 七、其他

View File

@ -114,78 +114,84 @@ public class RemoteHttpJobBean extends QuartzJobBean {
return new ReturnT<String>(ReturnT.FAIL_CODE, triggerSb.toString()); return new ReturnT<String>(ReturnT.FAIL_CODE, triggerSb.toString());
} }
// executor route strategy
ExecutorRouteStrategyEnum executorRouteStrategyEnum = ExecutorRouteStrategyEnum.match(jobInfo.getExecutorRouteStrategy(), null);
if (executorRouteStrategyEnum == null) {
triggerSb.append("<br>----------------------<br>").append("调度失败:").append("执行器路由策略为空");
return new ReturnT<String>(ReturnT.FAIL_CODE, triggerSb.toString());
}
triggerSb.append("<br>路由策略:").append(executorRouteStrategyEnum.name() + "-" + executorRouteStrategyEnum.getTitle());
// trigger remote executor // trigger remote executor
if (addressList.size() == 1) { if (executorRouteStrategyEnum == ExecutorRouteStrategyEnum.FAILOVER) {
String address = addressList.get(0); for (String address : addressList) {
// beat
ReturnT<String> beatResult = null;
try {
ExecutorBiz executorBiz = (ExecutorBiz) new NetComClientProxy(ExecutorBiz.class, address).getObject();
beatResult = executorBiz.beat();
} catch (Exception e) {
logger.error("", e);
beatResult = new ReturnT<String>(ReturnT.FAIL_CODE, ""+e );
}
triggerSb.append("<br>----------------------<br>")
.append("心跳检测:")
.append("<br>address").append(address)
.append("<br>code").append(beatResult.getCode())
.append("<br>msg").append(beatResult.getMsg());
// beat success
if (beatResult.getCode() == ReturnT.SUCCESS_CODE) {
jobLog.setExecutorAddress(address);
ReturnT<String> runResult = runExecutor(triggerParam, address);
triggerSb.append("<br>----------------------<br>").append(runResult.getMsg());
return new ReturnT<String>(runResult.getCode(), triggerSb.toString());
}
}
return new ReturnT<String>(ReturnT.FAIL_CODE, triggerSb.toString());
} else if (executorRouteStrategyEnum == ExecutorRouteStrategyEnum.BUSYOVER) {
for (String address : addressList) {
// beat
ReturnT<String> idleBeatResult = null;
try {
ExecutorBiz executorBiz = (ExecutorBiz) new NetComClientProxy(ExecutorBiz.class, address).getObject();
idleBeatResult = executorBiz.idleBeat(triggerParam.getJobId());
} catch (Exception e) {
logger.error("", e);
idleBeatResult = new ReturnT<String>(ReturnT.FAIL_CODE, ""+e );
}
triggerSb.append("<br>----------------------<br>")
.append("空闲检测:")
.append("<br>address").append(address)
.append("<br>code").append(idleBeatResult.getCode())
.append("<br>msg").append(idleBeatResult.getMsg());
// beat success
if (idleBeatResult.getCode() == ReturnT.SUCCESS_CODE) {
jobLog.setExecutorAddress(address);
ReturnT<String> runResult = runExecutor(triggerParam, address);
triggerSb.append("<br>----------------------<br>").append(runResult.getMsg());
return new ReturnT<String>(runResult.getCode(), triggerSb.toString());
}
}
return new ReturnT<String>(ReturnT.FAIL_CODE, triggerSb.toString());
} else {
// get address
String address = executorRouteStrategyEnum.getRouter().route(jobInfo.getId(), addressList);
jobLog.setExecutorAddress(address); jobLog.setExecutorAddress(address);
// run
ReturnT<String> runResult = runExecutor(triggerParam, address); ReturnT<String> runResult = runExecutor(triggerParam, address);
triggerSb.append("<br>----------------------<br>").append(runResult.getMsg()); triggerSb.append("<br>----------------------<br>").append(runResult.getMsg());
return new ReturnT<String>(runResult.getCode(), triggerSb.toString()); return new ReturnT<String>(runResult.getCode(), triggerSb.toString());
} else {
// executor route strategy
ExecutorRouteStrategyEnum executorRouteStrategyEnum = ExecutorRouteStrategyEnum.match(jobInfo.getExecutorRouteStrategy(), null);
triggerSb.append("<br>路由策略:").append(executorRouteStrategyEnum!=null?(executorRouteStrategyEnum.name() + "-" + executorRouteStrategyEnum.getTitle()):null);
if (executorRouteStrategyEnum == null) {
triggerSb.append("<br>----------------------<br>").append("调度失败:").append("执行器路由策略为空");
return new ReturnT<String>(ReturnT.FAIL_CODE, triggerSb.toString());
}
if (executorRouteStrategyEnum != ExecutorRouteStrategyEnum.FAILOVER) {
// get address
String address = executorRouteStrategyEnum.getRouter().route(jobInfo.getId(), addressList);
jobLog.setExecutorAddress(address);
// run
ReturnT<String> runResult = runExecutor(triggerParam, address);
triggerSb.append("<br>----------------------<br>").append(runResult.getMsg());
return new ReturnT<String>(runResult.getCode(), triggerSb.toString());
} else {
for (String address : addressList) {
// beat
ReturnT<String> beatResult = beatExecutor(address);
triggerSb.append("<br>----------------------<br>").append(beatResult.getMsg());
if (beatResult.getCode() == ReturnT.SUCCESS_CODE) {
jobLog.setExecutorAddress(address);
ReturnT<String> runResult = runExecutor(triggerParam, address);
triggerSb.append("<br>----------------------<br>").append(runResult.getMsg());
return new ReturnT<String>(runResult.getCode(), triggerSb.toString());
}
}
return new ReturnT<String>(ReturnT.FAIL_CODE, triggerSb.toString());
}
} }
} }
/**
* run executor
* @param address
* @return
*/
public ReturnT<String> beatExecutor(String address){
ReturnT<String> beatResult = null;
try {
ExecutorBiz executorBiz = (ExecutorBiz) new NetComClientProxy(ExecutorBiz.class, address).getObject();
beatResult = executorBiz.beat();
} catch (Exception e) {
logger.error("", e);
beatResult = new ReturnT<String>(ReturnT.FAIL_CODE, ""+e );
}
StringBuffer sb = new StringBuffer("心跳检测:");
sb.append("<br>address").append(address);
sb.append("<br>code").append(beatResult.getCode());
sb.append("<br>msg").append(beatResult.getMsg());
beatResult.setMsg(sb.toString());
return beatResult;
}
/** /**
* run executor * run executor
* @param triggerParam * @param triggerParam

View File

@ -14,7 +14,8 @@ public enum ExecutorRouteStrategyEnum {
CONSISTENT_HASH("一致性HASH", new ExecutorRouteConsistentHash()), CONSISTENT_HASH("一致性HASH", new ExecutorRouteConsistentHash()),
LEAST_FREQUENTLY_USED("最不经常使用", new ExecutorRouteLFU()), LEAST_FREQUENTLY_USED("最不经常使用", new ExecutorRouteLFU()),
LEAST_RECENTLY_USED("最近最久未使用", new ExecutorRouteLRU()), LEAST_RECENTLY_USED("最近最久未使用", new ExecutorRouteLRU()),
FAILOVER("故障转移", null); FAILOVER("故障转移", null),
BUSYOVER("忙碌转移", null);
ExecutorRouteStrategyEnum(String title, ExecutorRouter router) { ExecutorRouteStrategyEnum(String title, ExecutorRouter router) {
this.title = title; this.title = title;

View File

@ -15,6 +15,14 @@ public interface ExecutorBiz {
*/ */
public ReturnT<String> beat(); public ReturnT<String> beat();
/**
* idle beat
*
* @param jobId
* @return
*/
public ReturnT<String> idleBeat(int jobId);
/** /**
* kill * kill
* @param jobId * @param jobId

View File

@ -29,6 +29,22 @@ public class ExecutorBizImpl implements ExecutorBiz {
return ReturnT.SUCCESS; return ReturnT.SUCCESS;
} }
@Override
public ReturnT<String> idleBeat(int jobId) {
// isRunningOrHasQueue
boolean isRunningOrHasQueue = false;
JobThread jobThread = XxlJobExecutor.loadJobThread(jobId);
if (jobThread != null && jobThread.isRunningOrHasQueue()) {
isRunningOrHasQueue = true;
}
if (isRunningOrHasQueue) {
return new ReturnT<String>(ReturnT.FAIL_CODE, "job thread is running or has trigger queue.");
}
return ReturnT.SUCCESS;
}
@Override @Override
public ReturnT<String> kill(int jobId) { public ReturnT<String> kill(int jobId) {
// kill handlerThread, and create new one // kill handlerThread, and create new one

View File

@ -46,7 +46,8 @@ public class ExecutorRegistryThread extends Thread {
try { try {
RegistryParam registryParam = new RegistryParam(RegistryConfig.RegistType.EXECUTOR.name(), appName, executorAddress); RegistryParam registryParam = new RegistryParam(RegistryConfig.RegistType.EXECUTOR.name(), appName, executorAddress);
ReturnT<String> registryResult = AdminApiUtil.callApiFailover(AdminApiUtil.REGISTRY, registryParam); ReturnT<String> registryResult = AdminApiUtil.callApiFailover(AdminApiUtil.REGISTRY, registryParam);
logger.info(">>>>>>>>>>> xxl-job registry, RegistryParam:{}, registryResult:{}", new Object[]{registryParam.toString(), registryResult.toString()}); logger.info(">>>>>>>>>>> xxl-job Executor registry {}, RegistryParam:{}, registryResult:{}",
new Object[]{(registryResult.getCode()==ReturnT.SUCCESS_CODE?"success":"fail"), registryParam.toString(), registryResult.toString()});
} catch (Exception e) { } catch (Exception e) {
logger.error(">>>>>>>>>>> xxl-job ExecutorRegistryThread Exception:", e); logger.error(">>>>>>>>>>> xxl-job ExecutorRegistryThread Exception:", e);
} }