com.netflix.hystrix.HystrixObservableCommand 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;
import rx.Observable;
import com.netflix.hystrix.HystrixCommandProperties.ExecutionIsolationStrategy;
import com.netflix.hystrix.strategy.executionhook.HystrixCommandExecutionHook;
import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy;
import com.netflix.hystrix.strategy.eventnotifier.HystrixEventNotifier;
/**
* Used to wrap code that will execute potentially risky functionality (typically meaning a service call over the network)
* with fault and latency tolerance, statistics and performance metrics capture, circuit breaker and bulkhead functionality.
* This command should be used for a purely non-blocking call pattern. The caller of this command will be subscribed to the Observable returned by the run() method.
*
* @param
* the return type
*
* @ThreadSafe
*/
public abstract class HystrixObservableCommand extends AbstractCommand implements HystrixObservable, HystrixInvokableInfo {
/**
* Construct a {@link HystrixObservableCommand} with defined {@link HystrixCommandGroupKey}.
*
* The {@link HystrixCommandKey} will be derived from the implementing class name.
*
* @param group
* {@link HystrixCommandGroupKey} used to group together multiple {@link HystrixObservableCommand} objects.
*
* The {@link HystrixCommandGroupKey} is used to represent a common relationship between commands. For example, a library or team name, the system all related commands interace with,
* common business purpose etc.
*/
protected HystrixObservableCommand(HystrixCommandGroupKey group) {
// use 'null' to specify use the default
this(new Setter(group));
}
/**
*
* Overridden to true so that all onNext emissions are captured
*
* @return if onNext events should be reported on
* This affects {@link HystrixRequestLog}, and {@link HystrixEventNotifier} currently. Metrics/Hooks later
*/
@Override
protected boolean shouldOutputOnNextEvents() {
return true;
}
@Override
protected String getFallbackMethodName() {
return "resumeWithFallback";
}
@Override
protected boolean isFallbackUserDefined() {
Boolean containsFromMap = commandContainsFallback.get(commandKey);
if (containsFromMap != null) {
return containsFromMap;
} else {
Boolean toInsertIntoMap;
try {
getClass().getDeclaredMethod("resumeWithFallback");
toInsertIntoMap = true;
} catch (NoSuchMethodException nsme) {
toInsertIntoMap = false;
}
commandContainsFallback.put(commandKey, toInsertIntoMap);
return toInsertIntoMap;
}
}
@Override
protected boolean commandIsScalar() {
return false;
}
/**
* Construct a {@link HystrixObservableCommand} with defined {@link Setter} that allows injecting property and strategy overrides and other optional arguments.
*
* NOTE: The {@link HystrixCommandKey} is used to associate a {@link HystrixObservableCommand} with {@link HystrixCircuitBreaker}, {@link HystrixCommandMetrics} and other objects.
*
* Do not create multiple {@link HystrixObservableCommand} implementations with the same {@link HystrixCommandKey} but different injected default properties as the first instantiated will win.
*
* Properties passed in via {@link Setter#andCommandPropertiesDefaults} are cached for the given {@link HystrixCommandKey} for the life of the JVM
* or until {@link Hystrix#reset()} is called. Dynamic properties allow runtime changes. Read more on the Hystrix Wiki.
*
* @param setter
* Fluent interface for constructor arguments
*/
protected HystrixObservableCommand(Setter setter) {
// use 'null' to specify use the default
this(setter.groupKey, setter.commandKey, setter.threadPoolKey, null, null, setter.commandPropertiesDefaults, setter.threadPoolPropertiesDefaults, null, null, null, null, null);
}
/**
* Allow constructing a {@link HystrixObservableCommand} with injection of most aspects of its functionality.
*
* Some of these never have a legitimate reason for injection except in unit testing.
*
* Most of the args will revert to a valid default if 'null' is passed in.
*/
HystrixObservableCommand(HystrixCommandGroupKey group, HystrixCommandKey key, HystrixThreadPoolKey threadPoolKey, HystrixCircuitBreaker circuitBreaker, HystrixThreadPool threadPool,
HystrixCommandProperties.Setter commandPropertiesDefaults, HystrixThreadPoolProperties.Setter threadPoolPropertiesDefaults,
HystrixCommandMetrics metrics, TryableSemaphore fallbackSemaphore, TryableSemaphore executionSemaphore,
HystrixPropertiesStrategy propertiesStrategy, HystrixCommandExecutionHook executionHook) {
super(group, key, threadPoolKey, circuitBreaker, threadPool, commandPropertiesDefaults, threadPoolPropertiesDefaults, metrics, fallbackSemaphore, executionSemaphore, propertiesStrategy, executionHook);
}
/**
* Fluent interface for arguments to the {@link HystrixObservableCommand} constructor.
*
* The required arguments are set via the 'with' factory method and optional arguments via the 'and' chained methods.
*
* Example:
*
{@code
* Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("GroupName"))
.andCommandKey(HystrixCommandKey.Factory.asKey("CommandName"))
.andEventNotifier(notifier);
* }
*
* @NotThreadSafe
*/
final public static class Setter {
protected final HystrixCommandGroupKey groupKey;
protected HystrixCommandKey commandKey;
protected HystrixThreadPoolKey threadPoolKey;
protected HystrixCommandProperties.Setter commandPropertiesDefaults;
protected HystrixThreadPoolProperties.Setter threadPoolPropertiesDefaults;
/**
* Setter factory method containing required values.
*
* All optional arguments can be set via the chained methods.
*
* @param groupKey
* {@link HystrixCommandGroupKey} used to group together multiple {@link HystrixObservableCommand} objects.
*
* The {@link HystrixCommandGroupKey} is used to represent a common relationship between commands. For example, a library or team name, the system all related commands interace
* with,
* common business purpose etc.
*/
protected Setter(HystrixCommandGroupKey groupKey) {
this.groupKey = groupKey;
// default to using SEMAPHORE for ObservableCommand
commandPropertiesDefaults = setDefaults(HystrixCommandProperties.Setter());
}
/**
* Setter factory method with required values.
*
* All optional arguments can be set via the chained methods.
*
* @param groupKey
* {@link HystrixCommandGroupKey} used to group together multiple {@link HystrixObservableCommand} objects.
*
* The {@link HystrixCommandGroupKey} is used to represent a common relationship between commands. For example, a library or team name, the system all related commands interace
* with,
* common business purpose etc.
*/
public static Setter withGroupKey(HystrixCommandGroupKey groupKey) {
return new Setter(groupKey);
}
/**
* @param commandKey
* {@link HystrixCommandKey} used to identify a {@link HystrixObservableCommand} instance for statistics, circuit-breaker, properties, etc.
*
* By default this will be derived from the instance class name.
*
* NOTE: Every unique {@link HystrixCommandKey} will result in new instances of {@link HystrixCircuitBreaker}, {@link HystrixCommandMetrics} and {@link HystrixCommandProperties}.
* Thus,
* the number of variants should be kept to a finite and reasonable number to avoid high-memory usage or memory leacks.
*
* Hundreds of keys is fine, tens of thousands is probably not.
* @return Setter for fluent interface via method chaining
*/
public Setter andCommandKey(HystrixCommandKey commandKey) {
this.commandKey = commandKey;
return this;
}
/**
* Optional
*
* @param commandPropertiesDefaults
* {@link HystrixCommandProperties.Setter} with property overrides for this specific instance of {@link HystrixObservableCommand}.
*
* See the {@link HystrixPropertiesStrategy} JavaDocs for more information on properties and order of precedence.
* @return Setter for fluent interface via method chaining
*/
public Setter andCommandPropertiesDefaults(HystrixCommandProperties.Setter commandPropertiesDefaults) {
this.commandPropertiesDefaults = setDefaults(commandPropertiesDefaults);
return this;
}
private HystrixCommandProperties.Setter setDefaults(HystrixCommandProperties.Setter commandPropertiesDefaults) {
if (commandPropertiesDefaults.getExecutionIsolationStrategy() == null) {
// default to using SEMAPHORE for ObservableCommand if the user didn't set it
commandPropertiesDefaults.withExecutionIsolationStrategy(ExecutionIsolationStrategy.SEMAPHORE);
}
return commandPropertiesDefaults;
}
}
/**
* Implement this method with code to be executed when {@link #observe()} or {@link #toObservable()} are invoked.
*
* @return R response type
*/
protected abstract Observable construct();
/**
* If {@link #observe()} or {@link #toObservable()} fails in any way then this method will be invoked to provide an opportunity to return a fallback response.
*
* This should do work that does not require network transport to produce.
*
* In other words, this should be a static or cached result that can immediately be returned upon failure.
*
* If network traffic is wanted for fallback (such as going to MemCache) then the fallback implementation should invoke another {@link HystrixObservableCommand} instance that protects against
* that network
* access and possibly has another level of fallback that does not involve network access.
*
* DEFAULT BEHAVIOR: It throws UnsupportedOperationException.
*
* @return R or UnsupportedOperationException if not implemented
*/
protected Observable resumeWithFallback() {
return Observable.error(new UnsupportedOperationException("No fallback available."));
}
@Override
final protected Observable getExecutionObservable() {
return construct();
}
@Override
final protected Observable getFallbackObservable() {
return resumeWithFallback();
}
}