com.netflix.hystrix.util.HystrixTimer Maven / Gradle / Ivy
Show all versions of hystrix-core Show documentation
/**
* Copyright 2012 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.hystrix.util;
import com.netflix.hystrix.HystrixCollapser;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.strategy.HystrixPlugins;
import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
/**
* Timer used by {@link HystrixCommand} to timeout async executions and {@link HystrixCollapser} to trigger batch executions.
*/
public class HystrixTimer {
private static final Logger logger = LoggerFactory.getLogger(HystrixTimer.class);
private static HystrixTimer INSTANCE = new HystrixTimer();
private HystrixTimer() {
// private to prevent public instantiation
}
/**
* Retrieve the global instance.
*/
public static HystrixTimer getInstance() {
return INSTANCE;
}
/**
* Clears all listeners.
*
* NOTE: This will result in race conditions if {@link #addTimerListener(com.netflix.hystrix.util.HystrixTimer.TimerListener)} is being concurrently called.
*
*/
public static void reset() {
ScheduledExecutor ex = INSTANCE.executor.getAndSet(null);
if (ex != null && ex.getThreadPool() != null) {
ex.getThreadPool().shutdownNow();
}
}
/* package */ AtomicReference executor = new AtomicReference();
/**
* Add a {@link TimerListener} that will be executed until it is garbage collected or removed by clearing the returned {@link Reference}.
*
* NOTE: It is the responsibility of code that adds a listener via this method to clear this listener when completed.
*
*
*
* {@code
* // add a TimerListener
* Reference listener = HystrixTimer.getInstance().addTimerListener(listenerImpl);
*
* // sometime later, often in a thread shutdown, request cleanup, servlet filter or something similar the listener must be shutdown via the clear() method
* listener.clear();
* }
*
*
*
* @param listener
* TimerListener implementation that will be triggered according to its getIntervalTimeInMilliseconds()
method implementation.
* @return reference to the TimerListener that allows cleanup via the clear()
method
*/
public Reference addTimerListener(final TimerListener listener) {
startThreadIfNeeded();
// add the listener
Runnable r = new Runnable() {
@Override
public void run() {
try {
listener.tick();
} catch (Exception e) {
logger.error("Failed while ticking TimerListener", e);
}
}
};
ScheduledFuture> f = executor.get().getThreadPool().scheduleAtFixedRate(r, listener.getIntervalTimeInMilliseconds(), listener.getIntervalTimeInMilliseconds(), TimeUnit.MILLISECONDS);
return new TimerReference(listener, f);
}
private static class TimerReference extends SoftReference {
private final ScheduledFuture> f;
TimerReference(TimerListener referent, ScheduledFuture> f) {
super(referent);
this.f = f;
}
@Override
public void clear() {
super.clear();
// stop this ScheduledFuture from any further executions
f.cancel(false);
}
}
/**
* Since we allow resetting the timer (shutting down the thread) we need to lazily re-start it if it starts being used again.
*
* This does the lazy initialization and start of the thread in a thread-safe manner while having little cost the rest of the time.
*/
protected void startThreadIfNeeded() {
// create and start thread if one doesn't exist
while (executor.get() == null || ! executor.get().isInitialized()) {
if (executor.compareAndSet(null, new ScheduledExecutor())) {
// initialize the executor that we 'won' setting
executor.get().initialize();
}
}
}
/* package */ static class ScheduledExecutor {
/* package */ volatile ScheduledThreadPoolExecutor executor;
private volatile boolean initialized;
/**
* We want this only done once when created in compareAndSet so use an initialize method
*/
public void initialize() {
HystrixPropertiesStrategy propertiesStrategy = HystrixPlugins.getInstance().getPropertiesStrategy();
int coreSize = propertiesStrategy.getTimerThreadPoolProperties().getCorePoolSize().get();
ThreadFactory threadFactory = null;
if (!PlatformSpecific.isAppEngineStandardEnvironment()) {
threadFactory = new ThreadFactory() {
final AtomicInteger counter = new AtomicInteger();
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r, "HystrixTimer-" + counter.incrementAndGet());
thread.setDaemon(true);
return thread;
}
};
} else {
threadFactory = PlatformSpecific.getAppEngineThreadFactory();
}
executor = new ScheduledThreadPoolExecutor(coreSize, threadFactory);
initialized = true;
}
public ScheduledThreadPoolExecutor getThreadPool() {
return executor;
}
public boolean isInitialized() {
return initialized;
}
}
public static interface TimerListener {
/**
* The 'tick' is called each time the interval occurs.
*
* This method should NOT block or do any work but instead fire its work asynchronously to perform on another thread otherwise it will prevent the Timer from functioning.
*
* This contract is used to keep this implementation single-threaded and simplistic.
*
* If you need a ThreadLocal set, you can store the state in the TimerListener, then when tick() is called, set the ThreadLocal to your desired value.
*/
public void tick();
/**
* How often this TimerListener should 'tick' defined in milliseconds.
*/
public int getIntervalTimeInMilliseconds();
}
}