/** * Copyright 2016 Yahoo Inc. * * Licensed 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.yahoo.pulsar.client.impl; import java.io.IOException; import java.io.Serializable; import java.text.DecimalFormat; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.LongAdder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; import com.fasterxml.jackson.databind.SerializationFeature; import com.yahoo.pulsar.client.api.ConsumerConfiguration; import com.yahoo.pulsar.client.api.Message; import io.netty.util.Timeout; import io.netty.util.TimerTask; public class ConsumerStats implements Serializable { private static final long serialVersionUID = 1L; private TimerTask stat; private Timeout statTimeout; private ConsumerImpl consumer; private PulsarClientImpl pulsarClient; private long oldTime; private long statsIntervalSeconds; private final LongAdder numMsgsReceived; private final LongAdder numBytesReceived; private final LongAdder numReceiveFailed; private final LongAdder numAcksSent; private final LongAdder numAcksFailed; private final LongAdder totalMsgsReceived; private final LongAdder totalBytesReceived; private final LongAdder totalReceiveFailed; private final LongAdder totalAcksSent; private final LongAdder totalAcksFailed; private final DecimalFormat throughputFormat; public static final ConsumerStats CONSUMER_STATS_DISABLED = new ConsumerStatsDisabled(); public ConsumerStats() { numMsgsReceived = null; numBytesReceived = null; numReceiveFailed = null; numAcksSent = null; numAcksFailed = null; totalMsgsReceived = null; totalBytesReceived = null; totalReceiveFailed = null; totalAcksSent = null; totalAcksFailed = null; throughputFormat = null; } public ConsumerStats(PulsarClientImpl pulsarClient, ConsumerConfiguration conf, ConsumerImpl consumer) { this.pulsarClient = pulsarClient; this.consumer = consumer; this.statsIntervalSeconds = pulsarClient.getConfiguration().getStatsIntervalSeconds(); numMsgsReceived = new LongAdder(); numBytesReceived = new LongAdder(); numReceiveFailed = new LongAdder(); numAcksSent = new LongAdder(); numAcksFailed = new LongAdder(); totalMsgsReceived = new LongAdder(); totalBytesReceived = new LongAdder(); totalReceiveFailed = new LongAdder(); totalAcksSent = new LongAdder(); totalAcksFailed = new LongAdder(); throughputFormat = new DecimalFormat("0.00"); init(conf); } private void init(ConsumerConfiguration conf) { ObjectMapper m = new ObjectMapper(); m.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); ObjectWriter w = m.writerWithDefaultPrettyPrinter(); try { log.info("Starting Pulsar consumer perf with config: {}", w.writeValueAsString(conf)); log.info("Pulsar client config: {}", w.writeValueAsString(pulsarClient.getConfiguration())); } catch (IOException e) { log.error("Failed to dump config info: {}", e); } stat = (timeout) -> { if (timeout.isCancelled()) { return; } try { long now = System.nanoTime(); double elapsed = (now - oldTime) / 1e9; oldTime = now; long currentNumMsgsReceived = numMsgsReceived.sumThenReset(); long currentNumBytesReceived = numBytesReceived.sumThenReset(); long currentNumReceiveFailed = numReceiveFailed.sumThenReset(); long currentNumAcksSent = numAcksSent.sumThenReset(); long currentNumAcksFailed = numAcksFailed.sumThenReset(); totalMsgsReceived.add(currentNumMsgsReceived); totalBytesReceived.add(currentNumBytesReceived); totalReceiveFailed.add(currentNumReceiveFailed); totalAcksSent.add(currentNumAcksSent); totalAcksFailed.add(currentNumAcksFailed); if ((currentNumMsgsReceived | currentNumBytesReceived | currentNumReceiveFailed | currentNumAcksSent | currentNumAcksFailed) != 0) { log.info( "[{}] [{}] [{}] Prefetched messages: {} --- Consume throughput: {} msgs/s --- " + "Throughput received: {} msg/s --- {} Mbit/s --- " + "Ack sent rate: {} ack/s --- " + "Failed messages: {} --- " + "Failed acks: {}", consumer.getTopic(), consumer.getSubscription(), consumer.consumerName, consumer.incomingMessages.size(), throughputFormat.format(currentNumMsgsReceived / elapsed), throughputFormat.format(currentNumBytesReceived / elapsed * 8 / 1024 / 1024), throughputFormat.format(currentNumAcksSent / elapsed), currentNumReceiveFailed, currentNumAcksFailed); } } catch (Exception e) { log.error("[{}] [{}] [{}]: {}", consumer.getTopic(), consumer.subscription, consumer.consumerName, e.getMessage()); } finally { // schedule the next stat info statTimeout = pulsarClient.timer().newTimeout(stat, statsIntervalSeconds, TimeUnit.SECONDS); } }; oldTime = System.nanoTime(); statTimeout = pulsarClient.timer().newTimeout(stat, statsIntervalSeconds, TimeUnit.SECONDS); } void updateNumMsgsReceived(Message message) { if (message != null) { numMsgsReceived.increment(); numBytesReceived.add(message.getData().length); } } void incrementNumAcksSent(long numAcks) { numAcksSent.add(numAcks); } void incrementNumAcksFailed() { numAcksFailed.increment(); } void incrementNumReceiveFailed() { numReceiveFailed.increment(); } Timeout getStatTimeout() { return statTimeout; } void reset() { numMsgsReceived.reset(); numBytesReceived.reset(); numReceiveFailed.reset(); numAcksSent.reset(); numAcksFailed.reset(); totalMsgsReceived.reset(); totalBytesReceived.reset(); totalReceiveFailed.reset(); totalAcksSent.reset(); totalAcksFailed.reset(); } void updateCumulativeStats(ConsumerStats stats) { if (stats == null) { return; } numMsgsReceived.add(stats.numMsgsReceived.longValue()); numBytesReceived.add(stats.numBytesReceived.longValue()); numReceiveFailed.add(stats.numReceiveFailed.longValue()); numAcksSent.add(stats.numAcksSent.longValue()); numAcksFailed.add(stats.numAcksFailed.longValue()); totalMsgsReceived.add(stats.totalMsgsReceived.longValue()); totalBytesReceived.add(stats.totalBytesReceived.longValue()); totalReceiveFailed.add(stats.totalReceiveFailed.longValue()); totalAcksSent.add(stats.totalAcksSent.longValue()); totalAcksFailed.add(stats.totalAcksFailed.longValue()); } public long getNumMsgsReceived() { return numMsgsReceived.longValue(); } public long getNumBytesReceived() { return numBytesReceived.longValue(); } public long getNumAcksSent() { return numAcksSent.longValue(); } public long getNumAcksFailed() { return numAcksFailed.longValue(); } public long getNumReceiveFailed() { return numReceiveFailed.longValue(); } public long getTotalMsgsReceived() { return totalMsgsReceived.longValue(); } public long getTotalBytesReceived() { return totalBytesReceived.longValue(); } public long getTotalReceivedFailed() { return totalReceiveFailed.longValue(); } public long getTotalAcksSent() { return totalAcksSent.longValue(); } public long getTotalAcksFailed() { return totalAcksFailed.longValue(); } private static final Logger log = LoggerFactory.getLogger(ConsumerStats.class); }