/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.jstorm.task;
import backtype.storm.Config;
import backtype.storm.hooks.ITaskHook;
import backtype.storm.messaging.IConnection;
import backtype.storm.messaging.IContext;
import backtype.storm.scheduler.WorkerSlot;
import backtype.storm.serialization.KryoTupleDeserializer;
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 com.alibaba.jstorm.callback.AsyncLoopDefaultKill;
import com.alibaba.jstorm.callback.AsyncLoopThread;
import com.alibaba.jstorm.callback.RunnableCallback;
import com.alibaba.jstorm.client.ConfigExtension;
import com.alibaba.jstorm.cluster.Common;
import com.alibaba.jstorm.cluster.StormClusterState;
import com.alibaba.jstorm.daemon.worker.WorkerData;
import com.alibaba.jstorm.daemon.worker.timer.TaskHeartbeatTrigger;
import com.alibaba.jstorm.schedule.Assignment.AssignmentType;
import com.alibaba.jstorm.task.comm.TaskSendTargets;
import com.alibaba.jstorm.task.comm.UnanchoredSend;
import com.alibaba.jstorm.task.error.ITaskReportErr;
import com.alibaba.jstorm.task.error.TaskReportError;
import com.alibaba.jstorm.task.error.TaskReportErrorAndDie;
import com.alibaba.jstorm.task.execute.BaseExecutors;
import com.alibaba.jstorm.task.execute.BoltExecutors;
import com.alibaba.jstorm.task.execute.spout.MultipleThreadSpoutExecutors;
import com.alibaba.jstorm.task.execute.spout.SingleThreadSpoutExecutors;
import com.alibaba.jstorm.task.execute.spout.SpoutExecutors;
import com.alibaba.jstorm.task.group.MkGrouper;
import com.alibaba.jstorm.utils.JStormServerUtils;
import com.alibaba.jstorm.utils.JStormUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Task instance
*
* @author yannian/Longda
*/
public class Task implements Runnable {
private final static Logger LOG = LoggerFactory.getLogger(Task.class);
private Map<Object, Object> stormConf;
private TopologyContext topologyContext;
private TopologyContext userContext;
private IContext context;
private TaskTransfer taskTransfer;
private TaskReceiver taskReceiver;
private Map<Integer, DisruptorQueue> innerTaskTransfer;
private Map<Integer, DisruptorQueue> deserializeQueues;
private Map<Integer, DisruptorQueue> controlQueues;
private AsyncLoopDefaultKill workHalt;
private String topologyId;
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 TaskBaseMetric taskStats;
private WorkerData workerData;
private TaskSendTargets taskSendTargets;
private TaskReportErrorAndDie reportErrorDie;
private boolean isTaskBatchTuple;
private TaskShutdownDameon taskShutdownDameon;
private ConcurrentHashMap<WorkerSlot, IConnection> nodeportSocket;
private ConcurrentHashMap<Integer, WorkerSlot> taskNodeport;
private BaseExecutors baseExecutors;
@SuppressWarnings("rawtypes")
public Task(WorkerData workerData, int taskId) throws Exception {
openOrPrepareWasCalled = new Atom(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.stormConf = Common.component_conf(workerData.getStormConf(), topologyContext, componentId);
this.taskStatus = new TaskStatus();
this.innerTaskTransfer = workerData.getInnerTaskTransfer();
this.deserializeQueues = workerData.getDeserializeQueues();
this.controlQueues = workerData.getControlQueues();
this.topologyId = workerData.getTopologyId();
this.context = workerData.getContext();
this.workHalt = workerData.getWorkHalt();
this.zkCluster = workerData.getZkCluster();
this.nodeportSocket = workerData.getNodeportSocket();
this.taskNodeport = workerData.getTaskNodeport();
// 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
reportErrorDie = new TaskReportErrorAndDie(reportError, workHalt);
this.taskStats = new TaskBaseMetric(topologyId, componentId, taskId);
//register auto hook
List<String> listHooks = Config.getTopologyAutoTaskHooks(stormConf);
for (String hook : listHooks) {
ITaskHook iTaskHook = (ITaskHook) Utils.newInstance(hook);
userContext.addTaskHook(iTaskHook);
}
LOG.info("Begin to deserialize taskObj " + componentId + ":" + this.taskId);
try {
WorkerClassLoader.switchThreadContext();
this.taskObj = Common.get_task_object(topologyContext.getRawTopology(), componentId, WorkerClassLoader.getInstance());
WorkerClassLoader.restoreThreadContext();
} catch (Exception e) {
if (reportErrorDie != null) {
reportErrorDie.report(e);
} else {
throw e;
}
}
isTaskBatchTuple = ConfigExtension.isTaskBatchTuple(stormConf);
LOG.info("Transfer/receive in batch mode :" + isTaskBatchTuple);
LOG.info("Loading task " + componentId + ":" + this.taskId);
}
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);
return new TaskSendTargets(stormConf, component, streamComponentGrouper, topologyContext, taskStats);
}
private void updateSendTargets() {
if (taskSendTargets != null) {
Map<String, Map<String, MkGrouper>> streamComponentGrouper = Common.outbound_components(topologyContext, workerData);
taskSendTargets.updateStreamCompGrouper(streamComponentGrouper);
} else {
LOG.error("taskSendTargets is null when trying to update it.");
}
}
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 BaseExecutors mkExecutor() {
BaseExecutors baseExecutor = null;
if (taskObj instanceof IBolt) {
baseExecutor = new BoltExecutors(this);
} else if (taskObj instanceof ISpout) {
if (isSingleThread(stormConf) == true) {
baseExecutor = new SingleThreadSpoutExecutors(this);
} else {
baseExecutor = new MultipleThreadSpoutExecutors(this);
}
}
return baseExecutor;
}
/**
* create executor to receive tuples and run bolt/spout execute function
*/
private RunnableCallback prepareExecutor() {
final BaseExecutors baseExecutor = mkExecutor();
return baseExecutor;
}
public TaskReceiver mkTaskReceiver() {
String taskName = JStormServerUtils.getName(componentId, taskId);
//if (isTaskBatchTuple)
// taskReceiver = new TaskBatchReceiver(this, taskId, stormConf, topologyContext, innerTaskTransfer, taskStatus, taskName);
//else
taskReceiver = new TaskReceiver(this, taskId, stormConf, topologyContext, innerTaskTransfer, taskStatus, taskName);
deserializeQueues.put(taskId, taskReceiver.getDeserializeQueue());
return taskReceiver;
}
public TaskShutdownDameon execute() throws Exception {
taskSendTargets = echoToSystemBolt();
// create thread to get tuple from zeroMQ,
// and pass the tuple to bolt/spout
taskTransfer = mkTaskSending(workerData);
RunnableCallback baseExecutor = prepareExecutor();
//set baseExecutors for update
setBaseExecutors((BaseExecutors)baseExecutor);
AsyncLoopThread executor_threads = new AsyncLoopThread(baseExecutor, false, Thread.MAX_PRIORITY, true);
taskReceiver = mkTaskReceiver();
List<AsyncLoopThread> allThreads = new ArrayList<AsyncLoopThread>();
allThreads.add(executor_threads);
LOG.info("Finished loading task " + componentId + ":" + taskId);
taskShutdownDameon = getShutdown(allThreads, baseExecutor);
return taskShutdownDameon;
}
private TaskTransfer mkTaskSending(WorkerData workerData) {
// sending tuple's serializer
KryoTupleSerializer serializer = new KryoTupleSerializer(workerData.getStormConf(), topologyContext.getRawTopology());
String taskName = JStormServerUtils.getName(componentId, taskId);
// Task sending all tuples through this Object
TaskTransfer taskTransfer;
taskTransfer = new TaskTransfer(this, taskName, serializer, taskStatus, workerData, topologyContext);
return taskTransfer;
}
public TaskShutdownDameon getShutdown(List<AsyncLoopThread> allThreads, RunnableCallback baseExecutor) {
AsyncLoopThread ackerThread = null;
if (baseExecutor instanceof SpoutExecutors) {
ackerThread = ((SpoutExecutors) baseExecutor).getAckerRunnableThread();
if (ackerThread != null) {
allThreads.add(ackerThread);
}
}
List<AsyncLoopThread> recvThreads = taskReceiver.getDeserializeThread();
for (AsyncLoopThread recvThread : recvThreads) {
allThreads.add(recvThread);
}
List<AsyncLoopThread> serializeThreads = taskTransfer.getSerializeThreads();
allThreads.addAll(serializeThreads);
TaskHeartbeatTrigger taskHeartbeatTrigger = ((BaseExecutors) baseExecutor).getTaskHbTrigger();
TaskShutdownDameon shutdown = new TaskShutdownDameon(taskStatus, topologyId, taskId, allThreads, zkCluster, taskObj, this, taskHeartbeatTrigger);
return shutdown;
}
public TaskShutdownDameon getTaskShutdownDameon() {
return taskShutdownDameon;
}
public void run() {
try {
taskShutdownDameon = this.execute();
} catch (Throwable e) {
LOG.error("init task take error", e);
if (reportErrorDie != null) {
reportErrorDie.report(e);
} else {
throw new RuntimeException(e);
}
}
}
public static TaskShutdownDameon mk_task(WorkerData workerData, int taskId) throws Exception {
Task t = new Task(workerData, taskId);
return t.execute();
}
/**
* Update the data which can be changed dynamically e.g. when scale-out of a task parallelism
*/
public void updateTaskData() {
// Only update the local task list of topologyContext here. Because
// other
// relative parts in context shall be updated while the updating of
// WorkerData (Task2Component and Component2Task map)
List<Integer> localTasks = JStormUtils.mk_list(workerData.getTaskids());
topologyContext.setThisWorkerTasks(localTasks);
userContext.setThisWorkerTasks(localTasks);
// Update the TaskSendTargets
updateSendTargets();
}
public long getWorkerAssignmentTs() {
return workerData.getAssignmentTs();
}
public AssignmentType getWorkerAssignmentType() {
return workerData.getAssignmentType();
}
public void unregisterDeserializeQueue() {
deserializeQueues.remove(taskId);
}
public String getComponentId() {
return componentId;
}
public Integer getTaskId() {
return taskId;
}
public DisruptorQueue getExecuteQueue() {
return innerTaskTransfer.get(taskId);
}
public DisruptorQueue getDeserializeQueue() {
return deserializeQueues.get(taskId);
}
public Map<Object, Object> getStormConf() {
return stormConf;
}
public TopologyContext getTopologyContext() {
return topologyContext;
}
public TopologyContext getUserContext() {
return userContext;
}
public TaskTransfer getTaskTransfer() {
return taskTransfer;
}
public TaskReceiver getTaskReceiver() {
return taskReceiver;
}
public Map<Integer, DisruptorQueue> getInnerTaskTransfer() {
return innerTaskTransfer;
}
public Map<Integer, DisruptorQueue> getDeserializeQueues() {
return deserializeQueues;
}
public Map<Integer, DisruptorQueue> getControlQueues() {
return controlQueues;
}
public String getTopologyId() {
return topologyId;
}
public TaskStatus getTaskStatus() {
return taskStatus;
}
public StormClusterState getZkCluster() {
return zkCluster;
}
public Object getTaskObj() {
return taskObj;
}
public TaskBaseMetric getTaskStats() {
return taskStats;
}
public WorkerData getWorkerData() {
return workerData;
}
public TaskSendTargets getTaskSendTargets() {
return taskSendTargets;
}
public TaskReportErrorAndDie getReportErrorDie() {
return reportErrorDie;
}
public boolean isTaskBatchTuple() {
return isTaskBatchTuple;
}
public ConcurrentHashMap<WorkerSlot, IConnection> getNodeportSocket() {
return nodeportSocket;
}
public ConcurrentHashMap<Integer, WorkerSlot> getTaskNodeport() {
return taskNodeport;
}
public BaseExecutors getBaseExecutors() {
return baseExecutors;
}
public void setBaseExecutors(BaseExecutors baseExecutors) {
this.baseExecutors = baseExecutors;
}
}