package org.act.tstream.schedule;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.act.tstream.callback.RunnableCallback;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.apache.thrift7.TException;
import backtype.storm.Config;
import backtype.storm.utils.Utils;
import org.act.tstream.client.ConfigExtension;
import org.act.tstream.cluster.Cluster;
import org.act.tstream.cluster.StormClusterState;
import org.act.tstream.cluster.StormConfig;
import org.act.tstream.daemon.nimbus.NimbusData;
import org.act.tstream.task.Assignment;
import org.act.tstream.utils.JStormServerUtils;
import org.act.tstream.utils.JStormUtils;
import org.act.tstream.utils.NetWorkUtils;
import org.act.tstream.utils.PathUtils;
public class FollowerRunnable implements Runnable {
private static final Logger LOG = Logger.getLogger(FollowerRunnable.class);
private NimbusData data;
private int sleepTime;
private volatile boolean state = true;
private RunnableCallback callback;
private final String hostPort;
@SuppressWarnings("unchecked")
public FollowerRunnable(final NimbusData data, int sleepTime) {
this.data = data;
this.sleepTime = sleepTime;
if (!ConfigExtension.isNimbusUseIp(data.getConf())) {
this.hostPort = NetWorkUtils.hostname()
+ ":"
+ String.valueOf(Utils.getInt(data.getConf().get(
Config.NIMBUS_THRIFT_PORT)));
} else {
this.hostPort = NetWorkUtils.ip()
+ ":"
+ String.valueOf(Utils.getInt(data.getConf().get(
Config.NIMBUS_THRIFT_PORT)));
}
try {
this.tryToBeLeader(data.getConf());
} catch (Exception e1) {
// TODO Auto-generated catch block
LOG.error("try to be leader error.", e1);
throw new RuntimeException(e1);
}
try {
data.getStormClusterState().register_nimbus_host(hostPort);
} catch (Exception e) {
LOG.error("register nimbus host fail!", e);
throw new RuntimeException();
}
callback = new RunnableCallback() {
@Override
public void run() {
if (!data.isLeader())
check();
}
};
}
@Override
public void run() {
// TODO Auto-generated method stub
LOG.info("Follower Thread starts!");
while (state) {
try {
Thread.sleep(sleepTime);
if (data.isLeader()) {
data.getStormClusterState()
.unregister_nimbus_host(hostPort);
checkOwnMaster();
continue;
}
if (!data.getStormClusterState().leader_existed()) {
this.tryToBeLeader(data.getConf());
continue;
}
check();
data.getStormClusterState().update_follower_hb(hostPort,
data.uptime());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
continue;
} catch (Exception e) {
if (state) {
LOG.error("Unknow exception ", e);
}
}
}
LOG.info("Follower Thread has closed!");
}
public void clean() {
state = false;
}
private synchronized void check() {
StormClusterState clusterState = data.getStormClusterState();
try {
String master_stormdist_root = StormConfig.masterStormdistRoot(data
.getConf());
List<String> code_ids = PathUtils
.read_dir_contents(master_stormdist_root);
List<String> assignments_ids = clusterState.assignments(callback);
List<String> done_ids = new ArrayList<String>();
for (String id : code_ids) {
if (assignments_ids.contains(id)) {
done_ids.add(id);
}
}
for (String id : done_ids) {
assignments_ids.remove(id);
code_ids.remove(id);
}
for (String topologyId : code_ids) {
deleteLocalTopology(topologyId);
}
for (String id : assignments_ids) {
Assignment assignment = clusterState.assignment_info(id, null);
downloadCodeFromMaster(assignment, id);
}
} catch (IOException e) {
// TODO Auto-generated catch block
LOG.error("Get stormdist dir error!", e);
return;
} catch (Exception e) {
// TODO Auto-generated catch block
LOG.error("Check error!", e);
return;
}
}
private void deleteLocalTopology(String topologyId) throws IOException {
String dir_to_delete = StormConfig.masterStormdistRoot(data.getConf(),
topologyId);
try {
PathUtils.rmr(dir_to_delete);
LOG.info("delete:" + dir_to_delete + "successfully!");
} catch (IOException e) {
// TODO Auto-generated catch block
LOG.error("delete:" + dir_to_delete + "fail!", e);
}
}
private void downloadCodeFromMaster(Assignment assignment, String topologyId)
throws IOException, TException {
try {
String localRoot = StormConfig.masterStormdistRoot(data.getConf(),
topologyId);
String tmpDir = StormConfig.masterInbox(data.getConf()) + "/"
+ UUID.randomUUID().toString();
String masterCodeDir = assignment.getMasterCodeDir();
JStormServerUtils.downloadCodeFromMaster(data.getConf(), tmpDir,
masterCodeDir, topologyId, false);
FileUtils.moveDirectory(new File(tmpDir), new File(localRoot));
} catch (TException e) {
// TODO Auto-generated catch block
LOG.error(e + " downloadStormCode failed " + "topologyId:"
+ topologyId + "masterCodeDir:"
+ assignment.getMasterCodeDir());
throw e;
}
LOG.info("Finished downloading code for topology id " + topologyId
+ " from " + assignment.getMasterCodeDir());
}
private void tryToBeLeader(final Map conf) throws Exception {
RunnableCallback masterCallback = new RunnableCallback() {
@Override
public void run() {
try {
tryToBeLeader(conf);
} catch (Exception e) {
LOG.error("To be master error", e);
JStormUtils.halt_process(30,
"Cant't to be master" + e.getMessage());
}
}
};
data.setLeader(data.getStormClusterState().try_to_be_leader(
Cluster.MASTER_SUBTREE, hostPort, masterCallback));
}
/**
* Check whether current node is master or not
*
* @throws Exception
*/
private void checkOwnMaster() throws Exception {
int retry_times = 10;
StormClusterState zkClient = data.getStormClusterState();
for (int i = 0; i < retry_times; i++, JStormUtils.sleepMs(sleepTime)) {
if (zkClient.leader_existed() == false) {
continue;
}
String zkHost = zkClient.get_leader_host();
if (hostPort.equals(zkHost) == true) {
// current process own master
return;
}
LOG.warn("Current Nimbus has start thrift, but fail to own zk master :"
+ zkHost);
}
// current process doesn't own master
String err = "Current Nimubs fail to own nimbus_master, should halt process";
LOG.error(err);
JStormUtils.halt_process(0, err);
}
}