/**
* 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.execute;
import backtype.storm.Config;
import backtype.storm.Constants;
import backtype.storm.task.IBolt;
import backtype.storm.task.OutputCollector;
import backtype.storm.topology.IRichBatchBolt;
import backtype.storm.tuple.MessageId;
import backtype.storm.tuple.Tuple;
import backtype.storm.tuple.TupleExt;
import backtype.storm.tuple.TupleImplExt;
import com.alibaba.jstorm.client.ConfigExtension;
import com.alibaba.jstorm.cluster.Common;
import com.alibaba.jstorm.daemon.worker.timer.TickTupleTrigger;
import com.alibaba.jstorm.daemon.worker.timer.TimerConstants;
import com.alibaba.jstorm.daemon.worker.timer.TimerTrigger;
import com.alibaba.jstorm.metric.JStormMetrics;
import com.alibaba.jstorm.task.*;
import com.alibaba.jstorm.task.acker.Acker;
import com.alibaba.jstorm.task.master.ctrlevent.TopoMasterCtrlEvent;
import com.alibaba.jstorm.utils.JStormUtils;
import com.alibaba.jstorm.utils.Pair;
import com.alibaba.jstorm.utils.RotatingMap;
import com.alibaba.jstorm.utils.TimeUtils;
import com.lmax.disruptor.EventHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**
* BoltExecutor
*
* @author yannian/Longda
*/
public class BoltExecutors extends BaseExecutors implements EventHandler {
private static Logger LOG = LoggerFactory.getLogger(BoltExecutors.class);
protected IBolt bolt;
protected RotatingMap<Tuple, Long> tuple_start_times;
private int ackerNum = 0;
// internal outputCollector is BoltCollector
private OutputCollector outputCollector;
/**
* execute time, used for backpressure, in ms.
*/
private volatile double exeTime;
private boolean isSystemBolt;
//, IBolt _bolt, TaskTransfer _transfer_fn, Map<Integer, DisruptorQueue> innerTaskTransfer, Map storm_conf,
//TaskSendTargets _send_fn, TaskStatus taskStatus, TopologyContext sysTopologyCxt, TopologyContext userTopologyCxt, TaskBaseMetric _task_stats,
//ITaskReportErr _report_error, JStormMetricsReporter metricReport
public BoltExecutors(Task task) {
super(task);
this.bolt = (IBolt) task.getTaskObj();
// create TimeCacheMap
this.tuple_start_times = new RotatingMap<Tuple, Long>(Acker.TIMEOUT_BUCKET_NUM);
this.ackerNum = JStormUtils.parseInt(storm_conf.get(Config.TOPOLOGY_ACKER_EXECUTORS));
// create BoltCollector
BoltCollector output_collector = null;
if (ConfigExtension.isTaskBatchTuple(storm_conf)) {
output_collector = new BoltBatchCollector(task, tuple_start_times, message_timeout_secs);
} else {
output_collector = new BoltCollector(task, tuple_start_times, message_timeout_secs);
}
outputCollector = new OutputCollector(output_collector);
//this task don't continue until it bulid connection with topologyMaster
Integer topologyId = sysTopologyCtx.getTopologyMasterId();
List<Integer> localWorkerTasks = sysTopologyCtx.getThisWorkerTasks();
if (topologyId != 0 && !localWorkerTasks.contains(topologyId)) {
while (getConnection(topologyId) == null) {
JStormUtils.sleepMs(10);
LOG.info("this task still is building connection with topology Master");
}
}
taskHbTrigger.setBoltOutputCollector(output_collector);
taskHbTrigger.register();
Object tickFrequence = storm_conf.get(Config.TOPOLOGY_TICK_TUPLE_FREQ_MS);
if (tickFrequence == null) {
tickFrequence = storm_conf.get(Config.TOPOLOGY_TICK_TUPLE_FREQ_SECS);
if (tickFrequence != null)
tickFrequence = JStormUtils.parseInt(tickFrequence) * 1000;
}
isSystemBolt = Common.isSystemComponent(componentId);
if (tickFrequence != null && !isSystemBolt) {
Integer frequence = JStormUtils.parseInt(tickFrequence);
TickTupleTrigger tickTupleTrigger = new TickTupleTrigger(sysTopologyCtx, frequence, idStr + Constants.SYSTEM_TICK_STREAM_ID, controlQueue);
tickTupleTrigger.register();
}
LOG.info("Successfully create BoltExecutors " + idStr);
}
@Override
public void init() {
bolt.prepare(storm_conf, userTopologyCtx, outputCollector);
//send the HbMsg to TM after finish prepare
taskHbTrigger.updateExecutorStatus(TaskStatus.RUN);
LOG.info("Succeesfully do Bolt.prepare");
}
@Override
public String getThreadName() {
return idStr + "-" + BoltExecutors.class.getSimpleName();
}
@Override
public void run() {
if (!isFinishInit) {
initWrapper();
}
while (!taskStatus.isShutdown()) {
try {
//Asynchronous release the queue, but still is single thread
controlQueue.consumeBatch(this);
exeQueue.consumeBatchWhenAvailable(this);
/* processControlEvent();*/
} catch (Throwable e) {
if (!taskStatus.isShutdown()) {
LOG.error(idStr + " bolt exeutor error", e);
}
}
}
}
@Override
public void onEvent(Object event, long sequence, boolean endOfBatch) throws Exception {
if (event == null) {
return;
}
long start = System.currentTimeMillis();
try {
if (event instanceof Tuple) {
Tuple tuple = (Tuple) event;
int tupleNum = 1;
Long startTime = System.currentTimeMillis();
long lifeCycleStart = ((TupleExt) tuple).getCreationTimeStamp();
task_stats.tupleLifeCycle(tuple.getSourceComponent(), tuple.getSourceStreamId(), lifeCycleStart, startTime);
if (((TupleExt) tuple).isBatchTuple()) {
List<Object> values = ((Tuple) event).getValues();
tupleNum = values.size();
if (bolt instanceof IRichBatchBolt) {
processControlEvent();
processTupleBatchEvent(tuple);
} else {
for (Object value : values) {
Pair<MessageId, List<Object>> val = (Pair<MessageId, List<Object>>) value;
TupleImplExt t = new TupleImplExt(sysTopologyCtx, val.getSecond(), val.getFirst(), ((TupleImplExt) event));
processControlEvent();
processTupleEvent(t);
}
}
} else {
processTupleEvent(tuple);
}
task_stats.recv_tuple(tuple.getSourceComponent(), tuple.getSourceStreamId(), tupleNum);
if (ackerNum == 0) {
// only when acker is disabled
// get tuple process latency
if (JStormMetrics.enabled) {
long endTime = System.currentTimeMillis();
task_stats.update_bolt_acked_latency(tuple.getSourceComponent(), tuple.getSourceStreamId(), startTime, endTime, tupleNum);
}
}
} else if (event instanceof TimerTrigger.TimerEvent) {
processTimerEvent((TimerTrigger.TimerEvent) event);
} else {
LOG.warn("Bolt executor received unknown message");
}
} finally {
if (JStormMetrics.enabled) {
exeTime = System.currentTimeMillis() - start;
}
}
}
private void processTupleBatchEvent(Tuple tuple) {
try {
if ((!isSystemBolt && tuple.getSourceStreamId().equals(Common.TOPOLOGY_MASTER_CONTROL_STREAM_ID)) ||
tuple.getSourceStreamId().equals(Common.TOPOLOGY_MASTER_REGISTER_METRICS_RESP_STREAM_ID)) {
if (tuple.getValues().get(0) instanceof Pair) {
for (Object value : tuple.getValues()) {
Pair<MessageId, List<Object>> val = (Pair<MessageId, List<Object>>) value;
TupleImplExt t = new TupleImplExt(sysTopologyCtx, val.getSecond(), val.getFirst(), ((TupleImplExt) tuple));
processTupleEvent(t);
}
}
} else {
bolt.execute(tuple);
}
} catch (Throwable e) {
error = e;
LOG.error("bolt execute error ", e);
report_error.report(e);
}
}
private void processTupleEvent(Tuple tuple) {
if (tuple.getMessageId() != null && tuple.getMessageId().isAnchored()) {
tuple_start_times.put(tuple, System.currentTimeMillis());
}
try {
if (!isSystemBolt && tuple.getSourceStreamId().equals(Common.TOPOLOGY_MASTER_CONTROL_STREAM_ID)) {
TopoMasterCtrlEvent event = (TopoMasterCtrlEvent) tuple.getValue(0);
if (event.isTransactionEvent()) {
bolt.execute(tuple);
} else {
LOG.warn("Received unexpected control event, {}", event);
}
} else if (tuple.getSourceStreamId().equals(Common.TOPOLOGY_MASTER_REGISTER_METRICS_RESP_STREAM_ID)) {
this.metricsReporter.updateMetricMeta((Map<String, Long>) tuple.getValue(0));
} else {
bolt.execute(tuple);
}
} catch (Throwable e) {
error = e;
LOG.error("bolt execute error ", e);
report_error.report(e);
}
}
private void processTimerEvent(TimerTrigger.TimerEvent event) {
switch (event.getOpCode()) {
case TimerConstants.ROTATING_MAP: {
Map<Tuple, Long> timeoutMap = tuple_start_times.rotate();
if (ackerNum > 0) {
// only when acker is enable
for (Entry<Tuple, Long> entry : timeoutMap.entrySet()) {
Tuple input = entry.getKey();
task_stats.bolt_failed_tuple(input.getSourceComponent(), input.getSourceStreamId());
}
}
break;
}
case TimerConstants.TICK_TUPLE: {
try {
Tuple tuple = (Tuple) event.getMsg();
bolt.execute(tuple);
} catch (Throwable e) {
error = e;
LOG.error("bolt execute error ", e);
report_error.report(e);
}
break;
}
case TimerConstants.TASK_HEARTBEAT: {
taskHbTrigger.setExeThreadHbTime(TimeUtils.current_time_secs());
break;
}
default: {
LOG.warn("Receive unsupported timer event, opcode=" + event.getOpCode());
break;
}
}
}
protected void processControlEvent() {
Object event = controlQueue.poll();
if (event != null) {
if (event instanceof TimerTrigger.TimerEvent) {
processTimerEvent((TimerTrigger.TimerEvent) event);
LOG.debug("Received one event from control queue");
} else if (event instanceof Tuple) {
processTupleEvent((Tuple) event);
} else {
LOG.warn("Received unknown control event, " + event.getClass().getName());
}
}
}
public double getExecuteTime() {
return exeTime;
}
@Override
public Object getOutputCollector() {
return outputCollector;
}
}