package com.yirendai.infra.cicada.cluster;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.yirendai.infra.cicada.configure.CicadaWebProps;
import com.yirendai.infra.cicada.entity.Fragment;
import com.yirendai.infra.cicada.entity.Job;
import com.yirendai.infra.cicada.util.ZookeeperUtil;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.LinkedList;
import java.util.List;
@Slf4j
@Component
public class JobDispatcher {
private static final String CICADA_JOBNODE_DEFAULT_CONTENT = "jobNode";
private static final long TASK_INTERVAL_MILLIS = 300L * 1000;
@Autowired
private CicadaWebProps props;
@Autowired
private CuratorFramework zkClient;
@Setter
private String jobNodePath;
@Scheduled(fixedRate = TASK_INTERVAL_MILLIS)
public void dispatch() {
// 非主节点不参与任务分发
if (!ClusterLeaderManager.isLeader()) {
return;
}
final Job job = new Job();
final List<String> instances = listNodes();
// 设置任务分片信息
final int size = instances.size();
for (int i = 0; i < size; ++i) {
final Fragment fragment = new Fragment();
fragment.setStart(props.getJobSlotRange() * i / size);
fragment.setEnd(props.getJobSlotRange() * (i + 1) / size);
job.addJobFragment(instances.get(i), fragment);
}
// 设置任务其他属性
long jobId = 1;
final long endTimestamp = System.currentTimeMillis();
long startTimestamp = endTimestamp - TASK_INTERVAL_MILLIS;
final Job lastJob = getJob();
if (lastJob != null) {
startTimestamp = lastJob.getEndTimestamp();
lastJob.incrJodId();
jobId = lastJob.getId();
}
job.setId(jobId);
job.setStartTimestamp(startTimestamp);
job.setEndTimestamp(endTimestamp);
// 将任务信息更新到zookeeper
putJob(job);
}
public void putJob(final Job job) {
final String jobStr = JSON.toJSONString(job);
final String jobNodePath = props.getJobNodePath();
try {
if (!ZookeeperUtil.exists(zkClient, props.getJobNodePath())) {
ZookeeperUtil.create(zkClient, props.getJobNodePath(), jobStr);
return;
}
zkClient.setData().forPath(jobNodePath, jobStr.getBytes());
} catch (Exception ex) {
log.error("failed put job to zookeeper, error: {}", ex);
// add throw new RuntimeException code here!
}
}
public Job getJob() {
final String jobNodePath = props.getJobNodePath();
Job job = null;
String lastJobStr = null;
try {
final byte[] lastJobByteArr = zkClient.getData().forPath(jobNodePath);
lastJobStr = new String(lastJobByteArr);
if (!lastJobStr.equals(CICADA_JOBNODE_DEFAULT_CONTENT)) {
job = JSON.parseObject(lastJobStr, Job.class);
}
} catch (JSONException ex) {
log.error("failed parse jsonStr to object, json: {}, error: {}", lastJobStr, ex);
} catch (Exception ex) {
log.error("failed get job info, path: {}, error: {}", jobNodePath, ex);
}
return job;
}
private List<String> listNodes() {
List<String> nodes = null;
final String instancesNodePath = props.getInstancesNodePath();
try {
nodes = zkClient.getChildren().forPath(instancesNodePath);
} catch (Exception ex) {
log.error("failed get instance list of {}, error {}", instancesNodePath, ex);
// 如果捕获到异常,只在节点列表里添加自身
nodes = new LinkedList<String>();
nodes.add(ClusterNodeRegister.getNodeLabel());
}
return nodes;
}
}