package resa.metrics; import backtype.storm.Config; import backtype.storm.metric.api.MultiCountMetric; import backtype.storm.task.IOutputCollector; import backtype.storm.task.OutputCollector; import backtype.storm.task.TopologyContext; import backtype.storm.topology.IRichBolt; import backtype.storm.tuple.Tuple; import backtype.storm.utils.Utils; import resa.topology.DelegatedBolt; import resa.util.ConfigUtil; import resa.util.ResaConfig; import resa.util.Sampler; import java.util.Collection; import java.util.List; import java.util.Map; /** * Created by ding on 14-1-27. */ public class MeasurableBolt extends DelegatedBolt { private class MeasurableOutputCollector extends OutputCollector { private boolean sample = false; MeasurableOutputCollector(IOutputCollector delegate) { super(delegate); } public void setEmitSample(boolean sample) { this.sample = sample; } @Override public List<Integer> emit(String streamId, Collection<Tuple> anchors, List<Object> tuple) { if (sample) { emitMetric.scope(streamId).incr(); } return super.emit(streamId, anchors, tuple); } @Override public void emitDirect(int taskId, String streamId, Collection<Tuple> anchors, List<Object> tuple) { if (sample) { emitMetric.scope(streamId).incr(); } super.emitDirect(taskId, streamId, anchors, tuple); } } private transient CMVMetric executeMetric; private Sampler sampler; private transient MultiCountMetric emitMetric; private transient MeasurableOutputCollector measurableCollector; private long lastMetricsSent; public MeasurableBolt(IRichBolt delegate) { super(delegate); } @Override public void prepare(Map conf, TopologyContext context, OutputCollector outputCollector) { int interval = Utils.getInt(conf.get(Config.TOPOLOGY_BUILTIN_METRICS_BUCKET_SIZE_SECS)); executeMetric = context.registerMetric(MetricNames.TASK_EXECUTE, new CMVMetric(), interval); emitMetric = context.registerMetric(MetricNames.EMIT_COUNT, new MultiCountMetric(), interval); lastMetricsSent = System.currentTimeMillis(); context.registerMetric(MetricNames.DURATION, this::getMetricsDuration, interval); sampler = new Sampler(ConfigUtil.getDouble(conf, ResaConfig.COMP_SAMPLE_RATE, 0.05)); measurableCollector = new MeasurableOutputCollector(outputCollector); super.prepare(conf, context, measurableCollector); } private long getMetricsDuration() { long now = System.currentTimeMillis(); long duration = now - lastMetricsSent; lastMetricsSent = now; return duration; } @Override public void execute(Tuple tuple) { long elapse; if (sampler.shoudSample()) { // enable emit sample measurableCollector.setEmitSample(true); long arrivalTime = System.nanoTime(); super.execute(tuple); elapse = System.nanoTime() - arrivalTime; } else { elapse = -1; // disable emit sample measurableCollector.setEmitSample(false); super.execute(tuple); } // avoid numerical overflow if (elapse > 0) { String id = tuple.getSourceComponent() + ":" + tuple.getSourceStreamId(); executeMetric.addMetric(id, elapse / 1000000.0); // System.out.println("from " + tuple.getSourceComponent() + ":" + tuple.getSourceStreamId() + // " cost->" + (elapse / 1000000.0)); } } }