| /** |
| * 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. |
| */ |
| |
| // This file is forked from https://github.com/Netflix/Hystrix/blob/master/hystrix-core/src/main/java/com/netflix/hystrix/HystrixCommandProperties.java |
| |
| package org.apache.servicecomb.bizkeeper; |
| |
| import static com.netflix.hystrix.strategy.properties.HystrixPropertiesChainedProperty.forBoolean; |
| import static com.netflix.hystrix.strategy.properties.HystrixPropertiesChainedProperty.forInteger; |
| import static com.netflix.hystrix.strategy.properties.HystrixPropertiesChainedProperty.forString; |
| |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import com.netflix.hystrix.HystrixCommandKey; |
| import com.netflix.hystrix.HystrixCommandProperties; |
| import com.netflix.hystrix.strategy.properties.HystrixDynamicProperty; |
| import com.netflix.hystrix.strategy.properties.HystrixProperty; |
| |
| public class HystrixCommandPropertiesExt extends HystrixCommandProperties { |
| |
| private static final Logger LOGGER = LoggerFactory.getLogger(HystrixCommandProperties.class); |
| |
| /* defaults */ |
| /* package */ |
| // default => statisticalWindow: 10000 = 10 seconds (and default of 10 |
| // buckets so each bucket is 1 second) |
| static final Integer DEFAULT_METRICSROLLINGSTATISTICALWINDOW = 10000; |
| |
| // default => statisticalWindowBuckets: 10 = 10 buckets in a 10 second |
| // window so each bucket is 1 second |
| private static final Integer DEFAULT_METRICSROLLINGSTATISTICALWINDOWBUCKETS = 10; |
| |
| // default => statisticalWindowVolumeThreshold: 20 requests in 10 seconds |
| // must occur before statistics matter |
| private static final Integer DEFAULT_CIRCUITBREAKERREQUESTVOLUMETHRESHOLD = 20; |
| |
| // default => sleepWindow: 15000 = 15 seconds that we will sleep before trying |
| // again after tripping the circuit |
| private static final Integer DEFAULT_CIRCUITBREAKERSLEEPWINDOWINMILLISECONDS = 15000; |
| |
| // default => errorThresholdPercentage = 50 = if 50%+ of requests in 10 |
| // seconds are failures or latent then we will trip the circuit |
| private static final Integer DEFAULT_CIRCUITBREAKERERRORTHRESHOLDPERCENTAGE = 50; |
| |
| // default => forceCircuitOpen = false (we want to allow traffic) |
| private static final Boolean DEFAULT_CIRCUITBREAKERFORCEOPEN = false; |
| |
| /* package */ |
| // default => ignoreErrors = false |
| static final Boolean DEFAULT_CIRCUITBREAKERFORCECLOSED = false; |
| |
| // default => executionTimeoutInMilliseconds: 30000 = 30 second |
| private static final Integer DEFAULT_EXECUTIONTIMEOUTINMILLISECONDS = 30000; |
| |
| private static final Boolean DEFAULT_EXECUTIONTIMEOUTENABLED = false; |
| |
| private static final ExecutionIsolationStrategy DEFAULT_ISOLATIONSTRATEGY = ExecutionIsolationStrategy.SEMAPHORE; |
| |
| private static final Boolean DEFAULT_EXECUTIONISOLATIONTHREADINTERRUPTONTIMEOUT = true; |
| |
| private static final Boolean DEFAULT_METRICSROLLINGPERCENTILEENABLED = false; |
| |
| private static final Boolean DEFAULT_REQUESTCACHEENABLED = true; |
| |
| private static final Integer DEFAULT_FALLBACKISOLATIONSEMAPHOREMAXCONCURRENTREQUESTS = 10; |
| |
| private static final Boolean DEFAULT_FALLBACKENABLED = true; |
| |
| private static final Integer DEFAULT_EXECUTIONISOLATIONSEMAPHOREMAXCONCURRENTREQUESTS = 1000; |
| |
| private static final Boolean DEFAULT_REQUESTLOGENABLED = true; |
| |
| private static final Boolean DEFAULT_CIRCUITBREAKERENABLED = true; |
| |
| // default to 1 minute for RollingPercentile |
| private static final Integer DEFAULT_METRICSROLLINGPERCENTILEWINDOW = 60000; |
| |
| // default to 6 buckets (10 seconds each in 60 second window) |
| private static final Integer DEFAULT_METRICSROLLINGPERCENTILEWINDOWBUCKETS = 6; |
| |
| // default to 100 values max per bucket |
| private static final Integer DEFAULT_METRICSROLLINGPERCENTILEBUCKETSIZE = 100; |
| |
| // default to 1000ms as max frequency between allowing snapshots of health |
| // (error percentage etc) |
| private static final Integer DEFAULT_METRICSHEALTHSNAPSHOTINTERVALINMILLISECONDS = 1000; |
| |
| private static final int COMMAND_KEY_LENGTH = 3; |
| |
| @SuppressWarnings("unused") |
| private final HystrixCommandKey key; |
| |
| // number of requests that must be made within a statisticalWindow before |
| // open/close decisions are made using stats |
| private final HystrixProperty<Integer> circuitBreakerRequestVolumeThreshold; |
| |
| // milliseconds after tripping circuit before allowing retry |
| private final HystrixProperty<Integer> circuitBreakerSleepWindowInMilliseconds; |
| |
| // Whether circuit breaker should be enabled. |
| private final HystrixProperty<Boolean> circuitBreakerEnabled; |
| |
| // % of 'marks' that must be failed to trip the circuit |
| private final HystrixProperty<Integer> circuitBreakerErrorThresholdPercentage; |
| |
| // a property to allow forcing the circuit open (stopping all requests) |
| private final HystrixProperty<Boolean> circuitBreakerForceOpen; |
| |
| // a property to allow ignoring errors and therefore never trip 'open' (ie. |
| // allow all traffic through) |
| private final HystrixProperty<Boolean> circuitBreakerForceClosed; |
| |
| // Whether a command should be executed in a separate thread or not. |
| private final HystrixProperty<ExecutionIsolationStrategy> executionIsolationStrategy; |
| |
| // Timeout value in milliseconds for a command |
| private final HystrixProperty<Integer> executionTimeoutInMilliseconds; |
| |
| // Whether timeout should be triggered |
| private final HystrixProperty<Boolean> executionTimeoutEnabled; |
| |
| // What thread-pool this command should run in (if running on a separate |
| // thread). |
| private final HystrixProperty<String> executionIsolationThreadPoolKeyOverride; |
| |
| // Number of permits for execution semaphore |
| private final HystrixProperty<Integer> executionIsolationSemaphoreMaxConcurrentRequests; |
| |
| // Number of permits for fallback semaphore |
| private final HystrixProperty<Integer> fallbackIsolationSemaphoreMaxConcurrentRequests; |
| |
| // Whether fallback should be attempted. |
| private final HystrixProperty<Boolean> fallbackEnabled; |
| |
| // Whether an underlying Future/Thread (when runInSeparateThread == true) |
| // should be interrupted after a timeout |
| private final HystrixProperty<Boolean> executionIsolationThreadInterruptOnTimeout; |
| |
| // milliseconds back that will be tracked |
| private final HystrixProperty<Integer> metricsRollingStatisticalWindowInMilliseconds; |
| |
| // number of buckets in the statisticalWindow |
| private final HystrixProperty<Integer> metricsRollingStatisticalWindowBuckets; |
| |
| // Whether monitoring should be enabled (SLA and Tracers). |
| private final HystrixProperty<Boolean> metricsRollingPercentileEnabled; |
| |
| // number of milliseconds that will be tracked in RollingPercentile |
| private final HystrixProperty<Integer> metricsRollingPercentileWindowInMilliseconds; |
| |
| // number of buckets percentileWindow will be divided into |
| private final HystrixProperty<Integer> metricsRollingPercentileWindowBuckets; |
| |
| // how many values will be stored in each percentileWindowBucket |
| private final HystrixProperty<Integer> metricsRollingPercentileBucketSize; |
| |
| // time between health snapshots |
| private final HystrixProperty<Integer> metricsHealthSnapshotIntervalInMilliseconds; |
| |
| // whether command request logging is enabled. |
| private final HystrixProperty<Boolean> requestLogEnabled; |
| |
| // Whether request caching is enabled. |
| private final HystrixProperty<Boolean> requestCacheEnabled; |
| |
| protected HystrixCommandPropertiesExt(HystrixCommandKey key) { |
| this(key, HystrixCommandProperties.Setter(), "servicecomb"); |
| } |
| |
| protected HystrixCommandPropertiesExt(HystrixCommandKey key, HystrixCommandProperties.Setter builder) { |
| this(key, builder, "servicecomb"); |
| } |
| |
| protected HystrixCommandPropertiesExt(HystrixCommandKey key, HystrixCommandProperties.Setter builder, |
| String propertyPrefix) { |
| super(key, builder, propertyPrefix); |
| this.key = key; |
| this.circuitBreakerEnabled = getProperty(propertyPrefix, |
| "circuitBreaker", |
| key, |
| "enabled", |
| builder.getCircuitBreakerEnabled(), |
| DEFAULT_CIRCUITBREAKERENABLED); |
| this.circuitBreakerRequestVolumeThreshold = getProperty(propertyPrefix, |
| "circuitBreaker", |
| key, |
| "requestVolumeThreshold", |
| builder.getCircuitBreakerRequestVolumeThreshold(), |
| DEFAULT_CIRCUITBREAKERREQUESTVOLUMETHRESHOLD); |
| this.circuitBreakerSleepWindowInMilliseconds = getProperty(propertyPrefix, |
| "circuitBreaker", |
| key, |
| "sleepWindowInMilliseconds", |
| builder.getCircuitBreakerSleepWindowInMilliseconds(), |
| DEFAULT_CIRCUITBREAKERSLEEPWINDOWINMILLISECONDS); |
| this.circuitBreakerErrorThresholdPercentage = getProperty(propertyPrefix, |
| "circuitBreaker", |
| key, |
| "errorThresholdPercentage", |
| builder.getCircuitBreakerErrorThresholdPercentage(), |
| DEFAULT_CIRCUITBREAKERERRORTHRESHOLDPERCENTAGE); |
| this.circuitBreakerForceOpen = getProperty(propertyPrefix, |
| "circuitBreaker", |
| key, |
| "forceOpen", |
| builder.getCircuitBreakerForceOpen(), |
| DEFAULT_CIRCUITBREAKERFORCEOPEN); |
| this.circuitBreakerForceClosed = getProperty(propertyPrefix, |
| "circuitBreaker", |
| key, |
| "forceClosed", |
| builder.getCircuitBreakerForceClosed(), |
| DEFAULT_CIRCUITBREAKERFORCECLOSED); |
| this.executionIsolationStrategy = getProperty(propertyPrefix, |
| "isolation", |
| key, |
| "strategy", |
| builder.getExecutionIsolationStrategy(), |
| DEFAULT_ISOLATIONSTRATEGY); |
| this.executionTimeoutInMilliseconds = getProperty(propertyPrefix, |
| "isolation", |
| key, |
| "timeoutInMilliseconds", |
| builder.getExecutionTimeoutInMilliseconds(), |
| DEFAULT_EXECUTIONTIMEOUTINMILLISECONDS); |
| this.executionTimeoutEnabled = getProperty(propertyPrefix, |
| "isolation", |
| key, |
| "timeout.enabled", |
| builder.getExecutionTimeoutEnabled(), |
| DEFAULT_EXECUTIONTIMEOUTENABLED); |
| this.executionIsolationThreadInterruptOnTimeout = getProperty(propertyPrefix, |
| "isolation", |
| key, |
| "interruptOnTimeout", |
| builder.getExecutionIsolationThreadInterruptOnTimeout(), |
| DEFAULT_EXECUTIONISOLATIONTHREADINTERRUPTONTIMEOUT); |
| this.executionIsolationSemaphoreMaxConcurrentRequests = getProperty(propertyPrefix, |
| "isolation", |
| key, |
| "maxConcurrentRequests", |
| builder.getExecutionIsolationSemaphoreMaxConcurrentRequests(), |
| DEFAULT_EXECUTIONISOLATIONSEMAPHOREMAXCONCURRENTREQUESTS); |
| this.fallbackIsolationSemaphoreMaxConcurrentRequests = getProperty(propertyPrefix, |
| "fallback", |
| key, |
| "maxConcurrentRequests", |
| builder.getFallbackIsolationSemaphoreMaxConcurrentRequests(), |
| DEFAULT_FALLBACKISOLATIONSEMAPHOREMAXCONCURRENTREQUESTS); |
| this.fallbackEnabled = getProperty(propertyPrefix, |
| "fallback", |
| key, |
| "enabled", |
| builder.getFallbackEnabled(), |
| DEFAULT_FALLBACKENABLED); |
| this.metricsRollingStatisticalWindowInMilliseconds = getProperty(propertyPrefix, |
| "metrics", |
| key, |
| "rollingStats.timeInMilliseconds", |
| builder.getMetricsRollingStatisticalWindowInMilliseconds(), |
| DEFAULT_METRICSROLLINGSTATISTICALWINDOW); |
| this.metricsRollingStatisticalWindowBuckets = getProperty(propertyPrefix, |
| "metrics", |
| key, |
| "rollingStats.numBuckets", |
| builder.getMetricsRollingStatisticalWindowBuckets(), |
| DEFAULT_METRICSROLLINGSTATISTICALWINDOWBUCKETS); |
| this.metricsRollingPercentileEnabled = getProperty(propertyPrefix, |
| "metrics", |
| key, |
| "rollingPercentile.enabled", |
| builder.getMetricsRollingPercentileEnabled(), |
| DEFAULT_METRICSROLLINGPERCENTILEENABLED); |
| this.metricsRollingPercentileWindowInMilliseconds = getProperty(propertyPrefix, |
| "metrics", |
| key, |
| "rollingPercentile.timeInMilliseconds", |
| builder.getMetricsRollingPercentileWindowInMilliseconds(), |
| DEFAULT_METRICSROLLINGPERCENTILEWINDOW); |
| this.metricsRollingPercentileWindowBuckets = getProperty(propertyPrefix, |
| "metrics", |
| key, |
| "rollingPercentile.numBuckets", |
| builder.getMetricsRollingPercentileWindowBuckets(), |
| DEFAULT_METRICSROLLINGPERCENTILEWINDOWBUCKETS); |
| this.metricsRollingPercentileBucketSize = getProperty(propertyPrefix, |
| "metrics", |
| key, |
| "rollingPercentile.bucketSize", |
| builder.getMetricsRollingPercentileBucketSize(), |
| DEFAULT_METRICSROLLINGPERCENTILEBUCKETSIZE); |
| this.metricsHealthSnapshotIntervalInMilliseconds = getProperty(propertyPrefix, |
| "metrics", |
| key, |
| "healthSnapshot.intervalInMilliseconds", |
| builder.getMetricsHealthSnapshotIntervalInMilliseconds(), |
| DEFAULT_METRICSHEALTHSNAPSHOTINTERVALINMILLISECONDS); |
| this.requestCacheEnabled = getProperty(propertyPrefix, |
| "requestCache", |
| key, |
| "enabled", |
| builder.getRequestCacheEnabled(), |
| DEFAULT_REQUESTCACHEENABLED); |
| this.requestLogEnabled = getProperty(propertyPrefix, |
| "requestLog", |
| key, |
| "enabled", |
| builder.getRequestLogEnabled(), |
| DEFAULT_REQUESTLOGENABLED); |
| |
| // threadpool doesn't have a global override, only instance level makes |
| // sense |
| this.executionIsolationThreadPoolKeyOverride = forString() |
| .add(propertyPrefix + ".command." + key.name() + ".threadPoolKeyOverride", null) |
| .build(); |
| } |
| |
| @Override |
| public HystrixProperty<Boolean> circuitBreakerEnabled() { |
| return circuitBreakerEnabled; |
| } |
| |
| @Override |
| public HystrixProperty<Integer> circuitBreakerErrorThresholdPercentage() { |
| return circuitBreakerErrorThresholdPercentage; |
| } |
| |
| @Override |
| public HystrixProperty<Boolean> circuitBreakerForceClosed() { |
| return circuitBreakerForceClosed; |
| } |
| |
| @Override |
| public HystrixProperty<Boolean> circuitBreakerForceOpen() { |
| return circuitBreakerForceOpen; |
| } |
| |
| @Override |
| public HystrixProperty<Integer> circuitBreakerRequestVolumeThreshold() { |
| return circuitBreakerRequestVolumeThreshold; |
| } |
| |
| @Override |
| public HystrixProperty<Integer> circuitBreakerSleepWindowInMilliseconds() { |
| return circuitBreakerSleepWindowInMilliseconds; |
| } |
| |
| @Override |
| public HystrixProperty<Integer> executionIsolationSemaphoreMaxConcurrentRequests() { |
| return executionIsolationSemaphoreMaxConcurrentRequests; |
| } |
| |
| @Override |
| public HystrixProperty<ExecutionIsolationStrategy> executionIsolationStrategy() { |
| return executionIsolationStrategy; |
| } |
| |
| @Override |
| public HystrixProperty<Boolean> executionIsolationThreadInterruptOnTimeout() { |
| return executionIsolationThreadInterruptOnTimeout; |
| } |
| |
| @Override |
| public HystrixProperty<String> executionIsolationThreadPoolKeyOverride() { |
| return executionIsolationThreadPoolKeyOverride; |
| } |
| |
| @Override |
| @Deprecated // prefer {@link #executionTimeoutInMilliseconds} |
| public HystrixProperty<Integer> executionIsolationThreadTimeoutInMilliseconds() { |
| return executionTimeoutInMilliseconds; |
| } |
| |
| @Override |
| public HystrixProperty<Integer> executionTimeoutInMilliseconds() { |
| return executionIsolationThreadTimeoutInMilliseconds(); |
| } |
| |
| @Override |
| public HystrixProperty<Boolean> executionTimeoutEnabled() { |
| return executionTimeoutEnabled; |
| } |
| |
| @Override |
| public HystrixProperty<Integer> fallbackIsolationSemaphoreMaxConcurrentRequests() { |
| return fallbackIsolationSemaphoreMaxConcurrentRequests; |
| } |
| |
| @Override |
| public HystrixProperty<Boolean> fallbackEnabled() { |
| return fallbackEnabled; |
| } |
| |
| @Override |
| public HystrixProperty<Integer> metricsHealthSnapshotIntervalInMilliseconds() { |
| return metricsHealthSnapshotIntervalInMilliseconds; |
| } |
| |
| @Override |
| public HystrixProperty<Integer> metricsRollingPercentileBucketSize() { |
| return metricsRollingPercentileBucketSize; |
| } |
| |
| @Override |
| public HystrixProperty<Boolean> metricsRollingPercentileEnabled() { |
| return metricsRollingPercentileEnabled; |
| } |
| |
| @Override |
| @Deprecated |
| public HystrixProperty<Integer> metricsRollingPercentileWindow() { |
| return metricsRollingPercentileWindowInMilliseconds; |
| } |
| |
| @Override |
| public HystrixProperty<Integer> metricsRollingPercentileWindowInMilliseconds() { |
| return metricsRollingPercentileWindowInMilliseconds; |
| } |
| |
| @Override |
| public HystrixProperty<Integer> metricsRollingPercentileWindowBuckets() { |
| return metricsRollingPercentileWindowBuckets; |
| } |
| |
| @Override |
| public HystrixProperty<Integer> metricsRollingStatisticalWindowInMilliseconds() { |
| return metricsRollingStatisticalWindowInMilliseconds; |
| } |
| |
| @Override |
| public HystrixProperty<Integer> metricsRollingStatisticalWindowBuckets() { |
| return metricsRollingStatisticalWindowBuckets; |
| } |
| |
| @Override |
| public HystrixProperty<Boolean> requestCacheEnabled() { |
| return requestCacheEnabled; |
| } |
| |
| @Override |
| public HystrixProperty<Boolean> requestLogEnabled() { |
| return requestLogEnabled; |
| } |
| |
| private HystrixProperty<ExecutionIsolationStrategy> getProperty(String propertyPrefix, String command, |
| HystrixCommandKey key, String instanceProperty, ExecutionIsolationStrategy builderOverrideValue, |
| ExecutionIsolationStrategy defaultValue) { |
| return new ExecutionIsolationStrategyHystrixProperty(builderOverrideValue, key, propertyPrefix, command, |
| defaultValue, instanceProperty); |
| } |
| |
| private static final class ExecutionIsolationStrategyHystrixProperty |
| implements HystrixProperty<ExecutionIsolationStrategy> { |
| private final HystrixDynamicProperty<String> property; |
| |
| private volatile ExecutionIsolationStrategy value; |
| |
| private final ExecutionIsolationStrategy defaultValue; |
| |
| private ExecutionIsolationStrategyHystrixProperty(ExecutionIsolationStrategy builderOverrideValue, |
| HystrixCommandKey key, String propertyPrefix, String command, ExecutionIsolationStrategy defaultValue, |
| String instanceProperty) { |
| this.defaultValue = defaultValue; |
| String overrideValue = null; |
| if (builderOverrideValue != null) { |
| overrideValue = builderOverrideValue.name(); |
| } |
| property = forString() |
| .add(propertyPrefix + "." + command + "." + key.name() + "." + instanceProperty, null) |
| .add(propertyPrefix + "." + command + "." + serviceKey(key.name()) + "." + instanceProperty, |
| overrideValue) |
| .add(propertyPrefix + "." + command + "." + typeKey(key.name()) + "." + instanceProperty, |
| defaultValue.name()) |
| .build(); |
| |
| // initialize the enum value from the property |
| parseProperty(); |
| |
| // use a callback to handle changes so we only handle the parse cost |
| // on updates rather than every fetch |
| // when the property value changes we'll update the value |
| property.addCallback(this::parseProperty); |
| } |
| |
| @Override |
| public ExecutionIsolationStrategy get() { |
| return value; |
| } |
| |
| private void parseProperty() { |
| try { |
| value = ExecutionIsolationStrategy.valueOf(property.get()); |
| } catch (Exception e) { |
| LOGGER.error("Unable to derive ExecutionIsolationStrategy from property value: " + property.get(), e); |
| // use the default value |
| value = defaultValue; |
| } |
| } |
| } |
| |
| private static String serviceKey(String key) { |
| String[] keyparts = key.split("\\.", COMMAND_KEY_LENGTH); |
| if (keyparts.length == COMMAND_KEY_LENGTH) { |
| return keyparts[0] + "." + keyparts[1]; |
| } |
| return key; |
| } |
| |
| private static String typeKey(String key) { |
| int index = key.indexOf("."); |
| if (index > 0) { |
| return key.substring(0, index); |
| } |
| return key; |
| } |
| |
| private HystrixProperty<Integer> getProperty(String propertyPrefix, String command, HystrixCommandKey key, |
| String instanceProperty, Integer builderOverrideValue, |
| Integer defaultValue) { |
| return forInteger() |
| .add(propertyPrefix + "." + command + "." + key.name() + "." + instanceProperty, null) |
| .add(propertyPrefix + "." + command + "." + serviceKey(key.name()) + "." + instanceProperty, |
| null) |
| .add(propertyPrefix + "." + command + "." + typeKey(key.name()) + "." + instanceProperty, |
| builderOverrideValue == null ? defaultValue : builderOverrideValue) |
| .build(); |
| } |
| |
| private HystrixProperty<Boolean> getProperty(String propertyPrefix, String command, HystrixCommandKey key, |
| String instanceProperty, Boolean builderOverrideValue, Boolean defaultValue) { |
| return forBoolean() |
| .add(propertyPrefix + "." + command + "." + key.name() + "." + instanceProperty, null) |
| .add(propertyPrefix + "." + command + "." + serviceKey(key.name()) + "." + instanceProperty, |
| null) |
| .add(propertyPrefix + "." + command + "." + typeKey(key.name()) + "." + instanceProperty, |
| builderOverrideValue == null ? defaultValue : builderOverrideValue) |
| .build(); |
| } |
| } |