/** * Copyright 2015 Netflix, Inc. * <p/> * 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 * <p/> * http://www.apache.org/licenses/LICENSE-2.0 * <p/> * 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.metric; import com.netflix.hystrix.HystrixCollapser; import com.netflix.hystrix.HystrixCollapserKey; import com.netflix.hystrix.HystrixCollapserProperties; import com.netflix.hystrix.HystrixCommand; import com.netflix.hystrix.HystrixCommandGroupKey; import com.netflix.hystrix.HystrixCommandKey; import com.netflix.hystrix.HystrixCommandProperties; import com.netflix.hystrix.HystrixEventType; import com.netflix.hystrix.HystrixThreadPoolKey; import com.netflix.hystrix.HystrixThreadPoolProperties; import com.netflix.hystrix.exception.HystrixBadRequestException; import rx.functions.Func2; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; public abstract class CommandStreamTest { static final AtomicInteger uniqueId = new AtomicInteger(0); public static class Command extends HystrixCommand<Integer> { final String arg; final HystrixEventType executionResult; final int executionLatency; final HystrixEventType fallbackExecutionResult; final int fallbackExecutionLatency; private Command(Setter setter, HystrixEventType executionResult, int executionLatency, String arg, HystrixEventType fallbackExecutionResult, int fallbackExecutionLatency) { super(setter); this.executionResult = executionResult; this.executionLatency = executionLatency; this.fallbackExecutionResult = fallbackExecutionResult; this.fallbackExecutionLatency = fallbackExecutionLatency; this.arg = arg; } public static Command from(HystrixCommandGroupKey groupKey, HystrixCommandKey key, HystrixEventType desiredEventType) { return from(groupKey, key, desiredEventType, 0); } public static Command from(HystrixCommandGroupKey groupKey, HystrixCommandKey key, HystrixEventType desiredEventType, int latency) { return from(groupKey, key, desiredEventType, latency, HystrixCommandProperties.ExecutionIsolationStrategy.THREAD); } public static Command from(HystrixCommandGroupKey groupKey, HystrixCommandKey key, HystrixEventType desiredEventType, int latency, HystrixEventType desiredFallbackEventType) { return from(groupKey, key, desiredEventType, latency, HystrixCommandProperties.ExecutionIsolationStrategy.THREAD, desiredFallbackEventType); } public static Command from(HystrixCommandGroupKey groupKey, HystrixCommandKey key, HystrixEventType desiredEventType, int latency, HystrixEventType desiredFallbackEventType, int fallbackLatency) { return from(groupKey, key, desiredEventType, latency, HystrixCommandProperties.ExecutionIsolationStrategy.THREAD, desiredFallbackEventType, fallbackLatency); } public static Command from(HystrixCommandGroupKey groupKey, HystrixCommandKey key, HystrixEventType desiredEventType, int latency, HystrixCommandProperties.ExecutionIsolationStrategy isolationStrategy) { return from(groupKey, key, desiredEventType, latency, isolationStrategy, HystrixEventType.FALLBACK_SUCCESS, 0); } public static Command from(HystrixCommandGroupKey groupKey, HystrixCommandKey key, HystrixEventType desiredEventType, int latency, HystrixCommandProperties.ExecutionIsolationStrategy isolationStrategy, HystrixEventType desiredFallbackEventType) { return from(groupKey, key, desiredEventType, latency, isolationStrategy, desiredFallbackEventType, 0); } public static Command from(HystrixCommandGroupKey groupKey, HystrixCommandKey key, HystrixEventType desiredEventType, int latency, HystrixCommandProperties.ExecutionIsolationStrategy isolationStrategy, HystrixEventType desiredFallbackEventType, int fallbackLatency) { Setter setter = Setter.withGroupKey(groupKey) .andCommandKey(key) .andCommandPropertiesDefaults(HystrixCommandProperties.Setter() .withExecutionTimeoutInMilliseconds(600) .withExecutionIsolationStrategy(isolationStrategy) .withCircuitBreakerEnabled(true) .withCircuitBreakerRequestVolumeThreshold(3) .withMetricsHealthSnapshotIntervalInMilliseconds(100) .withMetricsRollingStatisticalWindowInMilliseconds(1000) .withMetricsRollingStatisticalWindowBuckets(10) .withRequestCacheEnabled(true) .withRequestLogEnabled(true) .withFallbackIsolationSemaphoreMaxConcurrentRequests(5)) .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey(groupKey.name())) .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter() .withCoreSize(10) .withMaxQueueSize(-1)); String uniqueArg; switch (desiredEventType) { case SUCCESS: uniqueArg = uniqueId.incrementAndGet() + ""; return new Command(setter, HystrixEventType.SUCCESS, latency, uniqueArg, desiredFallbackEventType, 0); case FAILURE: uniqueArg = uniqueId.incrementAndGet() + ""; return new Command(setter, HystrixEventType.FAILURE, latency, uniqueArg, desiredFallbackEventType, fallbackLatency); case TIMEOUT: uniqueArg = uniqueId.incrementAndGet() + ""; return new Command(setter, HystrixEventType.SUCCESS, 700, uniqueArg, desiredFallbackEventType, fallbackLatency); case BAD_REQUEST: uniqueArg = uniqueId.incrementAndGet() + ""; return new Command(setter, HystrixEventType.BAD_REQUEST, latency, uniqueArg, desiredFallbackEventType, 0); case RESPONSE_FROM_CACHE: String arg = uniqueId.get() + ""; return new Command(setter, HystrixEventType.SUCCESS, 0, arg, desiredFallbackEventType, 0); default: throw new RuntimeException("not supported yet"); } } public static List<Command> getCommandsWithResponseFromCache(HystrixCommandGroupKey groupKey, HystrixCommandKey key) { Command cmd1 = Command.from(groupKey, key, HystrixEventType.SUCCESS); Command cmd2 = Command.from(groupKey, key, HystrixEventType.RESPONSE_FROM_CACHE); List<Command> cmds = new ArrayList<Command>(); cmds.add(cmd1); cmds.add(cmd2); return cmds; } @Override protected Integer run() throws Exception { try { Thread.sleep(executionLatency); switch (executionResult) { case SUCCESS: return 1; case FAILURE: throw new RuntimeException("induced failure"); case BAD_REQUEST: throw new HystrixBadRequestException("induced bad request"); default: throw new RuntimeException("unhandled HystrixEventType : " + executionResult); } } catch (InterruptedException ex) { System.out.println("Received InterruptedException : " + ex); throw ex; } } @Override protected Integer getFallback() { try { Thread.sleep(fallbackExecutionLatency); } catch (InterruptedException ex) { throw new RuntimeException(ex); } switch (fallbackExecutionResult) { case FALLBACK_SUCCESS: return -1; case FALLBACK_FAILURE: throw new RuntimeException("induced failure"); case FALLBACK_MISSING: throw new UnsupportedOperationException("fallback not defined"); default: throw new RuntimeException("unhandled HystrixEventType : " + fallbackExecutionResult); } } @Override protected String getCacheKey() { return arg; } } public static class Collapser extends HystrixCollapser<List<Integer>, Integer, Integer> { private final Integer arg; public static Collapser from(Integer arg) { return new Collapser(HystrixCollapserKey.Factory.asKey("Collapser"), arg); } public static Collapser from(HystrixCollapserKey key, Integer arg) { return new Collapser(key, arg); } private Collapser(HystrixCollapserKey key, Integer arg) { super(Setter.withCollapserKey(key) .andCollapserPropertiesDefaults( HystrixCollapserProperties.Setter() .withTimerDelayInMilliseconds(100))); this.arg = arg; } @Override public Integer getRequestArgument() { return arg; } @Override protected HystrixCommand<List<Integer>> createCommand(Collection<CollapsedRequest<Integer, Integer>> collapsedRequests) { List<Integer> args = new ArrayList<Integer>(); for (CollapsedRequest<Integer, Integer> collapsedReq: collapsedRequests) { args.add(collapsedReq.getArgument()); } return new BatchCommand(args); } @Override protected void mapResponseToRequests(List<Integer> batchResponse, Collection<CollapsedRequest<Integer, Integer>> collapsedRequests) { for (CollapsedRequest<Integer, Integer> collapsedReq: collapsedRequests) { collapsedReq.emitResponse(collapsedReq.getArgument()); collapsedReq.setComplete(); } } @Override protected String getCacheKey() { return arg.toString(); } } private static class BatchCommand extends HystrixCommand<List<Integer>> { private List<Integer> args; protected BatchCommand(List<Integer> args) { super(HystrixCommandGroupKey.Factory.asKey("BATCH")); this.args = args; } @Override protected List<Integer> run() throws Exception { System.out.println(Thread.currentThread().getName() + " : Executing batch of : " + args.size()); return args; } } protected static String bucketToString(long[] eventCounts) { StringBuilder sb = new StringBuilder(); sb.append("["); for (HystrixEventType eventType : HystrixEventType.values()) { if (eventCounts[eventType.ordinal()] > 0) { sb.append(eventType.name()).append("->").append(eventCounts[eventType.ordinal()]).append(", "); } } sb.append("]"); return sb.toString(); } protected static boolean hasData(long[] eventCounts) { for (HystrixEventType eventType : HystrixEventType.values()) { if (eventCounts[eventType.ordinal()] > 0) { return true; } } return false; } }