package org.act.tstream.task;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.act.tstream.callback.AsyncLoopThread;
import org.act.tstream.callback.RunnableCallback;
import org.apache.log4j.Logger;
import backtype.storm.Config;
import backtype.storm.messaging.IContext;
import backtype.storm.serialization.KryoTupleSerializer;
import backtype.storm.spout.ISpout;
import backtype.storm.task.IBolt;
import backtype.storm.task.TopologyContext;
import backtype.storm.utils.DisruptorQueue;
import backtype.storm.utils.Utils;
import backtype.storm.utils.WorkerClassLoader;
import clojure.lang.Atom;
import org.act.tstream.client.ConfigExtension;
import org.act.tstream.cluster.Common;
import org.act.tstream.cluster.StormClusterState;
import org.act.tstream.cluster.StormConfig;
import org.act.tstream.cluster.StormZkClusterState;
import org.act.tstream.daemon.worker.WorkerData;
import org.act.tstream.daemon.worker.WorkerHaltRunable;
import org.act.tstream.stats.CommonStatsRolling;
import org.act.tstream.task.comm.TaskSendTargets;
import org.act.tstream.task.comm.UnanchoredSend;
import org.act.tstream.task.error.ITaskReportErr;
import org.act.tstream.task.error.TaskReportError;
import org.act.tstream.task.error.TaskReportErrorAndDie;
import org.act.tstream.task.execute.BaseExecutors;
import org.act.tstream.task.execute.BoltExecutors;
import org.act.tstream.task.execute.spout.MultipleThreadSpoutExecutors;
import org.act.tstream.task.execute.spout.SingleThreadSpoutExecutors;
import org.act.tstream.task.execute.spout.SpoutExecutors;
import org.act.tstream.task.group.MkGrouper;
import org.act.tstream.task.heartbeat.TaskHeartbeatRunable;
import org.act.tstream.task.heartbeat.TaskStats;
import org.act.tstream.utils.JStormServerUtils;
import org.act.tstream.utils.JStormUtils;
import com.lmax.disruptor.WaitStrategy;
import com.lmax.disruptor.dsl.ProducerType;
/**
* Task instance
*
* @author yannian/Longda
*
*/
public class Task {
private final static Logger LOG = Logger.getLogger(Task.class);
private Map<Object, Object> stormConf;
private TopologyContext topologyContext;
private TopologyContext userContext;
private String topologyid;
private IContext context;
private TaskTransfer taskTransfer;
private Map<Integer, DisruptorQueue> innerTaskTransfer;
private Map<Integer, DisruptorQueue> deserializeQueues;
private WorkerHaltRunable workHalt;
private Integer taskid;
private String componentid;
private volatile TaskStatus taskStatus;
private Atom openOrPrepareWasCalled;
// running time counter
private UptimeComputer uptime = new UptimeComputer();
private StormClusterState zkCluster;
private Object taskObj;
private CommonStatsRolling taskStats;
private WorkerData workerData;
private String componentType; //"spout" or "bolt"
@SuppressWarnings("rawtypes")
public Task(WorkerData workerData, int taskId) throws Exception {
openOrPrepareWasCalled = new Atom(Boolean.valueOf(false));
this.workerData = workerData;
this.topologyContext = workerData.getContextMaker()
.makeTopologyContext(workerData.getSysTopology(), taskId,
openOrPrepareWasCalled);
this.userContext = workerData.getContextMaker().makeTopologyContext(
workerData.getRawTopology(), taskId, openOrPrepareWasCalled);
this.taskid = taskId;
this.componentid = topologyContext.getThisComponentId();
this.taskStatus = new TaskStatus();
this.taskTransfer = getSendingTransfer(workerData);
this.innerTaskTransfer = workerData.getInnerTaskTransfer();
this.deserializeQueues = workerData.getDeserializeQueues();
this.topologyid = workerData.getTopologyId();
this.context = workerData.getContext();
this.workHalt = workerData.getWorkHalt();
this.zkCluster = new StormZkClusterState(workerData.getZkClusterstate());
this.stormConf = Common.component_conf(workerData.getStormConf(),
topologyContext, componentid);
// get real task object -- spout/bolt/spoutspec
this.taskObj = Common.get_task_object(topologyContext.getRawTopology(),
componentid, WorkerClassLoader.getInstance());
int samplerate = StormConfig.sampling_rate(stormConf);
this.taskStats = new CommonStatsRolling(samplerate);
LOG.info("Loading task " + componentid + ":" + taskid);
}
private void setComponentType() {
if (taskObj instanceof IBolt) {
componentType = "bolt";
} else if (taskObj instanceof ISpout) {
componentType = "spout";
}
}
private TaskSendTargets makeSendTargets() {
String component = topologyContext.getThisComponentId();
// get current task's output
// <Stream_id,<component, Grouping>>
Map<String, Map<String, MkGrouper>> streamComponentGrouper = Common
.outbound_components(topologyContext, workerData);
Map<Integer, String> task2Component = topologyContext
.getTaskToComponent();
Map<String, List<Integer>> component2Tasks = JStormUtils
.reverse_map(task2Component);
return new TaskSendTargets(stormConf, component,
streamComponentGrouper, topologyContext, component2Tasks,
taskStats);
}
private TaskTransfer getSendingTransfer(WorkerData workerData) {
// sending tuple's serializer
KryoTupleSerializer serializer = new KryoTupleSerializer(
workerData.getStormConf(), topologyContext);
String taskName = JStormServerUtils.getName(componentid, taskid);
// Task sending all tuples through this Object
return new TaskTransfer(taskName, serializer, taskStatus, workerData);
}
public TaskSendTargets echoToSystemBolt() {
// send "startup" tuple to system bolt
List<Object> msg = new ArrayList<Object>();
msg.add("startup");
// create task receive object
TaskSendTargets sendTargets = makeSendTargets();
UnanchoredSend.send(topologyContext, sendTargets, taskTransfer,
Common.SYSTEM_STREAM_ID, msg);
return sendTargets;
}
public boolean isSingleThread(Map conf) {
boolean isOnePending = JStormServerUtils.isOnePending(conf);
if (isOnePending == true) {
return true;
}
return ConfigExtension.isSpoutSingleThread(conf);
}
public RunnableCallback mk_executors(DisruptorQueue deserializeQueue,
TaskSendTargets sendTargets, ITaskReportErr report_error) {
if (taskObj instanceof IBolt) {
return new BoltExecutors((IBolt) taskObj, taskTransfer,
innerTaskTransfer, stormConf, deserializeQueue, sendTargets,
taskStatus, topologyContext, userContext, taskStats,
report_error);
} else if (taskObj instanceof ISpout) {
if (isSingleThread(stormConf) == true) {
return new SingleThreadSpoutExecutors((ISpout) taskObj, taskTransfer,
innerTaskTransfer, stormConf, deserializeQueue, sendTargets,
taskStatus, topologyContext, userContext, taskStats,
report_error);
}else {
return new MultipleThreadSpoutExecutors((ISpout) taskObj, taskTransfer,
innerTaskTransfer, stormConf, deserializeQueue, sendTargets,
taskStatus, topologyContext, userContext, taskStats,
report_error);
}
}
return null;
}
/**
* create executor to receive tuples and run bolt/spout execute function
*
* @param puller
* @param sendTargets
* @return
*/
private RunnableCallback mkExecutor(DisruptorQueue deserializeQueue,
TaskSendTargets sendTargets) {
// create report error callback,
// in fact it is storm_cluster.report-task-error
ITaskReportErr reportError = new TaskReportError(zkCluster, topologyid,
taskid);
// report error and halt worker
TaskReportErrorAndDie reportErrorDie = new TaskReportErrorAndDie(
reportError, workHalt);
return mk_executors(deserializeQueue, sendTargets, reportErrorDie);
}
public DisruptorQueue registerDisruptorQueue() {
int queueSize = JStormUtils.parseInt(
stormConf.get(Config.TOPOLOGY_EXECUTOR_RECEIVE_BUFFER_SIZE), 256);
WaitStrategy waitStrategy = (WaitStrategy) Utils
.newInstance((String) stormConf
.get(Config.TOPOLOGY_DISRUPTOR_WAIT_STRATEGY));
DisruptorQueue queue = DisruptorQueue.mkInstance("TaskDeserialize", ProducerType.SINGLE,
queueSize, waitStrategy);
deserializeQueues.put(taskid, queue);
return queue;
}
public TaskShutdownDameon execute() throws Exception {
setComponentType();
DisruptorQueue deserializeQueue = registerDisruptorQueue();
TaskSendTargets sendTargets = echoToSystemBolt();
// create thread to get tuple from zeroMQ,
// and pass the tuple to bolt/spout
RunnableCallback baseExecutor = mkExecutor(deserializeQueue, sendTargets);
AsyncLoopThread executor_threads = new AsyncLoopThread(baseExecutor,
false, Thread.MAX_PRIORITY, true);
List<AsyncLoopThread> allThreads = new ArrayList<AsyncLoopThread>();
allThreads.add(executor_threads);
TaskHeartbeatRunable.registerTaskStats(taskid, new TaskStats(componentType, taskStats));
LOG.info("Finished loading task " + componentid + ":" + taskid);
return getShutdown(allThreads, deserializeQueue, baseExecutor);
}
public TaskShutdownDameon getShutdown(List<AsyncLoopThread> allThreads,
DisruptorQueue deserializeQueue, RunnableCallback baseExecutor) {
AsyncLoopThread ackerThread = null;
if (baseExecutor instanceof SpoutExecutors) {
ackerThread = ((SpoutExecutors) baseExecutor).getAckerRunnableThread();
if (ackerThread != null) {
allThreads.add(ackerThread);
}
}
AsyncLoopThread recvThread = ((BaseExecutors) baseExecutor).getDeserlizeThread();
allThreads.add(recvThread);
AsyncLoopThread serializeThread = taskTransfer.getSerializeThread();
allThreads.add(serializeThread);
TaskShutdownDameon shutdown = new TaskShutdownDameon(taskStatus,
topologyid, taskid, allThreads, zkCluster, taskObj);
return shutdown;
}
public static TaskShutdownDameon mk_task(WorkerData workerData, int taskId)
throws Exception {
Task t = new Task(workerData, taskId);
return t.execute();
}
}