LoadBalancerProperties.java
/*
* Copyright 2012-present the original author or authors.
*
* 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
*
* https://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.springframework.cloud.client.loadbalancer;
import java.io.IOException;
import java.time.Duration;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeoutException;
import reactor.util.retry.RetryBackoffSpec;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer;
import org.springframework.cloud.commons.util.IdUtils;
import org.springframework.core.env.PropertyResolver;
import org.springframework.http.HttpMethod;
import org.springframework.util.LinkedCaseInsensitiveMap;
import org.springframework.web.client.RestTemplate;
/**
* The base configuration bean for Spring Cloud LoadBalancer.
*
* See {@link LoadBalancerClientsProperties} for the {@link ConfigurationProperties}
* annotation.
*
* @author Olga Maciaszek-Sharma
* @author Gandhimathi Velusamy
* @author Zhuozhi Ji
* @since 2.2.1
*/
public class LoadBalancerProperties {
/**
* Properties for <code>HealthCheckServiceInstanceListSupplier</code>.
*/
private HealthCheck healthCheck = new HealthCheck();
/**
* Allows setting the value of <code>hint</code> that is passed on to the LoadBalancer
* request and can subsequently be used in {@link ReactiveLoadBalancer}
* implementations.
*/
private Map<String, String> hint = new LinkedCaseInsensitiveMap<>();
/**
* Allows setting the name of the header used for passing the hint for hint-based
* service instance filtering.
*/
private String hintHeaderName = "X-SC-LB-Hint";
/**
* Properties for Spring-Retry and Reactor Retry support in Spring Cloud LoadBalancer.
*/
private Retry retry = new Retry();
/**
* Properties for LoadBalancer sticky-session.
*/
private StickySession stickySession = new StickySession();
/**
* If this flag is set to {@code true},
* {@code ServiceInstanceListSupplier#get(Request request)} method will be implemented
* to call {@code delegate.get(request)} in classes assignable from
* {@code DelegatingServiceInstanceListSupplier} that don't already implement that
* method, with the exclusion of {@code CachingServiceInstanceListSupplier} and
* {@code HealthCheckServiceInstanceListSupplier}, which should be placed in the
* instance supplier hierarchy directly after the supplier performing instance
* retrieval over the network, before any request-based filtering is done,
* {@code true} by default.
*/
private boolean callGetWithRequestOnDelegates = true;
/**
* Properties for
* {@code org.springframework.cloud.loadbalancer.core.SubsetServiceInstanceListSupplier}.
*/
private Subset subset = new Subset();
/**
* Enabling X-Forwarded Host and Proto Headers.
*/
private XForwarded xForwarded = new XForwarded();
/**
* Properties for LoadBalancer metrics.
*/
private Stats stats = new Stats();
public HealthCheck getHealthCheck() {
return healthCheck;
}
public void setHealthCheck(HealthCheck healthCheck) {
this.healthCheck = healthCheck;
}
public Map<String, String> getHint() {
return hint;
}
public void setHint(Map<String, String> hint) {
this.hint = hint;
}
public Retry getRetry() {
return retry;
}
public void setRetry(Retry retry) {
this.retry = retry;
}
public StickySession getStickySession() {
return stickySession;
}
public void setStickySession(StickySession stickySession) {
this.stickySession = stickySession;
}
public String getHintHeaderName() {
return hintHeaderName;
}
public void setHintHeaderName(String hintHeaderName) {
this.hintHeaderName = hintHeaderName;
}
public void setXForwarded(XForwarded xForwarded) {
this.xForwarded = xForwarded;
}
public XForwarded getXForwarded() {
return xForwarded;
}
public boolean isCallGetWithRequestOnDelegates() {
return callGetWithRequestOnDelegates;
}
public Subset getSubset() {
return subset;
}
public void setSubset(Subset subset) {
this.subset = subset;
}
public void setCallGetWithRequestOnDelegates(boolean callGetWithRequestOnDelegates) {
this.callGetWithRequestOnDelegates = callGetWithRequestOnDelegates;
}
public Stats getStats() {
return stats;
}
public void setStats(Stats stats) {
this.stats = stats;
}
public static class StickySession {
/**
* The name of the cookie holding the preferred instance id.
*/
private String instanceIdCookieName = "sc-lb-instance-id";
/**
* Indicates whether a cookie with the newly selected instance should be added by
* SC LoadBalancer.
*/
private boolean addServiceInstanceCookie = false;
public String getInstanceIdCookieName() {
return instanceIdCookieName;
}
public void setInstanceIdCookieName(String instanceIdCookieName) {
this.instanceIdCookieName = instanceIdCookieName;
}
public boolean isAddServiceInstanceCookie() {
return addServiceInstanceCookie;
}
public void setAddServiceInstanceCookie(boolean addServiceInstanceCookie) {
this.addServiceInstanceCookie = addServiceInstanceCookie;
}
}
public static class XForwarded {
/**
* To Enable X-Forwarded Headers.
*/
private boolean enabled = false;
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
public static class HealthCheck {
/**
* Initial delay value for the HealthCheck scheduler.
*/
private Duration initialDelay = Duration.ZERO;
/**
* Interval for rerunning the HealthCheck scheduler.
*/
private Duration interval = Duration.ofSeconds(25);
/**
* Interval for refetching available service instances.
*/
private Duration refetchInstancesInterval = Duration.ofSeconds(25);
/**
* Path at which the health-check request should be made. Can be set up per
* <code>serviceId</code>. A <code>default</code> value can be set up as well. If
* none is set up, <code>/actuator/health</code> will be used.
*/
private Map<String, String> path = new LinkedCaseInsensitiveMap<>();
/**
* Port at which the health-check request should be made. If none is set, the port
* under which the requested service is available at the service instance.
*/
private Integer port;
/**
* Indicates whether the instances should be refetched by the
* <code>HealthCheckServiceInstanceListSupplier</code>. This can be used if the
* instances can be updated and the underlying delegate does not provide an
* ongoing flux.
*/
private boolean refetchInstances = false;
/**
* Indicates whether health checks should keep repeating. It might be useful to
* set it to <code>false</code> if periodically refetching the instances, as every
* refetch will also trigger a healthcheck.
*/
private boolean repeatHealthCheck = true;
/**
* Indicates whether the {@code healthCheckFlux} should emit on each alive
* {@link ServiceInstance} that has been retrieved. If set to {@code false}, the
* entire alive instances sequence is first collected into a list and only then
* emitted.
*/
private boolean updateResultsList = true;
public boolean getRefetchInstances() {
return refetchInstances;
}
public void setRefetchInstances(boolean refetchInstances) {
this.refetchInstances = refetchInstances;
}
public boolean getRepeatHealthCheck() {
return repeatHealthCheck;
}
public void setRepeatHealthCheck(boolean repeatHealthCheck) {
this.repeatHealthCheck = repeatHealthCheck;
}
public Duration getInitialDelay() {
return initialDelay;
}
public void setInitialDelay(Duration initialDelay) {
this.initialDelay = initialDelay;
}
public Duration getRefetchInstancesInterval() {
return refetchInstancesInterval;
}
public void setRefetchInstancesInterval(Duration refetchInstancesInterval) {
this.refetchInstancesInterval = refetchInstancesInterval;
}
public Map<String, String> getPath() {
return path;
}
public void setPath(Map<String, String> path) {
this.path = path;
}
public Duration getInterval() {
return interval;
}
public void setInterval(Duration interval) {
this.interval = interval;
}
public Integer getPort() {
return port;
}
public void setPort(Integer port) {
this.port = port;
}
public boolean isUpdateResultsList() {
return updateResultsList;
}
public void setUpdateResultsList(boolean updateResultsList) {
this.updateResultsList = updateResultsList;
}
}
public static class Retry {
private boolean enabled = true;
/**
* Indicates retries should be attempted on operations other than
* {@link HttpMethod#GET}.
*/
private boolean retryOnAllOperations = false;
/**
* Indicates retries should be attempted for all exceptions, not only those
* specified in {@code retryableExceptions}.
*/
private boolean retryOnAllExceptions = false;
/**
* Number of retries to be executed on the same <code>ServiceInstance</code>.
*/
private int maxRetriesOnSameServiceInstance = 0;
/**
* Number of retries to be executed on the next <code>ServiceInstance</code>. A
* <code>ServiceInstance</code> is chosen before each retry call.
*/
private int maxRetriesOnNextServiceInstance = 1;
/**
* A {@link Set} of status codes that should trigger a retry.
*/
private Set<Integer> retryableStatusCodes = new HashSet<>();
/**
* A {@link Set} of {@link Throwable} classes that should trigger a retry.
*/
private Set<Class<? extends Throwable>> retryableExceptions = new HashSet<>(
Arrays.asList(IOException.class, TimeoutException.class, RetryableStatusCodeException.class,
org.springframework.cloud.client.loadbalancer.reactive.RetryableStatusCodeException.class));
/**
* Properties for Reactor Retry backoffs in Spring Cloud LoadBalancer.
*/
private Backoff backoff = new Backoff();
/**
* Returns true if the load balancer should retry failed requests.
* @return True if the load balancer should retry failed requests; false
* otherwise.
*/
public boolean isEnabled() {
return this.enabled;
}
/**
* Sets whether the load balancer should retry failed requests.
* @param enabled Whether the load balancer should retry failed requests.
*/
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public boolean isRetryOnAllOperations() {
return retryOnAllOperations;
}
public void setRetryOnAllOperations(boolean retryOnAllOperations) {
this.retryOnAllOperations = retryOnAllOperations;
}
public int getMaxRetriesOnSameServiceInstance() {
return maxRetriesOnSameServiceInstance;
}
public void setMaxRetriesOnSameServiceInstance(int maxRetriesOnSameServiceInstance) {
this.maxRetriesOnSameServiceInstance = maxRetriesOnSameServiceInstance;
}
public int getMaxRetriesOnNextServiceInstance() {
return maxRetriesOnNextServiceInstance;
}
public void setMaxRetriesOnNextServiceInstance(int maxRetriesOnNextServiceInstance) {
this.maxRetriesOnNextServiceInstance = maxRetriesOnNextServiceInstance;
}
public Set<Integer> getRetryableStatusCodes() {
return retryableStatusCodes;
}
public void setRetryableStatusCodes(Set<Integer> retryableStatusCodes) {
this.retryableStatusCodes = retryableStatusCodes;
}
public Set<Class<? extends Throwable>> getRetryableExceptions() {
return retryableExceptions;
}
public void setRetryableExceptions(Set<Class<? extends Throwable>> retryableExceptions) {
retryableExceptions
.add(org.springframework.cloud.client.loadbalancer.reactive.RetryableStatusCodeException.class);
this.retryableExceptions = retryableExceptions;
}
public Backoff getBackoff() {
return backoff;
}
public void setBackoff(Backoff backoff) {
this.backoff = backoff;
}
public boolean isRetryOnAllExceptions() {
return retryOnAllExceptions;
}
public void setRetryOnAllExceptions(boolean retryOnAllExceptions) {
this.retryOnAllExceptions = retryOnAllExceptions;
}
public static class Backoff {
/**
* Indicates whether Reactor Retry backoffs should be applied.
*/
private boolean enabled = false;
/**
* Used to set {@link RetryBackoffSpec#minBackoff}.
*/
private Duration minBackoff = Duration.ofMillis(5);
/**
* Used to set {@link RetryBackoffSpec#maxBackoff}.
*/
private Duration maxBackoff = Duration.ofMillis(Long.MAX_VALUE);
/**
* Used to set {@link RetryBackoffSpec#jitter}.
*/
private double jitter = 0.5d;
public Duration getMinBackoff() {
return minBackoff;
}
public void setMinBackoff(Duration minBackoff) {
this.minBackoff = minBackoff;
}
public Duration getMaxBackoff() {
return maxBackoff;
}
public void setMaxBackoff(Duration maxBackoff) {
this.maxBackoff = maxBackoff;
}
public double getJitter() {
return jitter;
}
public void setJitter(double jitter) {
this.jitter = jitter;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
}
public static class Subset {
/**
* Instance id of deterministic subsetting. If not set,
* {@link IdUtils#getDefaultInstanceId(PropertyResolver)} will be used.
*/
private String instanceId = "";
/**
* Max subset size of deterministic subsetting.
*/
private int size = 100;
public String getInstanceId() {
return instanceId;
}
public void setInstanceId(String instanceId) {
this.instanceId = instanceId;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
}
public static class Stats {
/**
* Indicates whether the {@code path} should be added to {@code uri} tag in
* metrics. When {@link RestTemplate} is used to execute load-balanced requests
* with high cardinality paths, setting it to {@code false} is recommended.
*/
private boolean includePath = true;
public boolean isIncludePath() {
return includePath;
}
public void setIncludePath(boolean includePath) {
this.includePath = includePath;
}
}
}