com.netflix.hystrix.Hystrix Maven / Gradle / Ivy
Show all versions of hystrix-core Show documentation
/**
* Copyright 2015 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;
import java.util.NoSuchElementException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import com.netflix.hystrix.strategy.HystrixPlugins;
import com.netflix.hystrix.strategy.properties.HystrixPropertiesFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import rx.functions.Action0;
/**
* Lifecycle management of Hystrix.
*/
public class Hystrix {
private static final Logger logger = LoggerFactory.getLogger(Hystrix.class);
/**
* Reset state and release resources in use (such as thread-pools).
*
* NOTE: This can result in race conditions if HystrixCommands are concurrently being executed.
*
*/
public static void reset() {
// shutdown thread-pools
HystrixThreadPool.Factory.shutdown();
_reset();
}
/**
* Reset state and release resources in use (such as threadpools) and wait for completion.
*
* NOTE: This can result in race conditions if HystrixCommands are concurrently being executed.
*
*
* @param time
* time to wait for thread-pools to shutdown
* @param unit
* {@link TimeUnit} for time
to wait for thread-pools to shutdown
*/
public static void reset(long time, TimeUnit unit) {
// shutdown thread-pools
HystrixThreadPool.Factory.shutdown(time, unit);
_reset();
}
/**
* Reset logic that doesn't have time/TimeUnit arguments.
*/
private static void _reset() {
// clear metrics
HystrixCommandMetrics.reset();
HystrixThreadPoolMetrics.reset();
HystrixCollapserMetrics.reset();
// clear collapsers
HystrixCollapser.reset();
// clear circuit breakers
HystrixCircuitBreaker.Factory.reset();
HystrixPlugins.reset();
HystrixPropertiesFactory.reset();
currentCommand.set(new ConcurrentStack());
}
private static ThreadLocal> currentCommand = new ThreadLocal>() {
@Override
protected ConcurrentStack initialValue() {
return new ConcurrentStack();
}
};
/**
* Allows a thread to query whether it's current point of execution is within the scope of a HystrixCommand.
*
* When ExecutionIsolationStrategy is THREAD then this applies to the isolation (child/worker) thread not the calling thread.
*
* When ExecutionIsolationStrategy is SEMAPHORE this applies to the calling thread.
*
* @return HystrixCommandKey of current command being executed or null if none.
*/
public static HystrixCommandKey getCurrentThreadExecutingCommand() {
if (currentCommand == null) {
// statics do "interesting" things across classloaders apparently so this can somehow be null ...
return null;
}
return currentCommand.get().peek();
}
/**
*
* @return Action0 to perform the same work as `endCurrentThreadExecutingCommand()` but can be done from any thread
*/
/* package */static Action0 startCurrentThreadExecutingCommand(HystrixCommandKey key) {
final ConcurrentStack list = currentCommand.get();
try {
list.push(key);
} catch (Exception e) {
logger.warn("Unable to record command starting", e);
}
return new Action0() {
@Override
public void call() {
endCurrentThreadExecutingCommand(list);
}
};
}
/* package */static void endCurrentThreadExecutingCommand() {
endCurrentThreadExecutingCommand(currentCommand.get());
}
private static void endCurrentThreadExecutingCommand(ConcurrentStack list) {
try {
if (!list.isEmpty()) {
list.pop();
}
} catch (NoSuchElementException e) {
// this shouldn't be possible since we check for empty above and this is thread-isolated
logger.debug("No command found to end.", e);
} catch (Exception e) {
logger.warn("Unable to end command.", e);
}
}
/* package-private */ static int getCommandCount() {
return currentCommand.get().size();
}
/**
* Trieber's algorithm for a concurrent stack
* @param
*/
private static class ConcurrentStack {
AtomicReference> top = new AtomicReference>();
public void push(E item) {
Node newHead = new Node(item);
Node oldHead;
do {
oldHead = top.get();
newHead.next = oldHead;
} while (!top.compareAndSet(oldHead, newHead));
}
public E pop() {
Node oldHead;
Node newHead;
do {
oldHead = top.get();
if (oldHead == null) {
return null;
}
newHead = oldHead.next;
} while (!top.compareAndSet(oldHead, newHead));
return oldHead.item;
}
public boolean isEmpty() {
return top.get() == null;
}
public int size() {
int currentSize = 0;
Node current = top.get();
while (current != null) {
currentSize++;
current = current.next;
}
return currentSize;
}
public E peek() {
if (top.get() == null) {
return null;
} else {
return top.get().item;
}
}
private class Node {
public final E item;
public Node next;
public Node(E item) {
this.item = item;
}
}
}
}