/**
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import backtype.storm.task.ICollectorCallback;
import backtype.storm.task.OutputCollectorCb;
import backtype.storm.utils.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.jstorm.common.metric.AsmHistogram;
import com.alibaba.jstorm.metric.JStormMetrics;
import com.alibaba.jstorm.metric.MetricDef;
import com.alibaba.jstorm.metric.MetricType;
import com.alibaba.jstorm.metric.MetricUtils;
import com.alibaba.jstorm.task.Task;
import com.alibaba.jstorm.task.TaskBaseMetric;
import com.alibaba.jstorm.task.TaskTransfer;
import com.alibaba.jstorm.task.acker.Acker;
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.utils.JStormUtils;
import com.alibaba.jstorm.utils.RotatingMap;
import backtype.storm.Config;
import backtype.storm.task.TopologyContext;
import backtype.storm.tuple.MessageId;
import backtype.storm.tuple.Tuple;
import backtype.storm.tuple.TupleExt;
import backtype.storm.tuple.TupleImplExt;
/**
* bolt output interface, do emit/ack/fail
*
* @author yannian/Longda
*/
public class BoltCollector extends OutputCollectorCb {
private static Logger LOG = LoggerFactory.getLogger(BoltCollector.class);
protected ITaskReportErr reportError;
protected TaskSendTargets sendTargets;
protected TaskTransfer taskTransfer;
protected TopologyContext topologyContext;
protected Integer task_id;
// protected TimeCacheMap<Tuple, Long> tuple_start_times;
protected final RotatingMap<Tuple, Long> tuple_start_times;
protected TaskBaseMetric task_stats;
// protected TimeCacheMap<Tuple, Long> pending_acks;
protected final RotatingMap<Tuple, Long> pending_acks;
protected long lastRotate = System.currentTimeMillis();
protected long rotateTime;
protected Map storm_conf;
protected Integer ackerNum;
protected AsmHistogram emitTimer;
protected Random random;
//ITaskReportErr report_error, TaskSendTargets _send_fn, Map _storm_conf, TaskTransfer _transfer_fn,
//TopologyContext _topology_context, Integer task_id, TaskBaseMetric _task_stats
public BoltCollector(Task task, RotatingMap<Tuple, Long> tuple_start_times, int message_timeout_secs) {
this.rotateTime = 1000L * message_timeout_secs / (Acker.TIMEOUT_BUCKET_NUM - 1);
this.reportError = task.getReportErrorDie();
this.sendTargets = task.getTaskSendTargets();
this.storm_conf = task.getStormConf();
this.taskTransfer = task.getTaskTransfer();
this.topologyContext = task.getTopologyContext();
this.task_id = task.getTaskId();
this.task_stats = task.getTaskStats();
this.pending_acks = new RotatingMap<Tuple, Long>(Acker.TIMEOUT_BUCKET_NUM);
// this.pending_acks = new TimeCacheMap<Tuple,
// Long>(message_timeout_secs,
// Acker.TIMEOUT_BUCKET_NUM);
this.tuple_start_times = tuple_start_times;
this.ackerNum = JStormUtils.parseInt(storm_conf.get(Config.TOPOLOGY_ACKER_EXECUTORS));
String componentId = topologyContext.getThisComponentId();
this.emitTimer = (AsmHistogram) JStormMetrics.registerTaskMetric(MetricUtils.taskMetricName(
topologyContext.getTopologyId(), componentId, task_id, MetricDef.COLLECTOR_EMIT_TIME, MetricType.HISTOGRAM),
new AsmHistogram());
this.emitTimer.setEnabled(false);
//this.emitTimer.setTimeUnit(TimeUnit.NANOSECONDS);
random = new Random(Utils.secureRandomLong());
/* random.setSeed(System.currentTimeMillis());*/
}
@Override
public List<Integer> emit(String streamId, Collection<Tuple> anchors, List<Object> tuple) {
return sendBoltMsg(streamId, anchors, tuple, null, null);
}
@Override
public void emitDirect(int taskId, String streamId, Collection<Tuple> anchors, List<Object> tuple) {
sendBoltMsg(streamId, anchors, tuple, taskId, null);
}
@Override
public List<Integer> emit(String streamId, Collection<Tuple> anchors, List<Object> tuple, ICollectorCallback callback) {
return sendBoltMsg(streamId, anchors, tuple, null, callback);
}
@Override
public void emitDirect(int taskId, String streamId, Collection<Tuple> anchors, List<Object> tuple, ICollectorCallback callback) {
sendBoltMsg(streamId, anchors, tuple, taskId, callback);
}
public List<Integer> emitCtrl(String streamId, Collection<Tuple> anchors, List<Object> tuple) {
return sendCtrlMsg(streamId, tuple, anchors, null);
}
public void emitDirectCtrl(int taskId, String streamId, Collection<Tuple> anchors, List<Object> tuple) {
sendCtrlMsg(streamId, tuple, anchors, taskId);
}
protected List<Integer> sendBoltMsg(String outStreamId, Collection<Tuple> anchors, List<Object> values, Integer outTaskId,
ICollectorCallback callback) {
java.util.List<Integer> outTasks = null;
outTasks = sendMsg(outStreamId, values, anchors, outTaskId, callback);
return outTasks;
}
protected MessageId getMessageId(Collection<Tuple> anchors) {
MessageId ret = null;
if (anchors != null && ackerNum > 0) {
Map<Long, Long> anchors_to_ids = new HashMap<Long, Long>();
for (Tuple a : anchors) {
if (a.getMessageId() != null) {
Long edge_id = MessageId.generateId(random);
put_xor(pending_acks, a, edge_id);
MessageId messageId = a.getMessageId();
if (messageId != null){
for (Long root_id : messageId.getAnchorsToIds().keySet()) {
put_xor(anchors_to_ids, root_id, edge_id);
}
}
}
}
ret = MessageId.makeId(anchors_to_ids);
}
return ret;
}
public List<Integer> sendMsg(String out_stream_id, List<Object> values, Collection<Tuple> anchors, Integer out_task_id, ICollectorCallback callback) {
final long start = emitTimer.getTime();
List<Integer> outTasks = null;
try {
if (out_task_id != null) {
outTasks = sendTargets.get(out_task_id, out_stream_id, values, anchors, null);
} else {
outTasks = sendTargets.get(out_stream_id, values, anchors, null);
}
tryRotate();
for (Integer t : outTasks) {
MessageId msgid = getMessageId(anchors);
TupleImplExt tp = new TupleImplExt(topologyContext, values, task_id, out_stream_id, msgid);
tp.setTargetTaskId(t);
taskTransfer.transfer(tp);
}
} catch (Exception e) {
LOG.error("bolt emit", e);
} finally {
if (outTasks == null) {
outTasks = new ArrayList<Integer>();
}
if (callback != null)
callback.execute(out_stream_id, outTasks, values);
emitTimer.updateTime(start);
}
return outTasks;
}
private void tryRotate() {
long now = System.currentTimeMillis();
if (now - lastRotate > rotateTime) {
pending_acks.rotate();
lastRotate = now;
}
}
void unanchoredSend(TopologyContext topologyContext, TaskSendTargets taskTargets, TaskTransfer transfer_fn, String stream, List<Object> values){
UnanchoredSend.send(topologyContext, taskTargets, transfer_fn, stream, values);
}
void transferCtr(TupleImplExt tupleExt){
taskTransfer.transferControl(tupleExt);
}
protected List<Integer> sendCtrlMsg(String out_stream_id, List<Object> values, Collection<Tuple> anchors, Integer out_task_id) {
final long start = emitTimer.getTime();
java.util.List<Integer> out_tasks = null;
try {
if (out_task_id != null) {
out_tasks = sendTargets.get(out_task_id, out_stream_id, values, anchors, null);
} else {
out_tasks = sendTargets.get(out_stream_id, values, anchors, null);
}
tryRotate();
for (Integer t : out_tasks) {
MessageId msgid = getMessageId(anchors);
TupleImplExt tp = new TupleImplExt(topologyContext, values, task_id, out_stream_id, msgid);
tp.setTargetTaskId(t);
transferCtr(tp);
}
} catch (Exception e) {
LOG.error("bolt emit", e);
} finally {
emitTimer.updateTime(start);
}
return out_tasks;
}
@Override
public void ack(Tuple input) {
if (input.getMessageId() != null) {
Long ack_val = 0L;
Object pend_val = pending_acks.remove(input);
if (pend_val != null) {
ack_val = (Long) (pend_val);
}
for (Entry<Long, Long> e : input.getMessageId().getAnchorsToIds().entrySet()) {
unanchoredSend(topologyContext, sendTargets, taskTransfer, Acker.ACKER_ACK_STREAM_ID,
JStormUtils.mk_list((Object) e.getKey(), JStormUtils.bit_xor(e.getValue(), ack_val)));
}
}
Long latencyStart = (Long) tuple_start_times.remove(input);
task_stats.bolt_acked_tuple(input.getSourceComponent(), input.getSourceStreamId());
if (latencyStart != null && JStormMetrics.enabled) {
long endTime = System.currentTimeMillis();
task_stats.update_bolt_acked_latency(input.getSourceComponent(), input.getSourceStreamId(),
latencyStart, endTime);
}
}
@Override
public void fail(Tuple input) {
// if ackerNum == 0, we can just return
if (input.getMessageId() != null) {
pending_acks.remove(input);
for (Entry<Long, Long> e : input.getMessageId().getAnchorsToIds().entrySet()) {
unanchoredSend(topologyContext, sendTargets, taskTransfer, Acker.ACKER_FAIL_STREAM_ID, JStormUtils.mk_list((Object) e.getKey()));
}
}
tuple_start_times.remove(input);
task_stats.bolt_failed_tuple(input.getSourceComponent(), input.getSourceStreamId());
}
@Override
public void reportError(Throwable error) {
reportError.report(error);
}
public static void put_xor(RotatingMap<Tuple, Long> pending, Tuple key, Long id) {
// synchronized (pending) {
Long curr = pending.get(key);
if (curr == null) {
curr = 0L;
}
pending.put(key, JStormUtils.bit_xor(curr, id));
// }
}
public static void put_xor(Map<Long, Long> pending, Long key, Long id) {
// synchronized (pending) {
Long curr = pending.get(key);
if (curr == null) {
curr = 0L;
}
pending.put(key, JStormUtils.bit_xor(curr, id));
// }
}
}