package com.vip.saturn.job.basic;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.PropertyPlaceholderHelper;
import com.google.common.base.Strings;
import com.vip.saturn.job.SaturnJobReturn;
import com.vip.saturn.job.SaturnSystemErrorGroup;
import com.vip.saturn.job.SaturnSystemReturnCode;
import com.vip.saturn.job.exception.SaturnJobException;
import com.vip.saturn.job.internal.statistics.ProcessCountStatistics;
/**
* Saturn抽象父类
* @author linzhaoming
*/
public abstract class AbstractSaturnJob extends AbstractElasticJob {
private static Logger log = LoggerFactory.getLogger(AbstractSaturnJob.class);
protected static PropertyPlaceholderHelper placeHolderHelper = new PropertyPlaceholderHelper("{", "}");
@Override
protected final void executeJob(final JobExecutionMultipleShardingContext shardingContext) {
if (! (shardingContext instanceof SaturnExecutionContext)) {
log.error("[{}] msg=!!! The context must be instance of SaturnJobExecutionContext !!!", jobName);
return;
}
long start = System.currentTimeMillis();
SaturnExecutionContext saturnContext = (SaturnExecutionContext) shardingContext;
saturnContext.setSaturnJob(true);
//begin
Map<Integer, SaturnJobReturn> retMap = new HashMap<Integer, SaturnJobReturn>();
// shardingItemParameters为参数表解析出来的Key/Value值
Map<Integer, String> shardingItemParameters = shardingContext.getShardingItemParameters();
// items为需要处理的作业分片
List<Integer> items = shardingContext.getShardingItems();
log.info("[{}] msg=Job {} handle items: {}", jobName, jobName, items);
for (Integer item : items) {
// 兼容配置错误,如配置3个分片, 参数表配置为0=*, 2=*, 则1分片不会执行
if (!shardingItemParameters.containsKey(item)) {
log.error("The {} item's parameter is not valid, will not execute the business code, please check shardingItemParameters", item);
SaturnJobReturn errRet = new SaturnJobReturn(SaturnSystemReturnCode.SYSTEM_FAIL,
"Config of parameter is not valid, check shardingItemParameters", SaturnSystemErrorGroup.FAIL);
retMap.put(item, errRet);
}
}
Map<Integer, SaturnJobReturn> handleJobMap = handleJob(saturnContext);
if (handleJobMap != null) {
retMap.putAll(handleJobMap);
}
//end
// 汇总修改
if (retMap.size() > 0) {
for (int item : saturnContext.getShardingItems()) {
updateExecuteResult(retMap.get(item), saturnContext, item);
}
}
long end = System.currentTimeMillis();
log.info("[{}] msg={} finished, totalCost={}, return={}", jobName, jobName, (end - start), retMap);
}
protected void updateExecuteResult(SaturnJobReturn saturnJobReturn, SaturnExecutionContext saturnContext, int item) {
int successCount = 0;
int errorCount = 0;
SaturnJobReturn jobReturn = saturnJobReturn;
if (jobReturn == null) {
jobReturn = new SaturnJobReturn(SaturnSystemReturnCode.SYSTEM_FAIL, "Can not find the corresponding SaturnJobReturn", SaturnSystemErrorGroup.FAIL);
errorCount++;
} else {
if (SaturnSystemReturnCode.JOB_NO_COUNT != jobReturn.getReturnCode()) {
int errorGroup = jobReturn.getErrorGroup();
if (errorGroup == SaturnSystemErrorGroup.SUCCESS) {
successCount++;
} else if (errorGroup == SaturnSystemErrorGroup.TIMEOUT) {
errorCount++;
onTimeout(item);
} else {
errorCount++;
}
}
}
// 为了展现分片处理失败的状态
saturnContext.getShardingItemResults().put(item, jobReturn);
//执行次数加1
ProcessCountStatistics.increaseTotalCountDelta(executorName, jobName);
// 只要有出错和失败的分片,就认为是处理失败; 否则认为处理成功
if (errorCount == 0 && successCount >= 0) {
ProcessCountStatistics.incrementProcessSuccessCount(executorName, jobName, successCount);
} else {
ProcessCountStatistics.increaseErrorCountDelta(executorName, jobName);
ProcessCountStatistics.incrementProcessFailureCount(executorName, jobName, errorCount);
}
}
public Properties parseKV(String path) {
if (Strings.isNullOrEmpty(path)) {
return null;
}
Properties kv = new Properties();
String[] paths = path.split(",");
if (paths != null && paths.length > 0) {
for (String p : paths) {
String[] tmps = p.split("=");
if (tmps != null && tmps.length == 2) {
kv.put(tmps[0].trim(), tmps[1].trim());
} else {
log.error("msg=Param is not valid {}", p);
}
}
}
return kv;
}
/**
* 获取作业超时时间(秒)
*/
public int getTimeoutSeconds() {
return getConfigService().getTimeoutSeconds();
}
/**
* 获取替换后的作业分片执行值
* @param jobParameter 作业参数
* @param jobValue 作业value
* @return 替换后的值
*/
protected String getRealItemValue(String jobParameter, String jobValue) {
// 处理自定义参数
Properties kvProp = parseKV(jobParameter);
int kvSize = kvProp != null ? kvProp.size() : 0;
final String itemVal; // 作业分片的对应值
if (kvSize > 0) {
// 有自定义参数, 解析完替换
itemVal = placeHolderHelper.replacePlaceholders(jobValue, kvProp);
} else {
itemVal = jobValue;
}
return itemVal.replaceAll("!!", "\"").replaceAll("@@", "=").replaceAll("##", ",");
}
public String logBusinessExceptionIfNecessary(String jobName, Exception e) {
String message = null;
if(e instanceof ReflectiveOperationException) {
Throwable cause = e.getCause();
if(cause != null) {
message = cause.getMessage();
log.error(String.format(SaturnConstant.ERROR_LOG_FORMAT, jobName, message), e);
}
}
log.error(String.format(SaturnConstant.ERROR_LOG_FORMAT, jobName, e.getMessage()), e);
if(message == null) {
message = e.getMessage();
}
return message;
}
/**
* 更改作业cron表达式,请确认作业名是正确的。
* @param jobName 作业名
* @param cron cron表达式
* @param customContext 自定义上下文
* @throws SaturnJobException 可能抛的异常有:type为0,表示cron表达式无效;type为1,表示作业名在这个namespace下不存在;type为3,表示customContext内容超出1M;type为4,表示作业名有误,不能为$SaturnExecutors。
*/
public void updateJobCron(String jobName, String cron, Map<String, String> customContext) throws SaturnJobException {
this.getConfigService().updateJobCron(jobName, cron, customContext);
}
/**
* 实际处理逻辑
* @param shardingContext 上下文
* @return 每个分片返回一个SaturnJobReturn. 若为null,表示执行失败
*/
protected abstract Map<Integer, SaturnJobReturn> handleJob(SaturnExecutionContext shardingContext);
public abstract SaturnJobReturn doExecution(String jobName, Integer key, String value,
SaturnExecutionContext shardingContext, JavaShardingItemCallable callable) throws Throwable;
}