Skip to content

Commit

Permalink
feat(lb): deterministic subsetting algorithm to limit the number of i…
Browse files Browse the repository at this point in the history
…nstances
  • Loading branch information
jizhuozhi committed Oct 31, 2023
1 parent 6ff2aa7 commit 9935bc5
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.springframework.cloud.loadbalancer.config.LoadBalancerZoneConfig;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.PropertyResolver;
import org.springframework.http.HttpStatus;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
Expand Down Expand Up @@ -301,6 +302,16 @@ public ServiceInstanceListSupplierBuilder withHints() {
return this;
}

public ServiceInstanceListSupplierBuilder withSubset() {
DelegateCreator creator = (context, delegate) -> {
PropertyResolver resolver = context.getBean(PropertyResolver.class);
LoadBalancerClientFactory factory = context.getBean(LoadBalancerClientFactory.class);
return new SubsetServiceInstanceListSupplier(delegate, resolver, factory);
};
creators.add(creator);
return this;
}

/**
* Support {@link ServiceInstanceListSupplierBuilder} can be added to the expansion
* implementation of {@link ServiceInstanceListSupplier} by this method.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties;
import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer;
import org.springframework.core.env.PropertyResolver;

/**
Expand All @@ -42,8 +43,9 @@ public class SubsetServiceInstanceListSupplier extends DelegatingServiceInstance
private final int size;

public SubsetServiceInstanceListSupplier(ServiceInstanceListSupplier delegate, PropertyResolver resolver,
LoadBalancerProperties properties) {
ReactiveLoadBalancer.Factory<ServiceInstance> factory) {
super(delegate);
LoadBalancerProperties properties = factory.getProperties(getServiceId());
this.instanceId = resolver.resolvePlaceholders(properties.getSubset().getInstanceId());
this.size = properties.getSubset().getSize();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,13 @@
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties;
import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer;
import org.springframework.mock.env.MockEnvironment;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.springframework.cloud.loadbalancer.core.LoadBalancerTestUtils.buildLoadBalancerClientFactory;

/**
* Tests for {@link SubsetServiceInstanceListSupplier}
Expand All @@ -60,9 +62,10 @@ public void destroy() {

@Test
void shouldReturnEmptyWhenDelegateReturnedEmpty() {
when(delegate.getServiceId()).thenReturn("test");
when(delegate.get()).thenReturn(Flux.just(Collections.emptyList()));
SubsetServiceInstanceListSupplier supplier = new SubsetServiceInstanceListSupplier(delegate, env,
subsetProperties("foobar", 100));
factory("foobar", 100));

List<ServiceInstance> serviceInstances = Objects.requireNonNull(supplier.get().blockFirst());
assertThat(serviceInstances).isEmpty();
Expand All @@ -74,9 +77,10 @@ void shouldReturnSublistWithGivenSubsetSize() {
.mapToObj(i -> new DefaultServiceInstance(Integer.toString(i), "test", "host" + i, 8080, false, null))
.collect(Collectors.toList());

when(delegate.getServiceId()).thenReturn("test");
when(delegate.get()).thenReturn(Flux.just(instances));
SubsetServiceInstanceListSupplier supplier = new SubsetServiceInstanceListSupplier(delegate, env,
subsetProperties("foobar", 5));
factory("foobar", 5));

List<ServiceInstance> serviceInstances = Objects.requireNonNull(supplier.get().blockFirst());
assertThat(serviceInstances).hasSize(5);
Expand All @@ -88,9 +92,10 @@ void shouldReturnRawWhenLessThanSubsetSize() {
.mapToObj(i -> new DefaultServiceInstance(Integer.toString(i), "test", "host" + i, 8080, false, null))
.collect(Collectors.toList());

when(delegate.getServiceId()).thenReturn("test");
when(delegate.get()).thenReturn(Flux.just(instances));
SubsetServiceInstanceListSupplier supplier = new SubsetServiceInstanceListSupplier(delegate, env,
subsetProperties("foobar", 1000));
factory("foobar", 1000));

List<ServiceInstance> serviceInstances = Objects.requireNonNull(supplier.get().blockFirst());
assertThat(serviceInstances).hasSize(101);
Expand All @@ -102,14 +107,15 @@ void shouldReturnSameSublistForSameInstanceId() {
.mapToObj(i -> new DefaultServiceInstance(Integer.toString(i), "test", "host" + i, 8080, false, null))
.collect(Collectors.toList());

when(delegate.getServiceId()).thenReturn("test");
when(delegate.get()).thenReturn(Flux.just(instances));

SubsetServiceInstanceListSupplier supplier1 = new SubsetServiceInstanceListSupplier(delegate, env,
subsetProperties("foobar", 5));
factory("foobar", 5));
List<ServiceInstance> serviceInstances1 = Objects.requireNonNull(supplier1.get().blockFirst());

SubsetServiceInstanceListSupplier supplier2 = new SubsetServiceInstanceListSupplier(delegate, env,
subsetProperties("foobar", 5));
factory("foobar", 5));
List<ServiceInstance> serviceInstances2 = Objects.requireNonNull(supplier2.get().blockFirst());

assertThat(serviceInstances1).isEqualTo(serviceInstances2);
Expand All @@ -121,26 +127,28 @@ void shouldReturnDifferentSublistForDifferentInstanceId() {
.mapToObj(i -> new DefaultServiceInstance(Integer.toString(i), "test", "host" + i, 8080, false, null))
.collect(Collectors.toList());

when(delegate.getServiceId()).thenReturn("test");
when(delegate.get()).thenReturn(Flux.just(instances));

SubsetServiceInstanceListSupplier supplier1 = new SubsetServiceInstanceListSupplier(delegate, env,
subsetProperties("foobar1", 5));
factory("foobar1", 5));
List<ServiceInstance> serviceInstances1 = Objects.requireNonNull(supplier1.get().blockFirst());

SubsetServiceInstanceListSupplier supplier2 = new SubsetServiceInstanceListSupplier(delegate, env,
subsetProperties("foobar2", 5));
factory("foobar2", 5));
List<ServiceInstance> serviceInstances2 = Objects.requireNonNull(supplier2.get().blockFirst());

assertThat(serviceInstances1).isNotEqualTo(serviceInstances2);
}

LoadBalancerProperties subsetProperties(String instanceId, int size) {
ReactiveLoadBalancer.Factory<ServiceInstance> factory(String instanceId, int size) {
LoadBalancerProperties properties = new LoadBalancerProperties();
LoadBalancerProperties.Subset subset = new LoadBalancerProperties.Subset();
subset.setInstanceId(instanceId);
subset.setSize(size);
properties.setSubset(subset);
return properties;

return buildLoadBalancerClientFactory("test", properties);
}

}

0 comments on commit 9935bc5

Please sign in to comment.