Spring Cloud LoadBalancer

Spring Cloud provides its own client-side load-balancer abstraction and implementation. For the load-balancing mechanism, ReactiveLoadBalancer interface has been added and a Round-Robin-based and Random implementations have been provided for it. In order to get instances to select from reactive ServiceInstanceListSupplier is used. Currently we support a service-discovery-based implementation of ServiceInstanceListSupplier that retrieves available instances from Service Discovery using a Discovery Client available in the classpath.

It is possible to disable Spring Cloud LoadBalancer by setting the value of spring.cloud.loadbalancer.enabled to false.

Eager loading of LoadBalancer contexts

Spring Cloud LoadBalancer creates a separate Spring child context for each service id. By default, these contexts are initialised lazily, whenever the first request for a service id is being load-balanced.

You can choose to load those contexts eagerly. In order to do that, specify the service ids for which you want to do eager load using the spring.cloud.loadbalancer.eager-load.clients property, for example:

spring.cloud-loadbalancer.eager-load.clients[0]=my-first-client
spring.cloud-loadbalancer.eager-load.clients[1]=my-second-client

Switching between the load-balancing algorithms

The ReactiveLoadBalancer implementation that is used by default is RoundRobinLoadBalancer. To switch to a different implementation, either for selected services or all of them, you can use the custom LoadBalancer configurations mechanism.

For example, the following configuration can be passed via @LoadBalancerClient annotation to switch to using the RandomLoadBalancer:

public class CustomLoadBalancerConfiguration {

	@Bean
	ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,
			LoadBalancerClientFactory loadBalancerClientFactory) {
		String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
		return new RandomLoadBalancer(loadBalancerClientFactory
				.getLazyProvider(name, ServiceInstanceListSupplier.class),
				name);
	}
}
The classes you pass as @LoadBalancerClient or @LoadBalancerClients configuration arguments should either not be annotated with @Configuration or be outside component scan scope.

Spring Cloud LoadBalancer integrations

To make it easy to use Spring Cloud LoadBalancer, we provide ReactorLoadBalancerExchangeFilterFunction (which can be used with WebClient) and BlockingLoadBalancerClient (which works with RestTemplate and RestClient). You can see more information and examples of usage in the following sections:

Spring Cloud LoadBalancer Caching

Apart from the basic ServiceInstanceListSupplier implementation that retrieves instances via DiscoveryClient each time it has to choose an instance, we provide two caching implementations.

[[caffeine-backed-loadbalancer-cache-implementation]] === Caffeine-backed LoadBalancer Cache Implementation

If you have com.github.ben-manes.caffeine:caffeine in the classpath, Caffeine-based implementation will be used. See the LoadBalancerCacheConfiguration section for information on how to configure it.

If you are using Caffeine, you can also override the default Caffeine Cache setup for the LoadBalancer by passing your own Caffeine Specification in the spring.cloud.loadbalancer.cache.caffeine.spec property.

WARN: Passing your own Caffeine specification will override any other LoadBalancerCache settings, including General LoadBalancer Cache Configuration fields, such as ttl and capacity.

Default LoadBalancer Cache Implementation

If you do not have Caffeine in the classpath, the DefaultLoadBalancerCache, which comes automatically with spring-cloud-starter-loadbalancer, will be used. See the LoadBalancerCacheConfiguration section for information on how to configure it.

To use Caffeine instead of the default cache, add the com.github.ben-manes.caffeine:caffeine dependency to classpath.

LoadBalancer Cache Configuration

You can set your own ttl value (the time after write after which entries should be expired), expressed as Duration, by passing a String compliant with the Spring Boot String to Duration converter syntax. as the value of the spring.cloud.loadbalancer.cache.ttl property. You can also set your own LoadBalancer cache initial capacity by setting the value of the spring.cloud.loadbalancer.cache.capacity property.

The default setup includes ttl set to 35 seconds and the default initialCapacity is 256.

You can also altogether disable loadBalancer caching by setting the value of spring.cloud.loadbalancer.cache.enabled to false.

Although the basic, non-cached, implementation is useful for prototyping and testing, it’s much less efficient than the cached versions, so we recommend always using the cached version in production. If the caching is already done by the DiscoveryClient implementation, for example EurekaDiscoveryClient, the load-balancer caching should be disabled to prevent double caching.
When you create your own configuration, if you use CachingServiceInstanceListSupplier make sure to place it in the hierarchy directly after the supplier that retrieves the instances over the network, for example, DiscoveryClientServiceInstanceListSupplier, before any other filtering suppliers.

Weighted Load-Balancing

To enable weighted load-balancing, we provide the WeightedServiceInstanceListSupplier. We use WeightFunction to calculate the weight of each instance. By default, we try to read and parse the weight from the metadata map (the key is weight).

If the weight is not specified in the metadata map, we default the weight of this instance to be 1.

You can configure it either by setting the value of spring.cloud.loadbalancer.configurations to weighted or by providing your own ServiceInstanceListSupplier bean, for example:

public class CustomLoadBalancerConfiguration {

	@Bean
	public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
			ConfigurableApplicationContext context) {
		return ServiceInstanceListSupplier.builder()
					.withDiscoveryClient()
					.withWeighted()
					.withCaching()
					.build(context);
	}
}
You can also customize the weight calculation logic by providing WeightFunction.

You can use this sample configuration to make all instances have a random weight:

public class CustomLoadBalancerConfiguration {

	@Bean
	public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
			ConfigurableApplicationContext context) {
		return ServiceInstanceListSupplier.builder()
					.withDiscoveryClient()
					.withWeighted(instance -> ThreadLocalRandom.current().nextInt(1, 101))
					.withCaching()
					.build(context);
	}
}

Zone-Based Load-Balancing

To enable zone-based load-balancing, we provide the ZonePreferenceServiceInstanceListSupplier. We use DiscoveryClient-specific zone configuration (for example, eureka.instance.metadata-map.zone) to pick the zone that the client tries to filter available service instances for.

You can also override DiscoveryClient-specific zone setup by setting the value of spring.cloud.loadbalancer.zone property.
For the time being, only Eureka Discovery Client is instrumented to set the LoadBalancer zone. For other discovery client, set the spring.cloud.loadbalancer.zone property. More instrumentations coming shortly.
To determine the zone of a retrieved ServiceInstance, we check the value under the "zone" key in its metadata map.

The ZonePreferenceServiceInstanceListSupplier filters retrieved instances and only returns the ones within the same zone. If the zone is null or there are no instances within the same zone, it returns all the retrieved instances.

In order to use the zone-based load-balancing approach, you will have to instantiate a ZonePreferenceServiceInstanceListSupplier bean in a custom configuration.

We use delegates to work with ServiceInstanceListSupplier beans. We suggest using a DiscoveryClientServiceInstanceListSupplier delegate, wrapping it with a CachingServiceInstanceListSupplier to leverage LoadBalancer caching mechanism, and then passing the resulting bean in the constructor of ZonePreferenceServiceInstanceListSupplier.

You can use this sample configuration to set it up:

public class CustomLoadBalancerConfiguration {

	@Bean
	public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
			ConfigurableApplicationContext context) {
		return ServiceInstanceListSupplier.builder()
					.withDiscoveryClient()
                    .withCaching()
					.withZonePreference()
					.build(context);
	}
}

Instance Health-Check for LoadBalancer

It is possible to enable a scheduled HealthCheck for the LoadBalancer. The HealthCheckServiceInstanceListSupplier is provided for that. It regularly verifies if the instances provided by a delegate ServiceInstanceListSupplier are still alive and only returns the healthy instances, unless there are none - then it returns all the retrieved instances.

This mechanism is particularly helpful while using the SimpleDiscoveryClient. For the clients backed by an actual Service Registry, it’s not necessary to use, as we already get healthy instances after querying the external ServiceDiscovery.
This supplier is also recommended for setups with a small number of instances per service in order to avoid retrying calls on a failing instance.
If using any of the Service Discovery-backed suppliers, adding this health-check mechanism is usually not necessary, as we retrieve the health state of the instances directly from the Service Registry.
The HealthCheckServiceInstanceListSupplier relies on having updated instances provided by a delegate flux. In the rare cases when you want to use a delegate that does not refresh the instances, even though the list of instances may change (such as the DiscoveryClientServiceInstanceListSupplier provided by us), you can set spring.cloud.loadbalancer.health-check.refetch-instances to true to have the instance list refreshed by the HealthCheckServiceInstanceListSupplier. You can then also adjust the refretch intervals by modifying the value of spring.cloud.loadbalancer.health-check.refetch-instances-interval and opt to disable the additional healthcheck repetitions by setting spring.cloud.loadbalancer.health-check.repeat-health-check to false as every instances refetch will also trigger a healthcheck.

HealthCheckServiceInstanceListSupplier uses properties prefixed with spring.cloud.loadbalancer.health-check. You can set the initialDelay and interval for the scheduler. You can set the default path for the healthcheck URL by setting the value of the spring.cloud.loadbalancer.health-check.path.default property. You can also set a specific value for any given service by setting the value of the spring.cloud.loadbalancer.health-check.path.[SERVICE_ID] property, substituting [SERVICE_ID] with the correct ID of your service. If the [SERVICE_ID] is not specified, /actuator/health is used by default. If the [SERVICE_ID] is set to null or empty as a value, then the health check will not be executed. You can also set a custom port for health-check requests by setting the value of spring.cloud.loadbalancer.health-check.port. If none is set, the port under which the requested service is available at the service instance.

If you rely on the default path (/actuator/health), make sure you add spring-boot-starter-actuator to your collaborator’s dependencies, unless you are planning to add such an endpoint on your own.
By default, the healthCheckFlux will emit on each alive ServiceInstance that has been retrieved. You can modify this behaviour by setting the value of spring.cloud.loadbalancer.health-check.update-results-list to false. If this property is set to false, the entire alive instances sequence is first collected into a list and only then emitted, which ensures the flux does not emit values in between the health-check intervals set in properties.

In order to use the health-check scheduler approach, you will have to instantiate a HealthCheckServiceInstanceListSupplier bean in a custom configuration.

We use delegates to work with ServiceInstanceListSupplier beans. We suggest passing a DiscoveryClientServiceInstanceListSupplier delegate in the constructor of HealthCheckServiceInstanceListSupplier.

You can use this sample configuration to set it up:

public class CustomLoadBalancerConfiguration {

	@Bean
	public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
			ConfigurableApplicationContext context) {
		return ServiceInstanceListSupplier.builder()
					.withDiscoveryClient()
					.withHealthChecks()
					.build(context);
	    }
	}
For the non-reactive stack, create this supplier with the withBlockingHealthChecks(). You can also pass your own WebClient, RestTemplate or RestClient instance to be used for the checks.
HealthCheckServiceInstanceListSupplier has its own caching mechanism based on Reactor Flux replay(). Therefore, if it’s being used, you may want to skip wrapping that supplier with CachingServiceInstanceListSupplier.
When you create your own configuration, HealthCheckServiceInstanceListSupplier, make sure to place it in the hierarchy directly after the supplier that retrieves the instances over the network, for example, DiscoveryClientServiceInstanceListSupplier, before any other filtering suppliers.

Same instance preference for LoadBalancer

You can set up the LoadBalancer in such a way that it prefers the instance that was previously selected, if that instance is available.

For that, you need to use SameInstancePreferenceServiceInstanceListSupplier. You can configure it either by setting the value of spring.cloud.loadbalancer.configurations to same-instance-preference or by providing your own ServiceInstanceListSupplier bean — for example:

public class CustomLoadBalancerConfiguration {

	@Bean
	public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
			ConfigurableApplicationContext context) {
		return ServiceInstanceListSupplier.builder()
					.withDiscoveryClient()
					.withSameInstancePreference()
					.build(context);
	    }
	}
This is also a replacement for Zookeeper StickyRule.

Request-based Sticky Session for LoadBalancer

You can set up the LoadBalancer in such a way that it prefers the instance with instanceId provided in a request cookie. We currently support this if the request is being passed to the LoadBalancer through either ClientRequestContext or ServerHttpRequestContext, which are used by the SC LoadBalancer exchange filter functions and filters.

For that, you need to use the RequestBasedStickySessionServiceInstanceListSupplier. You can configure it either by setting the value of spring.cloud.loadbalancer.configurations to request-based-sticky-session or by providing your own ServiceInstanceListSupplier bean — for example:

public class CustomLoadBalancerConfiguration {

	@Bean
	public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
			ConfigurableApplicationContext context) {
		return ServiceInstanceListSupplier.builder()
					.withDiscoveryClient()
					.withRequestBasedStickySession()
					.build(context);
	    }
	}

For that functionality, it is useful to have the selected service instance (which can be different from the one in the original request cookie if that one is not available) to be updated before sending the request forward. To do that, set the value of spring.cloud.loadbalancer.sticky-session.add-service-instance-cookie to true.

By default, the name of the cookie is sc-lb-instance-id. You can modify it by changing the value of the spring.cloud.loadbalancer.instance-id-cookie-name property.

This feature is currently supported for WebClient-backed load-balancing.

Spring Cloud LoadBalancer Hints

Spring Cloud LoadBalancer lets you set String hints that are passed to the LoadBalancer within the Request object and that can later be used in ReactiveLoadBalancer implementations that can handle them.

You can set a default hint for all services by setting the value of the spring.cloud.loadbalancer.hint.default property. You can also set a specific value for any given service by setting the value of the spring.cloud.loadbalancer.hint.[SERVICE_ID] property, substituting [SERVICE_ID] with the correct ID of your service. If the hint is not set by the user, default is used.

Hint-Based Load-Balancing

We also provide a HintBasedServiceInstanceListSupplier, which is a ServiceInstanceListSupplier implementation for hint-based instance selection.

HintBasedServiceInstanceListSupplier checks for a hint request header (the default header-name is X-SC-LB-Hint, but you can modify it by changing the value of the spring.cloud.loadbalancer.hint-header-name property) and, if it finds a hint request header, uses the hint value passed in the header to filter service instances.

If no hint header has been added, HintBasedServiceInstanceListSupplier uses hint values from properties to filter service instances.

If no hint is set, either by the header or by properties, all service instances provided by the delegate are returned.

While filtering, HintBasedServiceInstanceListSupplier looks for service instances that have a matching value set under the hint key in their metadataMap. If no matching instances are found, all instances provided by the delegate are returned.

You can use the following sample configuration to set it up:

public class CustomLoadBalancerConfiguration {

	@Bean
	public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
			ConfigurableApplicationContext context) {
		return ServiceInstanceListSupplier.builder()
					.withDiscoveryClient()
                    .withCaching()
					.withHints()
					.build(context);
	}
}

Transform the load-balanced HTTP request

You can use the selected ServiceInstance to transform the load-balanced HTTP Request.

For RestTemplate and RestClient, you need to implement and define LoadBalancerRequestTransformer as follows:

@Bean
public LoadBalancerRequestTransformer transformer() {
	return new LoadBalancerRequestTransformer() {
		@Override
		public HttpRequest transformRequest(HttpRequest request, ServiceInstance instance) {
			return new HttpRequestWrapper(request) {
				@Override
				public HttpHeaders getHeaders() {
					HttpHeaders headers = new HttpHeaders();
					headers.putAll(super.getHeaders());
					headers.add("X-InstanceId", instance.getInstanceId());
					return headers;
				}
			};
		}
	};
}

For WebClient, you need to implement and define LoadBalancerClientRequestTransformer as follows:

@Bean
public LoadBalancerClientRequestTransformer transformer() {
	return new LoadBalancerClientRequestTransformer() {
		@Override
		public ClientRequest transformRequest(ClientRequest request, ServiceInstance instance) {
			return ClientRequest.from(request)
					.header("X-InstanceId", instance.getInstanceId())
					.build();
		}
	};
}

If multiple transformers are defined, they are applied in the order in which Beans are defined. Alternatively, you can use LoadBalancerRequestTransformer.DEFAULT_ORDER or LoadBalancerClientRequestTransformer.DEFAULT_ORDER to specify the order.

Spring Cloud LoadBalancer Subset

SubsetServiceInstanceListSupplier implements a deterministic subsetting algorithm to select a limited number of instances in the ServiceInstanceListSupplier delegates hierarchy.

You can configure it either by setting the value of spring.cloud.loadbalancer.configurations to subset or by providing your own ServiceInstanceListSupplier bean — for example:

public class CustomLoadBalancerConfiguration {

	@Bean
	public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
			ConfigurableApplicationContext context) {
		return ServiceInstanceListSupplier.builder()
					.withDiscoveryClient()
					.withSubset()
					.withCaching()
					.build(context);
	    }
	}
By default, each service instance is assigned a unique instanceId, and different instanceId values often select different subsets. Normally, you need not pay attention to it. However, if you need to have multiple instances select the same subset, you can set it with spring.cloud.loadbalancer.subset.instance-id (which supports placeholders).
By default, the size of the subset is set to 100. You can also set it with spring.cloud.loadbalancer.subset.size.

Spring Cloud LoadBalancer Starter

We also provide a starter that allows you to easily add Spring Cloud LoadBalancer in a Spring Boot app. In order to use it, just add org.springframework.cloud:spring-cloud-starter-loadbalancer to your Spring Cloud dependencies in your build file.

Spring Cloud LoadBalancer starter includes Spring Boot Caching and Evictor.

Passing Your Own Spring Cloud LoadBalancer Configuration

You can also use the @LoadBalancerClient annotation to pass your own load-balancer client configuration, passing the name of the load-balancer client and the configuration class, as follows:

@Configuration
@LoadBalancerClient(value = "stores", configuration = CustomLoadBalancerConfiguration.class)
public class MyConfiguration {

	@Bean
	@LoadBalanced
	public WebClient.Builder loadBalancedWebClientBuilder() {
		return WebClient.builder();
	}
}
In order to make working on your own LoadBalancer configuration easier, we have added a builder() method to the ServiceInstanceListSupplier class.
You can also use our alternative predefined configurations in place of the default ones by setting the value of spring.cloud.loadbalancer.configurations property to zone-preference to use ZonePreferenceServiceInstanceListSupplier with caching or to health-check to use HealthCheckServiceInstanceListSupplier with caching.

You can use this feature to instantiate different implementations of ServiceInstanceListSupplier or ReactorLoadBalancer, either written by you, or provided by us as alternatives (for example ZonePreferenceServiceInstanceListSupplier) to override the default setup.

You can see an example of a custom configuration here.

The annotation value arguments (stores in the example above) specifies the service id of the service that we should send the requests to with the given custom configuration.

You can also pass multiple configurations (for more than one load-balancer client) through the @LoadBalancerClients annotation, as the following example shows:

@Configuration
@LoadBalancerClients({@LoadBalancerClient(value = "stores", configuration = StoresLoadBalancerClientConfiguration.class), @LoadBalancerClient(value = "customers", configuration = CustomersLoadBalancerClientConfiguration.class)})
public class MyConfiguration {

	@Bean
	@LoadBalanced
	public WebClient.Builder loadBalancedWebClientBuilder() {
		return WebClient.builder();
	}
}
The classes you pass as @LoadBalancerClient or @LoadBalancerClients configuration arguments should either not be annotated with @Configuration or be outside component scan scope.
When you create your own configuration, if you use CachingServiceInstanceListSupplier or HealthCheckServiceInstanceListSupplier, makes sure to use one of them, not both, and make sure to place it in the hierarchy directly after the supplier that retrieves the instances over the network, for example, DiscoveryClientServiceInstanceListSupplier, before any other filtering suppliers.

Spring Cloud LoadBalancer Lifecycle

One type of bean that it may be useful to register using Custom LoadBalancer configuration is LoadBalancerLifecycle.

The LoadBalancerLifecycle beans provide callback methods, named onStart(Request<RC> request), onStartRequest(Request<RC> request, Response<T> lbResponse) and onComplete(CompletionContext<RES, T, RC> completionContext), that you should implement to specify what actions should take place before and after load-balancing.

onStart(Request<RC> request) takes a Request object as a parameter. It contains data that is used to select an appropriate instance, including the downstream client request and hint. onStartRequest also takes the Request object and, additionally, the Response<T> object as parameters. On the other hand, a CompletionContext object is provided to the onComplete(CompletionContext<RES, T, RC> completionContext) method. It contains the LoadBalancer Response, including the selected service instance, the Status of the request executed against that service instance and (if available) the response returned to the downstream client, and (if an exception has occurred) the corresponding Throwable.

The supports(Class requestContextClass, Class responseClass, Class serverTypeClass) method can be used to determine whether the processor in question handles objects of provided types. If not overridden by the user, it returns true.

In the preceding method calls, RC means RequestContext type, RES means client response type, and T means returned server type.

Spring Cloud LoadBalancer Statistics

We provide a LoadBalancerLifecycle bean called MicrometerStatsLoadBalancerLifecycle, which uses Micrometer to provide statistics for load-balanced calls.

In order to get this bean added to your application context, set the value of the spring.cloud.loadbalancer.stats.micrometer.enabled to true and have a MeterRegistry available (for example, by adding Spring Boot Actuator to your project).

MicrometerStatsLoadBalancerLifecycle registers the following meters in MeterRegistry:

  • loadbalancer.requests.active: A gauge that allows you to monitor the number of currently active requests for any service instance (service instance data available via tags);

  • loadbalancer.requests.success: A timer that measures the time of execution of any load-balanced requests that have ended in passing a response on to the underlying client;

  • loadbalancer.requests.failed: A timer that measures the time of execution of any load-balanced requests that have ended with an exception;

  • loadbalancer.requests.discard: A counter that measures the number of discarded load-balanced requests, i.e. requests where a service instance to run the request on has not been retrieved by the LoadBalancer.

Additional information regarding the service instances, request data, and response data is added to metrics via tags whenever available.

For some implementations, such as BlockingLoadBalancerClient, request and response data might not be available, as we establish generic types from arguments and might not be able to determine the types and read the data.
The meters are registered in the registry when at least one record is added for a given meter.
You can further configure the behavior of those metrics (for example, add publishing percentiles and histograms) by adding MeterFilters.

Configuring Individual LoadBalancerClients

Individual Loadbalancer clients may be configured individually with a different prefix spring.cloud.loadbalancer.clients.<clientId>. where clientId is the name of the loadbalancer. Default configuration values may be set in the spring.cloud.loadbalancer. namespace and will be merged with the client specific values taking precedence

application.yml
spring:
  cloud:
    loadbalancer:
      health-check:
        initial-delay: 1s
      clients:
        myclient:
          health-check:
            interval: 30s

The above example will result in a merged health-check @ConfigurationProperties object with initial-delay=1s and interval=30s.

The per-client configuration properties work for most of the properties, apart from the following global ones:

  • spring.cloud.loadbalancer.enabled - globally enables or disables load-balancing

  • spring.cloud.loadbalancer.retry.enabled - globally enables or disables load-balanced retries. If you enable it globally, you can still disable retries for specific clients using the client-prefixed properties, but not the other way round

  • spring.cloud.loadbalancer.cache.enabled - globally enables or disables LoadBalancer caching. If you enable it globally, you can still disable caching for specific clients by creating a custom configuration that does not include the CachingServiceInstanceListSupplier in the ServiceInstanceListSupplier delegates hierarchy, but not the other way round.

  • spring.cloud.loadbalancer.stats.micrometer.enabled - globally enables or disables LoadBalancer Micrometer metrics

For the properties where maps where already used, where you can specify a different value per-client without using the clients keyword (for example, hints, health-check.path), we have kept that behaviour in order to keep the library backwards compatible. It will be modified in the next major release.
Starting with 4.1.0, we have introduced the callGetWithRequestOnDelegates flag in LoadBalancerProperties. If this flag is set to true, ServiceInstanceListSupplier#get(Request request) method will be implemented to call delegate.get(request) in classes assignable from DelegatingServiceInstanceListSupplier that don’t already implement that method, with the exclusion of CachingServiceInstanceListSupplier and 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. It is set to true by default.

[[-aot-and-native-image-support]] == AOT and Native Image Support

Since 4.0.0, Spring Cloud LoadBalancer supports Spring AOT transformations and native images. However, to use this feature, you need to explicitly define your LoadBalancerClient service IDs. You can do so by using the value or name attributes of the @LoadBalancerClient annotation or as values of the spring.cloud.loadbalancer.eager-load.clients property.