I've read whole documentation, tutorial [1], and spend several hours in sources, but I still do not understand how to configure loadbalancer, especially if I don't use magic annotations.
I have following configuration:
@Configuration
public class AppConfig {
public static final String SERVICE_ID = "service";
@Primary
@Bean
public ServiceInstanceListSupplier serviceInstanceListSupplier() {
return ServiceInstanceListSuppliers.from(SERVICE_ID,
new DefaultServiceInstance(SERVICE_ID + "1", SERVICE_ID, "localhost", 8886, false),
new DefaultServiceInstance(SERVICE_ID + "2", SERVICE_ID, "localhost", 8887, false));
}
@Bean
public LoadBalancerClientFactory loadBalancerClientFactory() {
return new LoadBalancerClientFactory();
}
@Bean
public ReactorLoadBalancerExchangeFilterFunction loadBalancerExchangeFilterFunction(LoadBalancerProperties properties) {
return new ReactorLoadBalancerExchangeFilterFunction(loadBalancerClientFactory(), properties);
}
}
and use bean loadBalancerExchangeFilterFunction as:
WebClient.builder()
.baseUrl("http://service/test-consumer")
.filter(lbFunction)
.build();
and it works. The problem is, that it works regardless of what hostname I use. So if I replace hostname "service" with whatever work I like, I will be still sending data to localhost:8886 or localhost:8887.
Can someone explain what is the role of serviceId and how this is paired to collection of DefaultServiceInstance?
(I want to understand the internals, what are the key components, their purpose and their interplay. I'm not primarily looking for magic annotation, but that one actually explained would be also great. Debugging it is really hard, I have several A4 with class diagrams and it still makes no sense at all).
Question: Is there a misconfiguration? What is the purpose of serviceId? It seems that none, as webclient using ReactorLoadBalancerExchangeFilterFunction will create roundrobin loadbalancer over configured ServiceInstances regardless of what is actual hostname used in given webclient.
Question2: how could I create 2 loadbalanced services and control to which service (not node) will request go? Do I need 2 separate webclients or some url pattern(like using serviceId in place of hostname) will do? If I need 2 webclients, how is pairing to DefaultServiceInstance done?
[1] https://spring.io/guides/gs/spring-cloud-loadbalancer/
EDIT:
after suggested update, the configuration looks like:
@Configuration
public class AppConfig {
@Bean
public ServiceInstanceListSupplier instanceSupplier(ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder()
.withDiscoveryClient()
.withHealthChecks()
.build(context);
}
@Bean
public LoadBalancerClientFactory loadBalancerClientFactory() {
return new LoadBalancerClientFactory();
}
@Bean
public ReactorLoadBalancerExchangeFilterFunction loadBalancerExchangeFilterFunction(LoadBalancerProperties properties) {
return new ReactorLoadBalancerExchangeFilterFunction(loadBalancerClientFactory(), properties);
}
}
application.properties contains:
spring.cloud.discovery.client.simple.instances.complicated[0].uri=http://localhost:8886
spring.cloud.discovery.client.simple.instances.complicated[1].uri=http://localhost:8887
webclient call to URL: http://localhost:8888/test-consumer (ie. hostname not matching serviceID) produces:
o.s.c.l.core.RoundRobinLoadBalancer : No servers available for service: localhost
eactorLoadBalancerExchangeFilterFunction : LoadBalancer does not contain an instance for the service localhost
webclient call to URL: http://complicated/test-consumer (ie. hostname matching serviceID) produces:
o.s.c.l.core.RoundRobinLoadBalancer : No servers available for service: complicated
eactorLoadBalancerExchangeFilterFunction : LoadBalancer does not contain an instance for the service complicated
The reason for this is that this.serviceId = environment.getProperty(PROPERTY_NAME); in DiscoveryClientServiceInstanceListSupplier(ReactiveDiscoveryClient,Environment) evaluates as null, thus even though I'm looking for some serviceId, delegate.getInstance is called with null, so no ServiceInstances are found. IF I removed @Bean instanceSupplier, and hope for autoconfiguration do it somehow magically, the this.serviceId = environment.getProperty(PROPERTY_NAME); is somehow magically set, serviceId is propagated correctly, and it works. For calls which leads elsewhere than configured serviceId, it fails saying, that this serviceId is not know, instead of making call.
SO it does mean, that if I configure loadbalancer, I cannot call anything else but (auto)configured services???