All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.netflix.servo.publish.AsyncMetricObserver Maven / Gradle / Ivy

There is a newer version: 0.13.2
Show newest version
/*
 * Copyright 2014 Netflix, 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.netflix.servo.publish;

import com.netflix.servo.Metric;
import com.netflix.servo.monitor.Counter;
import com.netflix.servo.monitor.Monitors;
import com.netflix.servo.util.Preconditions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;

/**
 * Wraps another observer and asynchronously updates it in the background. The
 * update call will always return immediately. If the queue fills up newer
 * updates will overwrite older updates.
 * 

* If an exception is thrown when calling update on wrapped observer it will * be logged, but otherwise ignored. */ public final class AsyncMetricObserver extends BaseMetricObserver { private static final Logger LOGGER = LoggerFactory.getLogger(AsyncMetricObserver.class); private final MetricObserver wrappedObserver; private final long expireTime; private final BlockingQueue updateQueue; private volatile boolean stopUpdateThread = false; private final Thread processingThread; /** * The number of updates that have been expired and dropped. */ private final Counter expiredUpdateCount = Monitors.newCounter("expiredUpdateCount"); /** * Creates a new instance. * * @param name name of this observer * @param observer a wrapped observer that will be updated asynchronously * @param queueSize maximum size of the update queue, if the queue fills * up older entries will be dropped * @param expireTime age in milliseconds before an update expires and will * not be passed on to the wrapped observer */ public AsyncMetricObserver( String name, MetricObserver observer, int queueSize, long expireTime) { super(name); this.expireTime = expireTime; wrappedObserver = Preconditions.checkNotNull(observer, "observer"); Preconditions.checkArgument(queueSize >= 1, String.format("invalid queueSize %d, size must be >= 1", queueSize)); updateQueue = new LinkedBlockingDeque<>(queueSize); String threadName = getClass().getSimpleName() + "-" + name; processingThread = new Thread(new UpdateProcessor(), threadName); processingThread.setDaemon(true); processingThread.start(); } /** * Creates a new instance with an unbounded queue and no expiration time. * * @param name name of this observer * @param observer a wrapped observer that will be updated asynchronously */ public AsyncMetricObserver(String name, MetricObserver observer) { this(name, observer, Integer.MAX_VALUE, Long.MAX_VALUE); } /** * Creates a new instance with no expiration time. * * @param name name of this observer * @param observer a wrapped observer that will be updated asynchronously * @param queueSize maximum size of the update queue, if the queue fills * up older entries will be dropped */ public AsyncMetricObserver(String name, MetricObserver observer, int queueSize) { this(name, observer, queueSize, Long.MAX_VALUE); } /** * {@inheritDoc} */ public void updateImpl(List metrics) { long now = System.currentTimeMillis(); TimestampedUpdate update = new TimestampedUpdate(now, metrics); boolean result = updateQueue.offer(update); final int maxAttempts = 5; int attempts = 0; while (!result && attempts < maxAttempts) { updateQueue.remove(); result = updateQueue.offer(update); ++attempts; } if (!result) { incrementFailedCount(); } } /** * Stop the background thread that pushes updates to the wrapped observer. */ public void stop() { stopUpdateThread = true; processingThread.interrupt(); // in case it is blocked on an empty queue } private void processUpdate() { TimestampedUpdate update; try { update = updateQueue.take(); long cutoff = System.currentTimeMillis() - expireTime; if (update.getTimestamp() < cutoff) { expiredUpdateCount.increment(); return; } wrappedObserver.update(update.getMetrics()); } catch (InterruptedException ie) { LOGGER.warn("interrupted while adding to queue, update dropped"); incrementFailedCount(); } catch (Throwable t) { LOGGER.warn("update failed for downstream queue", t); incrementFailedCount(); } } private class UpdateProcessor implements Runnable { public void run() { while (!stopUpdateThread) { processUpdate(); } } } private static class TimestampedUpdate { private final long timestamp; private final List metrics; TimestampedUpdate(long timestamp, List metrics) { this.timestamp = timestamp; this.metrics = metrics; } long getTimestamp() { return timestamp; } List getMetrics() { return metrics; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy