Giriş
Spring Cloud provides several ways to implement load balancing, including Ribbon, Spring Cloud Load Balancer, Spring Cloud Gateway, and Kubernetes Load Balancer.
Bu proje, WebClient ile client side load balancing yapmak içindir.
Maven
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
LoadBalancerClientFactory Sınıfı
Örnek
@Bean
public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(
Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new RoundRobinLoadBalancer(
loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class),
name);
}
@LoadBalancedAnotasyonu
@LoadBalancerClient Anotasyonu
Load Balancing için iki gerçekleştirim var
1. RoundRobinLoadBalancer
2. RandomLoadBalancer
Örnek - RoundRobinLoadBalancer
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.client.WebClient;
@Configuration
@LoadBalancerClient(name = WebClientConfig.CLIENT_NAME,
configuration = IgniteLoadBalancerConfiguration.class)
public class WebClientConfig {
public static final String CLIENT_NAME = "client";
@Bean
@LoadBalanced
public WebClient.Builder usersClientBuilder() {
return WebClient.builder()
.defaultHeader("affinity-cache-name", "UserCache");
}
}
@Configuration
public class IgniteLoadBalancerConfiguration {
public static final String SERVICE_ID = "example";
@Bean
@Primary
public ServiceInstanceListSupplier serviceInstanceListSupplier(IgniteEx ignite) {
return new IgniteServiceInstanceListSuppler(ignite);
}
@Bean
public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(
Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new RoundRobinLoadBalancer(
loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class),
name);
}
}
IgniteServiceInstanceListSuppler will return instances based on the request key.
Örnek
Elimizde şöyle bir kod
olsun. locahhost üzerinde çalışan 4 tane port döndürür
@Bean
public ReactiveDiscoveryClient customDiscoveryClient() {
return new ReactiveDiscoveryClient() {
@Override
public String description() {
return "Calling another API example";
}
@Override
public Flux<ServiceInstance> getInstances(String serviceId) {
log.debug("getInstances: {}", serviceId);
return Flux.just(8080, 8081, 8082)
.map(port -> new DefaultServiceInstance(serviceId + "-" + port, serviceId,
"localhost", port, false));
}
@Override
public Flux<String> getServices() {
return Flux.just("ExampleApi");
}
};
}
Implement theReactiveDiscoveryClient . It allows us to return a list of instances that can be used when a specific call is triggered.
@Service
public class AccountsService {
private static final String API = "ExampleApi";
private final WebClient apiClient;
public AccountsService(WebClient.Builder loadBalancedWebClientBuilder) {
this.apiClient = loadBalancedWebClientBuilder
.build();
}
public Mono<String> login(String username) {
return apiClient.post()
.uri(uriBuilder -> uriBuilder
.host(API)
.path("/login")
.build())
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.header("username", username)
.exchangeToMono(r -> handleResponse(r));
}
private Mono<String> handleResponse(ClientResponse r) {
if (r.statusCode().is2xxSuccessful()) {
return r.bodyToMono(String.class);
}
return r.bodyToMono(String.class)
.switchIfEmpty(Mono.error(new IllegalStateException("Failed: " + r.statusCode())))
.flatMap(response -> Mono.error(new IllegalStateException("Failed: " +
r.statusCode() + ", " + response)));
}
}