diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/DefaultRequest.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/DefaultRequest.java index a4ead5b9..5641126b 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/DefaultRequest.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/DefaultRequest.java @@ -17,8 +17,30 @@ package org.springframework.cloud.client.loadbalancer.reactive; /** + * A default implementation of {@link Request}. + * * @author Spencer Gibb + * @author Olga Maciaszek-Sharma */ -public class DefaultRequest implements Request { +public class DefaultRequest implements Request { + + private T context; + + public DefaultRequest() { + new DefaultRequestContext(); + } + + public DefaultRequest(T context) { + this.context = context; + } + + @Override + public T getContext() { + return context; + } + + public void setContext(T context) { + this.context = context; + } } diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/DefaultRequestContext.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/DefaultRequestContext.java new file mode 100644 index 00000000..65e7ad43 --- /dev/null +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/DefaultRequestContext.java @@ -0,0 +1,47 @@ +/* + * Copyright 2012-2019 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.reactive; + +/** + * Contains information relevant to the request. + * + * @author Olga Maciaszek-Sharma + */ +public class DefaultRequestContext { + + /** + * A {@link String} value of hint that can be used to choose the correct service + * instance. + */ + private String hint = "default"; + + public DefaultRequestContext() { + } + + public DefaultRequestContext(String hint) { + this.hint = hint; + } + + public String getHint() { + return hint; + } + + public void setHint(String hint) { + this.hint = hint; + } + +} diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/ReactiveLoadBalancer.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/ReactiveLoadBalancer.java index 37eb3561..2e35561d 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/ReactiveLoadBalancer.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/ReactiveLoadBalancer.java @@ -30,13 +30,14 @@ public interface ReactiveLoadBalancer { /** * Default implementation of a request. */ - Request REQUEST = new DefaultRequest(); + Request REQUEST = new DefaultRequest(); /** * Choose the next server based on the load balancing algorithm. * @param request - incoming request * @return publisher for the response */ + @SuppressWarnings("rawtypes") Publisher> choose(Request request); default Publisher> choose() { // conflicting name diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/Request.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/Request.java index 9116082c..7a12bd47 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/Request.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/Request.java @@ -20,8 +20,14 @@ package org.springframework.cloud.client.loadbalancer.reactive; * Marker interface for a request. * * @author Spencer Gibb + * @author Olga Maciaszek-Sharma */ -public interface Request { +public interface Request { + + // Avoid breaking backward compatibility + default C getContext() { + return null; + } // TODO: define contents diff --git a/spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/core/ReactorLoadBalancer.java b/spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/core/ReactorLoadBalancer.java index ce0151e2..0b32c5c6 100644 --- a/spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/core/ReactorLoadBalancer.java +++ b/spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/core/ReactorLoadBalancer.java @@ -35,6 +35,7 @@ public interface ReactorLoadBalancer extends ReactiveLoadBalancer { * @param request - an input request * @return - mono of response */ + @SuppressWarnings("rawtypes") Mono> choose(Request request); default Mono> choose() { diff --git a/spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/core/RoundRobinLoadBalancer.java b/spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/core/RoundRobinLoadBalancer.java index 9b74ff31..9dd6bc72 100644 --- a/spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/core/RoundRobinLoadBalancer.java +++ b/spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/core/RoundRobinLoadBalancer.java @@ -104,6 +104,7 @@ public class RoundRobinLoadBalancer implements ReactorServiceInstanceLoadBalance this.position = new AtomicInteger(seedPosition); } + @SuppressWarnings("rawtypes") @Override // see original // https://github.com/Netflix/ocelli/blob/master/ocelli-core/ diff --git a/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/LoadBalancerTests.java b/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/LoadBalancerTests.java index a60b498f..c1d1d47b 100644 --- a/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/LoadBalancerTests.java +++ b/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/LoadBalancerTests.java @@ -24,6 +24,7 @@ import org.junit.runner.RunWith; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -33,7 +34,11 @@ import org.springframework.cloud.client.DefaultServiceInstance; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.loadbalancer.reactive.CompletionContext; import org.springframework.cloud.client.loadbalancer.reactive.CompletionContext.Status; +import org.springframework.cloud.client.loadbalancer.reactive.DefaultRequest; +import org.springframework.cloud.client.loadbalancer.reactive.DefaultRequestContext; +import org.springframework.cloud.client.loadbalancer.reactive.DefaultResponse; import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer; +import org.springframework.cloud.client.loadbalancer.reactive.Request; import org.springframework.cloud.client.loadbalancer.reactive.Response; import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient; import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients; @@ -45,6 +50,7 @@ import org.springframework.core.ResolvableType; import org.springframework.core.env.Environment; import org.springframework.test.context.junit4.SpringRunner; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.BDDAssertions.then; /** @@ -125,6 +131,11 @@ public class LoadBalancerTests { assertLoadBalancer(loadBalancer, Arrays.asList("1host", "2host-secure")); } + private static DefaultServiceInstance instance(String serviceId, String host, + boolean secure) { + return new DefaultServiceInstance(serviceId, serviceId, host, 80, secure); + } + @Test public void staticConfigurationWorksWithServiceInstanceListSupplier() { String serviceId = "test1"; @@ -136,9 +147,44 @@ public class LoadBalancerTests { assertLoadBalancer(loadBalancer, Arrays.asList("1host", "2host-secure")); } - private DefaultServiceInstance instance(String serviceId, String host, - boolean secure) { - return new DefaultServiceInstance(serviceId, serviceId, host, 80, secure); + @SuppressWarnings("ConstantConditions") + @Test + public void canPassHintViaRequest() { + String serviceId = "test1"; + RoundRobinLoadBalancer loadBalancer = new TestHintLoadBalancer( + ServiceInstanceListSuppliers.toProvider(serviceId, + instance(serviceId, "1host", false), + instance(serviceId, "2host-secure", true)), + serviceId); + Request request = new DefaultRequest<>( + new DefaultRequestContext("test2")); + + ServiceInstance serviceInstance = loadBalancer.choose(request).block() + .getServer(); + + assertThat(serviceInstance.getServiceId()).isEqualTo("test2"); + } + + private static class TestHintLoadBalancer extends RoundRobinLoadBalancer { + + TestHintLoadBalancer( + ObjectProvider serviceInstanceListSupplierProvider, + String serviceId) { + super(serviceInstanceListSupplierProvider, serviceId); + } + + @SuppressWarnings("rawtypes") + @Override + public Mono> choose(Request request) { + if (request.getContext() instanceof DefaultRequestContext) { + DefaultRequestContext requestContext = (DefaultRequestContext) request + .getContext(); + return Mono.just(new DefaultResponse( + instance(requestContext.getHint(), "host", false))); + } + return Mono.empty(); + } + } @EnableAutoConfiguration