From 690d1d3cef90f793c965b77681b41006fdd4cee8 Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Thu, 21 Apr 2022 16:12:06 +0100 Subject: [PATCH] Request data context in blocking lb client (#1100) --- .../AsyncLoadBalancerInterceptor.java | 10 +-- .../AsyncLoadBalancerRequest.java | 69 ++++++++++++++++ .../BlockingLoadBalancerRequest.java | 79 +++++++++++++++++++ .../HttpRequestLoadBalancerRequest.java | 31 ++++++++ .../LoadBalancerRequestFactory.java | 20 ++--- .../client/BlockingLoadBalancerClient.java | 20 ++++- 6 files changed, 209 insertions(+), 20 deletions(-) create mode 100644 spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/AsyncLoadBalancerRequest.java create mode 100644 spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/BlockingLoadBalancerRequest.java create mode 100644 spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/HttpRequestLoadBalancerRequest.java diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/AsyncLoadBalancerInterceptor.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/AsyncLoadBalancerInterceptor.java index 6d00965d..6e07677f 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/AsyncLoadBalancerInterceptor.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/AsyncLoadBalancerInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2022 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. @@ -27,6 +27,7 @@ import org.springframework.util.concurrent.ListenableFuture; /** * @author Rob Worsnop + * @author Olga Maciaszek-Sharma */ public class AsyncLoadBalancerInterceptor implements AsyncClientHttpRequestInterceptor { @@ -41,11 +42,8 @@ public class AsyncLoadBalancerInterceptor implements AsyncClientHttpRequestInter final AsyncClientHttpRequestExecution execution) throws IOException { final URI originalUri = request.getURI(); String serviceName = originalUri.getHost(); - return this.loadBalancer.execute(serviceName, instance -> { - HttpRequest serviceRequest = new ServiceRequestWrapper(request, instance, - AsyncLoadBalancerInterceptor.this.loadBalancer); - return execution.executeAsync(serviceRequest, body); - }); + return loadBalancer.execute(serviceName, new AsyncLoadBalancerRequest(loadBalancer, + new AsyncLoadBalancerRequest.ClientHttpRequestData(request, body, execution))); } } diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/AsyncLoadBalancerRequest.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/AsyncLoadBalancerRequest.java new file mode 100644 index 00000000..4005fd05 --- /dev/null +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/AsyncLoadBalancerRequest.java @@ -0,0 +1,69 @@ +/* + * Copyright 2012-2022 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; + +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.http.HttpRequest; +import org.springframework.http.client.AsyncClientHttpRequestExecution; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.util.concurrent.ListenableFuture; + +/** + * Default {@link LoadBalancerRequest} implementation for async use-cases. + * + * @author Olga Maciaszek-Sharma + * @since 3.1.2 + */ +class AsyncLoadBalancerRequest implements HttpRequestLoadBalancerRequest> { + + private final LoadBalancerClient loadBalancer; + + private final ClientHttpRequestData clientHttpRequestData; + + AsyncLoadBalancerRequest(LoadBalancerClient loadBalancer, ClientHttpRequestData clientHttpRequestData) { + this.loadBalancer = loadBalancer; + this.clientHttpRequestData = clientHttpRequestData; + } + + @Override + public ListenableFuture apply(ServiceInstance instance) throws Exception { + HttpRequest serviceRequest = new ServiceRequestWrapper(clientHttpRequestData.request, instance, loadBalancer); + return clientHttpRequestData.execution.executeAsync(serviceRequest, clientHttpRequestData.body); + } + + @Override + public HttpRequest getHttpRequest() { + return clientHttpRequestData.request; + } + + static class ClientHttpRequestData { + + private final HttpRequest request; + + private final byte[] body; + + private final AsyncClientHttpRequestExecution execution; + + ClientHttpRequestData(HttpRequest request, byte[] body, AsyncClientHttpRequestExecution execution) { + this.request = request; + this.body = body; + this.execution = execution; + } + + } + +} diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/BlockingLoadBalancerRequest.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/BlockingLoadBalancerRequest.java new file mode 100644 index 00000000..d51e247f --- /dev/null +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/BlockingLoadBalancerRequest.java @@ -0,0 +1,79 @@ +/* + * Copyright 2012-2022 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; + +import java.util.List; + +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.http.HttpRequest; +import org.springframework.http.client.ClientHttpRequestExecution; +import org.springframework.http.client.ClientHttpResponse; + +/** + * Default {@link LoadBalancerRequest} implementation. + * + * @author Olga Maciaszek-Sharma + * @since 3.1.2 + */ +class BlockingLoadBalancerRequest implements HttpRequestLoadBalancerRequest { + + private final LoadBalancerClient loadBalancer; + + private final List transformers; + + private final ClientHttpRequestData clientHttpRequestData; + + BlockingLoadBalancerRequest(LoadBalancerClient loadBalancer, List transformers, + ClientHttpRequestData clientHttpRequestData) { + this.loadBalancer = loadBalancer; + this.transformers = transformers; + this.clientHttpRequestData = clientHttpRequestData; + } + + @Override + public ClientHttpResponse apply(ServiceInstance instance) throws Exception { + HttpRequest serviceRequest = new ServiceRequestWrapper(clientHttpRequestData.request, instance, loadBalancer); + if (this.transformers != null) { + for (LoadBalancerRequestTransformer transformer : this.transformers) { + serviceRequest = transformer.transformRequest(serviceRequest, instance); + } + } + return clientHttpRequestData.execution.execute(serviceRequest, clientHttpRequestData.body); + } + + @Override + public HttpRequest getHttpRequest() { + return clientHttpRequestData.request; + } + + static class ClientHttpRequestData { + + private final HttpRequest request; + + private final byte[] body; + + private final ClientHttpRequestExecution execution; + + ClientHttpRequestData(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) { + this.request = request; + this.body = body; + this.execution = execution; + } + + } + +} diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/HttpRequestLoadBalancerRequest.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/HttpRequestLoadBalancerRequest.java new file mode 100644 index 00000000..9c4ae023 --- /dev/null +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/HttpRequestLoadBalancerRequest.java @@ -0,0 +1,31 @@ +/* + * Copyright 2012-2022 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; + +import org.springframework.http.HttpRequest; + +/** + * Represents a {@link LoadBalancerRequest} created on top of an {@link HttpRequest}. + * + * @author Olga Maciaszek-Sharma + * @since 3.1.2 + */ +public interface HttpRequestLoadBalancerRequest extends LoadBalancerRequest { + + HttpRequest getHttpRequest(); + +} diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/LoadBalancerRequestFactory.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/LoadBalancerRequestFactory.java index 2e45bd63..b8a06fbb 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/LoadBalancerRequestFactory.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/LoadBalancerRequestFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2022 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. @@ -16,6 +16,7 @@ package org.springframework.cloud.client.loadbalancer; +import java.util.ArrayList; import java.util.List; import org.springframework.http.HttpRequest; @@ -28,13 +29,14 @@ import org.springframework.http.client.ClientHttpResponse; * to the intercepted {@link HttpRequest}. * * @author William Tran + * @author Olga Maciaszek-Sharma * */ public class LoadBalancerRequestFactory { - private LoadBalancerClient loadBalancer; + private final LoadBalancerClient loadBalancer; - private List transformers; + private final List transformers; public LoadBalancerRequestFactory(LoadBalancerClient loadBalancer, List transformers) { @@ -44,19 +46,13 @@ public class LoadBalancerRequestFactory { public LoadBalancerRequestFactory(LoadBalancerClient loadBalancer) { this.loadBalancer = loadBalancer; + transformers = new ArrayList<>(); } public LoadBalancerRequest createRequest(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) { - return instance -> { - HttpRequest serviceRequest = new ServiceRequestWrapper(request, instance, this.loadBalancer); - if (this.transformers != null) { - for (LoadBalancerRequestTransformer transformer : this.transformers) { - serviceRequest = transformer.transformRequest(serviceRequest, instance); - } - } - return execution.execute(serviceRequest, body); - }; + return new BlockingLoadBalancerRequest(loadBalancer, transformers, + new BlockingLoadBalancerRequest.ClientHttpRequestData(request, body, execution)); } } diff --git a/spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/blocking/client/BlockingLoadBalancerClient.java b/spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/blocking/client/BlockingLoadBalancerClient.java index 8e8a6aac..8cc4bdcc 100644 --- a/spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/blocking/client/BlockingLoadBalancerClient.java +++ b/spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/blocking/client/BlockingLoadBalancerClient.java @@ -28,6 +28,7 @@ import org.springframework.cloud.client.loadbalancer.DefaultRequest; import org.springframework.cloud.client.loadbalancer.DefaultRequestContext; import org.springframework.cloud.client.loadbalancer.DefaultResponse; import org.springframework.cloud.client.loadbalancer.EmptyResponse; +import org.springframework.cloud.client.loadbalancer.HttpRequestLoadBalancerRequest; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.cloud.client.loadbalancer.LoadBalancerLifecycle; import org.springframework.cloud.client.loadbalancer.LoadBalancerLifecycleValidator; @@ -36,10 +37,14 @@ import org.springframework.cloud.client.loadbalancer.LoadBalancerRequest; import org.springframework.cloud.client.loadbalancer.LoadBalancerRequestAdapter; import org.springframework.cloud.client.loadbalancer.LoadBalancerUriTools; import org.springframework.cloud.client.loadbalancer.Request; +import org.springframework.cloud.client.loadbalancer.RequestData; +import org.springframework.cloud.client.loadbalancer.RequestDataContext; import org.springframework.cloud.client.loadbalancer.Response; import org.springframework.cloud.client.loadbalancer.ResponseData; +import org.springframework.cloud.client.loadbalancer.TimedRequestContext; import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer; import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory; +import org.springframework.http.HttpRequest; import org.springframework.http.client.ClientHttpResponse; import org.springframework.util.ReflectionUtils; @@ -73,8 +78,8 @@ public class BlockingLoadBalancerClient implements LoadBalancerClient { @Override public T execute(String serviceId, LoadBalancerRequest request) throws IOException { String hint = getHint(serviceId); - LoadBalancerRequestAdapter lbRequest = new LoadBalancerRequestAdapter<>(request, - new DefaultRequestContext(request, hint)); + LoadBalancerRequestAdapter lbRequest = new LoadBalancerRequestAdapter<>(request, + buildRequestContext(request, hint)); Set supportedLifecycleProcessors = getSupportedLifecycleProcessors(serviceId); supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onStart(lbRequest)); ServiceInstance serviceInstance = choose(serviceId, lbRequest); @@ -86,6 +91,17 @@ public class BlockingLoadBalancerClient implements LoadBalancerClient { return execute(serviceId, serviceInstance, lbRequest); } + private TimedRequestContext buildRequestContext(LoadBalancerRequest delegate, String hint) { + if (delegate instanceof HttpRequestLoadBalancerRequest) { + HttpRequest request = ((HttpRequestLoadBalancerRequest) delegate).getHttpRequest(); + if (request != null) { + RequestData requestData = new RequestData(request); + return new RequestDataContext(requestData, hint); + } + } + return new DefaultRequestContext(delegate, hint); + } + @Override public T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest request) throws IOException {