/** * 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.websocket.stats; import static com.yahoo.pulsar.websocket.ProducerHandler.ENTRY_LATENCY_BUCKETS_USEC; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.yahoo.pulsar.common.naming.DestinationName; import com.yahoo.pulsar.common.stats.Metrics; import com.yahoo.pulsar.common.util.collections.ConcurrentOpenHashMap; import com.yahoo.pulsar.websocket.WebSocketService; /** * It periodically generates stats metrics of proxy service, * */ public class ProxyStats { private final WebSocketService service; private final JvmMetrics jvmMetrics; private ConcurrentOpenHashMap<String, ProxyNamespaceStats> topicStats; private List<Metrics> tempMetricsCollection; private List<Metrics> metricsCollection; public ProxyStats(WebSocketService service) { super(); this.service = service; this.jvmMetrics = new JvmMetrics(service); this.topicStats = new ConcurrentOpenHashMap<>(); this.tempMetricsCollection = Lists.newArrayList(); this.metricsCollection = Lists.newArrayList(); // schedule stat generation task every 1 minute service.getExecutor().scheduleAtFixedRate(() -> generate(), 120, 60, TimeUnit.SECONDS); } /** * generates stats-metrics of proxy service and updates metricsCollection cache with latest stats. */ public synchronized void generate() { topicStats.clear(); service.getProducers().forEach((topic, handlers) -> { final String namespaceName = DestinationName.get(topic).getNamespace(); ProxyNamespaceStats nsStat = topicStats.computeIfAbsent(namespaceName, ns -> new ProxyNamespaceStats()); handlers.forEach(handler -> { nsStat.numberOfMsgPublished += handler.getAndResetNumMsgsSent(); nsStat.numberOfBytesPublished += handler.getAndResetNumBytesSent(); nsStat.numberOfPublishFailure += handler.getAndResetNumMsgsFailed(); if (nsStat.publishMsgLatency == null) { nsStat.publishMsgLatency = new StatsBuckets(ENTRY_LATENCY_BUCKETS_USEC); } handler.getPublishLatencyStatsUSec().refresh(); nsStat.publishMsgLatency.addAll(handler.getPublishLatencyStatsUSec()); System.out.println(nsStat.publishMsgLatency); }); }); service.getConsumers().forEach((topic, handlers) -> { final String namespaceName = DestinationName.get(topic).getNamespace(); ProxyNamespaceStats nsStat = topicStats.computeIfAbsent(namespaceName, ns -> new ProxyNamespaceStats()); handlers.forEach(handler -> { nsStat.numberOfMsgDelivered += handler.getAndResetNumMsgsAcked(); nsStat.numberOfBytesDelivered += handler.getAndResetNumBytesDelivered(); nsStat.numberOfMsgsAcked += handler.getAndResetNumMsgsAcked(); }); }); tempMetricsCollection.clear(); topicStats.forEach((namespace, stats) -> { tempMetricsCollection.add(stats.add(namespace)); }); // add jvm-metrics tempMetricsCollection.add(jvmMetrics.generate()); // swap tempmetrics to stat-metrics List<Metrics> tempRef = metricsCollection; metricsCollection = tempMetricsCollection; tempRef.clear(); } public synchronized List<Metrics> getMetrics() { return metricsCollection; } private static class ProxyNamespaceStats { public long numberOfMsgPublished; public long numberOfBytesPublished; public long numberOfPublishFailure; public StatsBuckets publishMsgLatency; public long numberOfMsgDelivered; public long numberOfBytesDelivered; public long numberOfMsgsAcked; public Metrics add(String namespace) { publishMsgLatency.refresh(); long[] latencyBuckets = publishMsgLatency.getBuckets(); Map<String, String> dimensionMap = Maps.newHashMap(); dimensionMap.put("namespace", namespace); Metrics dMetrics = Metrics.create(dimensionMap); dMetrics.put("ns_msg_publish_rate", numberOfMsgPublished); dMetrics.put("ns_byte_publish_rate", numberOfBytesPublished); dMetrics.put("ns_msg_failure_rate", numberOfPublishFailure); dMetrics.put("ns_msg_deliver_rate", numberOfMsgDelivered); dMetrics.put("ns_byte_deliver_rate", numberOfBytesDelivered); dMetrics.put("ns_msg_ack_rate", numberOfMsgsAcked); for (int i = 0; i < latencyBuckets.length; i++) { final String latencyBucket = i >= ENTRY_LATENCY_BUCKETS_USEC.length ? ENTRY_LATENCY_BUCKETS_USEC[ENTRY_LATENCY_BUCKETS_USEC.length-1] + "_higher" : Long.toString(ENTRY_LATENCY_BUCKETS_USEC[i]); dMetrics.put("ns_msg_publish_latency_" + latencyBucket, latencyBuckets[i]); } return dMetrics; } } }