blob: 1d1bdfeec79127b4b862c165d0b10c7cfd57264a [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.servicecomb.governance.policy;
import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.apache.servicecomb.governance.utils.GovernanceUtils;
import org.springframework.util.CollectionUtils;
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig.SlidingWindowType;
public class CircuitBreakerPolicy extends AbstractPolicy {
public static final float DEFAULT_FAILURE_RATE_THRESHOLD = 50;
public static final float DEFAULT_SLOW_CALL_RATE_THRESHOLD = 100;
public static final Duration DEFAULT_WAIT_DURATION_IN_OPEN_STATUS = Duration.ofMillis(60000);
//ms
public static final Duration DEFAULT_SLOW_CALL_DURATION_THRESHOLD = Duration.ofMillis(60000);
// the number of permitted calls when the CircuitBreaker is half open.
public static final int DEFAULT_PERMITTED = 10;
public static final int DEFAULT_MINIMUM_NUMBER_CALLS = 100;
public static final String DEFAULT_SLIDING_WINDOW_SIZE = "100";
public static final String DEFAULT_FAILURE_RESPONSE_STATUS_502 = "502";
public static final String DEFAULT_FAILURE_RESPONSE_STATUS_503 = "503";
public static final List<String> DEFAULT_STATUS_LIST = Arrays.asList(DEFAULT_FAILURE_RESPONSE_STATUS_502,
DEFAULT_FAILURE_RESPONSE_STATUS_503);
private float failureRateThreshold = DEFAULT_FAILURE_RATE_THRESHOLD;
private float slowCallRateThreshold = DEFAULT_SLOW_CALL_RATE_THRESHOLD;
private String waitDurationInOpenState = DEFAULT_WAIT_DURATION_IN_OPEN_STATUS.toString();
private String slowCallDurationThreshold = DEFAULT_SLOW_CALL_DURATION_THRESHOLD.toString();
private int permittedNumberOfCallsInHalfOpenState = DEFAULT_PERMITTED;
private int minimumNumberOfCalls = DEFAULT_MINIMUM_NUMBER_CALLS;
private String slidingWindowType;
private String slidingWindowSize = DEFAULT_SLIDING_WINDOW_SIZE;
//status code that need record as a failure
private List<String> recordFailureStatus = DEFAULT_STATUS_LIST;
//force close this circuit breaker. This parameter is not used by circuit breaker directly
private boolean forceClosed = false;
//force open this circuit breaker. This parameter is not used by circuit breaker directly
private boolean forceOpen = false;
public CircuitBreakerPolicy() {
}
@Override
public boolean isValid() {
if (failureRateThreshold > 100.0F || failureRateThreshold <= 0.0F) {
return false;
}
if (slowCallRateThreshold > 100.0F || slowCallRateThreshold <= 0.0F) {
return false;
}
if (Duration.parse(waitDurationInOpenState).toMillis() <= 0) {
return false;
}
if (Duration.parse(slowCallDurationThreshold).toMillis() <= 0) {
return false;
}
if (permittedNumberOfCallsInHalfOpenState <= 0) {
return false;
}
if (minimumNumberOfCalls <= 0) {
return false;
}
return super.isValid();
}
public float getFailureRateThreshold() {
return failureRateThreshold;
}
public void setFailureRateThreshold(float failureRateThreshold) {
this.failureRateThreshold = failureRateThreshold;
}
public float getSlowCallRateThreshold() {
return slowCallRateThreshold;
}
public void setSlowCallRateThreshold(float slowCallRateThreshold) {
this.slowCallRateThreshold = slowCallRateThreshold;
}
public String getWaitDurationInOpenState() {
return waitDurationInOpenState;
}
public void setWaitDurationInOpenState(String waitDurationInOpenState) {
this.waitDurationInOpenState = stringOfDuration(waitDurationInOpenState, DEFAULT_WAIT_DURATION_IN_OPEN_STATUS);
}
public String getSlowCallDurationThreshold() {
return slowCallDurationThreshold;
}
public void setSlowCallDurationThreshold(String slowCallDurationThreshold) {
this.slowCallDurationThreshold = stringOfDuration(slowCallDurationThreshold, DEFAULT_SLOW_CALL_DURATION_THRESHOLD);
}
public int getPermittedNumberOfCallsInHalfOpenState() {
return permittedNumberOfCallsInHalfOpenState;
}
public void setPermittedNumberOfCallsInHalfOpenState(int permittedNumberOfCallsInHalfOpenState) {
this.permittedNumberOfCallsInHalfOpenState = permittedNumberOfCallsInHalfOpenState;
}
public int getMinimumNumberOfCalls() {
return minimumNumberOfCalls;
}
public void setMinimumNumberOfCalls(int minimumNumberOfCalls) {
this.minimumNumberOfCalls = minimumNumberOfCalls;
}
public SlidingWindowType getSlidingWindowTypeEnum() {
if (StringUtils.isEmpty(slidingWindowType)) {
return SlidingWindowType.TIME_BASED;
}
try {
return SlidingWindowType.valueOf(slidingWindowType);
} catch (Exception e) {
return SlidingWindowType.TIME_BASED;
}
}
public String getSlidingWindowType() {
return this.slidingWindowType;
}
public void setSlidingWindowType(String slidingWindowType) {
this.slidingWindowType = slidingWindowType;
}
// time's unit is second
public String getSlidingWindowSize() {
return slidingWindowSize;
}
public void setSlidingWindowSize(String slidingWindowSize) {
this.slidingWindowSize = getValue(slidingWindowSize);
}
private String getValue(String slidingWindowSize) {
if (StringUtils.isEmpty(slidingWindowSize)) {
return DEFAULT_SLIDING_WINDOW_SIZE;
}
if (slidingWindowSize.matches(GovernanceUtils.DIGIT_REGEX)) {
if (Long.parseLong(slidingWindowSize) < 0) {
throw new RuntimeException("The value should be more than 0.");
}
return slidingWindowSize;
}
Duration duration = Duration.parse(GovernanceUtils.DIGIT_PREFIX + slidingWindowSize);
return String.valueOf(duration.getSeconds());
}
public List<String> getRecordFailureStatus() {
if (CollectionUtils.isEmpty(this.recordFailureStatus)) {
return DEFAULT_STATUS_LIST;
}
return this.recordFailureStatus;
}
public void setRecordFailureStatus(List<String> recordFailureStatus) {
if (recordFailureStatus == null) {
return;
}
this.recordFailureStatus = recordFailureStatus.stream().filter(e -> !StringUtils.isEmpty(e))
.collect(Collectors.toList());
}
public boolean isForceClosed() {
return forceClosed;
}
public void setForceClosed(boolean forceClosed) {
this.forceClosed = forceClosed;
}
public boolean isForceOpen() {
return forceOpen;
}
public void setForceOpen(boolean forceOpen) {
this.forceOpen = forceOpen;
}
@Override
public String toString() {
return "CircuitBreakerPolicy{" +
"failureRateThreshold=" + failureRateThreshold +
", slowCallRateThreshold=" + slowCallRateThreshold +
", waitDurationInOpenState=" + waitDurationInOpenState +
", slowCallDurationThreshold=" + slowCallDurationThreshold +
", permittedNumberOfCallsInHalfOpenState=" + permittedNumberOfCallsInHalfOpenState +
", minimumNumberOfCalls=" + minimumNumberOfCalls +
", slidingWindowType='" + slidingWindowType + '\'' +
", slidingWindowSize=" + slidingWindowSize +
", recordFailureStatus=" + recordFailureStatus +
'}';
}
}