From 5bddccf720b78888ae32df9ad68d18d8b7a784e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E9=AD=8F=2C=E5=B0=8F=E9=AD=8F=2C=E6=88=91?= =?UTF-8?q?=E4=BB=AC=E8=A6=81=E5=8E=BB=E5=93=AA=E9=87=8C=E5=91=80?= <30821411+galaxy-sea@users.noreply.github.com> Date: Thu, 1 Sep 2022 17:38:09 +0800 Subject: [PATCH] Customize load balanced requests according to the chosen ServiceInstance (#735) --- .../main/asciidoc/spring-cloud-openfeign.adoc | 27 +++++++++++ ...DefaultFeignLoadBalancerConfiguration.java | 13 ++++-- .../FeignBlockingLoadBalancerClient.java | 35 +++++++++++++- ...Client5FeignLoadBalancerConfiguration.java | 14 ++++-- ...pClientFeignLoadBalancerConfiguration.java | 14 ++++-- .../LoadBalancerFeignRequestTransformer.java | 46 +++++++++++++++++++ .../OkHttpFeignLoadBalancerConfiguration.java | 14 ++++-- ...ryableFeignBlockingLoadBalancerClient.java | 35 +++++++++++++- .../FeignBlockingLoadBalancerClientTests.java | 36 ++++++++++++++- ...eFeignBlockingLoadBalancerClientTests.java | 36 ++++++++++++++- 10 files changed, 246 insertions(+), 24 deletions(-) create mode 100644 spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/LoadBalancerFeignRequestTransformer.java diff --git a/docs/src/main/asciidoc/spring-cloud-openfeign.adoc b/docs/src/main/asciidoc/spring-cloud-openfeign.adoc index 39103a56..994476dd 100644 --- a/docs/src/main/asciidoc/spring-cloud-openfeign.adoc +++ b/docs/src/main/asciidoc/spring-cloud-openfeign.adoc @@ -815,6 +815,33 @@ Sometimes, when load balancing is enabled for Feign clients, you may want to use spring.cloud.openfeign.oauth2.load-balanced=true ---- +=== Transform the load-balanced HTTP request + +You can use the selected `ServiceInstance` to transform the load-balanced HTTP Request. + +For `Request`, you need to implement and define `LoadBalancerFeignRequestTransformer`, as follows: + +[source,java,indent=0] +---- + @Bean + public LoadBalancerFeignRequestTransformer transformer() { + return new LoadBalancerFeignRequestTransformer() { + + @Override + public Request transformRequest(Request request, ServiceInstance instance) { + Map> headers = new HashMap<>(request.headers()); + headers.put("X-ServiceId", Collections.singletonList(instance.getServiceId())); + headers.put("X-InstanceId", Collections.singletonList(instance.getInstanceId())); + return Request.create(request.httpMethod(), request.url(), headers, request.body(), request.charset(), + request.requestTemplate()); + } + }; + } +---- + +If multiple transformers are defined, they are applied in the order in which beans are defined. +Alternatively, you can use `LoadBalancerFeignRequestTransformer.DEFAULT_ORDER` to specify the order. + == Configuration properties To see the list of all Spring Cloud OpenFeign related configuration properties please check link:appendix.html[the Appendix page]. diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/DefaultFeignLoadBalancerConfiguration.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/DefaultFeignLoadBalancerConfiguration.java index 4c6a159f..e5a07ede 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/DefaultFeignLoadBalancerConfiguration.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/DefaultFeignLoadBalancerConfiguration.java @@ -16,6 +16,8 @@ package org.springframework.cloud.openfeign.loadbalancer; +import java.util.List; + import feign.Client; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; @@ -36,6 +38,7 @@ import org.springframework.context.annotation.Configuration; * that uses {@link Client.Default} under the hood. * * @author Olga Maciaszek-Sharma + * @author changjin wei(魏昌进) * @since 2.2.0 */ @Configuration(proxyBeanMethods = false) @@ -46,9 +49,10 @@ class DefaultFeignLoadBalancerConfiguration { @ConditionalOnMissingBean @Conditional(OnRetryNotEnabledCondition.class) public Client feignClient(LoadBalancerClient loadBalancerClient, - LoadBalancerClientFactory loadBalancerClientFactory) { + LoadBalancerClientFactory loadBalancerClientFactory, + List transformers) { return new FeignBlockingLoadBalancerClient(new Client.Default(null, null), loadBalancerClient, - loadBalancerClientFactory); + loadBalancerClientFactory, transformers); } @Bean @@ -58,9 +62,10 @@ class DefaultFeignLoadBalancerConfiguration { @ConditionalOnProperty(value = "spring.cloud.loadbalancer.retry.enabled", havingValue = "true", matchIfMissing = true) public Client feignRetryClient(LoadBalancerClient loadBalancerClient, - LoadBalancedRetryFactory loadBalancedRetryFactory, LoadBalancerClientFactory loadBalancerClientFactory) { + LoadBalancedRetryFactory loadBalancedRetryFactory, LoadBalancerClientFactory loadBalancerClientFactory, + List transformers) { return new RetryableFeignBlockingLoadBalancerClient(new Client.Default(null, null), loadBalancerClient, - loadBalancedRetryFactory, loadBalancerClientFactory); + loadBalancedRetryFactory, loadBalancerClientFactory, transformers); } } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/FeignBlockingLoadBalancerClient.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/FeignBlockingLoadBalancerClient.java index e0fe6e1e..e7f2d446 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/FeignBlockingLoadBalancerClient.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/FeignBlockingLoadBalancerClient.java @@ -19,6 +19,8 @@ package org.springframework.cloud.openfeign.loadbalancer; import java.io.IOException; import java.net.URI; import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.List; import java.util.Set; import feign.Client; @@ -49,6 +51,7 @@ import static org.springframework.cloud.openfeign.loadbalancer.LoadBalancerUtils * {@link ServiceInstance} to use while resolving the request host. * * @author Olga Maciaszek-Sharma + * @author changjin wei(魏昌进) * @since 2.2.0 */ @SuppressWarnings({ "unchecked", "rawtypes" }) @@ -62,9 +65,11 @@ public class FeignBlockingLoadBalancerClient implements Client { private final LoadBalancerClientFactory loadBalancerClientFactory; + private final List transformers; + /** * @deprecated in favour of - * {@link FeignBlockingLoadBalancerClient#FeignBlockingLoadBalancerClient(Client, LoadBalancerClient, LoadBalancerClientFactory)} + * {@link FeignBlockingLoadBalancerClient#FeignBlockingLoadBalancerClient(Client, LoadBalancerClient, LoadBalancerClientFactory, List)} */ @Deprecated public FeignBlockingLoadBalancerClient(Client delegate, LoadBalancerClient loadBalancerClient, @@ -72,13 +77,29 @@ public class FeignBlockingLoadBalancerClient implements Client { this.delegate = delegate; this.loadBalancerClient = loadBalancerClient; this.loadBalancerClientFactory = loadBalancerClientFactory; + this.transformers = Collections.emptyList(); } + /** + * @deprecated in favour of + * {@link FeignBlockingLoadBalancerClient#FeignBlockingLoadBalancerClient(Client, LoadBalancerClient, LoadBalancerClientFactory, List)} + */ + @Deprecated public FeignBlockingLoadBalancerClient(Client delegate, LoadBalancerClient loadBalancerClient, LoadBalancerClientFactory loadBalancerClientFactory) { this.delegate = delegate; this.loadBalancerClient = loadBalancerClient; this.loadBalancerClientFactory = loadBalancerClientFactory; + this.transformers = Collections.emptyList(); + } + + public FeignBlockingLoadBalancerClient(Client delegate, LoadBalancerClient loadBalancerClient, + LoadBalancerClientFactory loadBalancerClientFactory, + List transformers) { + this.delegate = delegate; + this.loadBalancerClient = loadBalancerClient; + this.loadBalancerClientFactory = loadBalancerClientFactory; + this.transformers = transformers; } @Override @@ -109,7 +130,7 @@ public class FeignBlockingLoadBalancerClient implements Client { .body(message, StandardCharsets.UTF_8).build(); } String reconstructedUrl = loadBalancerClient.reconstructURI(instance, originalUri).toString(); - Request newRequest = buildRequest(request, reconstructedUrl); + Request newRequest = buildRequest(request, reconstructedUrl, instance); return executeWithLoadBalancerLifecycleProcessing(delegate, options, newRequest, lbRequest, lbResponse, supportedLifecycleProcessors); } @@ -119,6 +140,16 @@ public class FeignBlockingLoadBalancerClient implements Client { request.charset(), request.requestTemplate()); } + protected Request buildRequest(Request request, String reconstructedUrl, ServiceInstance instance) { + Request newRequest = buildRequest(request, reconstructedUrl); + if (transformers != null) { + for (LoadBalancerFeignRequestTransformer transformer : transformers) { + newRequest = transformer.transformRequest(newRequest, instance); + } + } + return newRequest; + } + // Visible for Sleuth instrumentation public Client getDelegate() { return delegate; diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/HttpClient5FeignLoadBalancerConfiguration.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/HttpClient5FeignLoadBalancerConfiguration.java index 3cd32c17..debc0231 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/HttpClient5FeignLoadBalancerConfiguration.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/HttpClient5FeignLoadBalancerConfiguration.java @@ -16,6 +16,8 @@ package org.springframework.cloud.openfeign.loadbalancer; +import java.util.List; + import feign.Client; import feign.hc5.ApacheHttp5Client; import org.apache.hc.client5.http.classic.HttpClient; @@ -40,6 +42,7 @@ import org.springframework.context.annotation.Import; * that uses {@link ApacheHttp5Client} under the hood. * * @author Nguyen Ky Thanh + * @author changjin wei(魏昌进) */ @Configuration(proxyBeanMethods = false) @ConditionalOnClass(ApacheHttp5Client.class) @@ -53,9 +56,11 @@ class HttpClient5FeignLoadBalancerConfiguration { @ConditionalOnMissingBean @Conditional(OnRetryNotEnabledCondition.class) public Client feignClient(LoadBalancerClient loadBalancerClient, HttpClient httpClient5, - LoadBalancerClientFactory loadBalancerClientFactory) { + LoadBalancerClientFactory loadBalancerClientFactory, + List transformers) { Client delegate = new ApacheHttp5Client(httpClient5); - return new FeignBlockingLoadBalancerClient(delegate, loadBalancerClient, loadBalancerClientFactory); + return new FeignBlockingLoadBalancerClient(delegate, loadBalancerClient, loadBalancerClientFactory, + transformers); } @Bean @@ -65,10 +70,11 @@ class HttpClient5FeignLoadBalancerConfiguration { @ConditionalOnProperty(value = "spring.cloud.loadbalancer.retry.enabled", havingValue = "true", matchIfMissing = true) public Client feignRetryClient(LoadBalancerClient loadBalancerClient, HttpClient httpClient5, - LoadBalancedRetryFactory loadBalancedRetryFactory, LoadBalancerClientFactory loadBalancerClientFactory) { + LoadBalancedRetryFactory loadBalancedRetryFactory, LoadBalancerClientFactory loadBalancerClientFactory, + List transformers) { Client delegate = new ApacheHttp5Client(httpClient5); return new RetryableFeignBlockingLoadBalancerClient(delegate, loadBalancerClient, loadBalancedRetryFactory, - loadBalancerClientFactory); + loadBalancerClientFactory, transformers); } } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/HttpClientFeignLoadBalancerConfiguration.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/HttpClientFeignLoadBalancerConfiguration.java index e7143f72..2c2db333 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/HttpClientFeignLoadBalancerConfiguration.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/HttpClientFeignLoadBalancerConfiguration.java @@ -16,6 +16,8 @@ package org.springframework.cloud.openfeign.loadbalancer; +import java.util.List; + import feign.Client; import feign.httpclient.ApacheHttpClient; import org.apache.http.client.HttpClient; @@ -42,6 +44,7 @@ import org.springframework.context.annotation.Import; * * @author Olga Maciaszek-Sharma * @author Nguyen Ky Thanh + * @author changjin wei(魏昌进) * @since 2.2.0 */ @Configuration(proxyBeanMethods = false) @@ -57,9 +60,11 @@ class HttpClientFeignLoadBalancerConfiguration { @ConditionalOnMissingBean @Conditional(OnRetryNotEnabledCondition.class) public Client feignClient(LoadBalancerClient loadBalancerClient, HttpClient httpClient, - LoadBalancerClientFactory loadBalancerClientFactory) { + LoadBalancerClientFactory loadBalancerClientFactory, + List transformers) { ApacheHttpClient delegate = new ApacheHttpClient(httpClient); - return new FeignBlockingLoadBalancerClient(delegate, loadBalancerClient, loadBalancerClientFactory); + return new FeignBlockingLoadBalancerClient(delegate, loadBalancerClient, loadBalancerClientFactory, + transformers); } @Bean @@ -69,10 +74,11 @@ class HttpClientFeignLoadBalancerConfiguration { @ConditionalOnProperty(value = "spring.cloud.loadbalancer.retry.enabled", havingValue = "true", matchIfMissing = true) public Client feignRetryClient(LoadBalancerClient loadBalancerClient, HttpClient httpClient, - LoadBalancedRetryFactory loadBalancedRetryFactory, LoadBalancerClientFactory loadBalancerClientFactory) { + LoadBalancedRetryFactory loadBalancedRetryFactory, LoadBalancerClientFactory loadBalancerClientFactory, + List transformers) { ApacheHttpClient delegate = new ApacheHttpClient(httpClient); return new RetryableFeignBlockingLoadBalancerClient(delegate, loadBalancerClient, loadBalancedRetryFactory, - loadBalancerClientFactory); + loadBalancerClientFactory, transformers); } } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/LoadBalancerFeignRequestTransformer.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/LoadBalancerFeignRequestTransformer.java new file mode 100644 index 00000000..dd573219 --- /dev/null +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/LoadBalancerFeignRequestTransformer.java @@ -0,0 +1,46 @@ +/* + * Copyright 2013-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.openfeign.loadbalancer; + +import feign.Request; + +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.core.annotation.Order; + +/** + * Allows applications to transform the load-balanced {@link Request} given the chosen + * {@link org.springframework.cloud.client.ServiceInstance}. + * + * @author changjin wei(魏昌进) + */ +@Order(LoadBalancerFeignRequestTransformer.DEFAULT_ORDER) +public interface LoadBalancerFeignRequestTransformer { + + /** + * Order for the {@link LoadBalancerFeignRequestTransformer}. + */ + int DEFAULT_ORDER = 0; + + /** + * Allows transforming load-balanced requests based on the provided {@link ServiceInstance}. + * @param request Original request. + * @param instance ServiceInstance returned from LoadBalancer. + * @return New request or original request + */ + Request transformRequest(Request request, ServiceInstance instance); + +} diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/OkHttpFeignLoadBalancerConfiguration.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/OkHttpFeignLoadBalancerConfiguration.java index 9cb28ee0..08f14bec 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/OkHttpFeignLoadBalancerConfiguration.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/OkHttpFeignLoadBalancerConfiguration.java @@ -16,6 +16,8 @@ package org.springframework.cloud.openfeign.loadbalancer; +import java.util.List; + import feign.Client; import feign.okhttp.OkHttpClient; @@ -39,6 +41,7 @@ import org.springframework.context.annotation.Import; * that uses {@link OkHttpClient} under the hood. * * @author Olga Maciaszek-Sharma + * @author changjin wei(魏昌进) * @since 2.2.0 */ @Configuration(proxyBeanMethods = false) @@ -53,9 +56,11 @@ class OkHttpFeignLoadBalancerConfiguration { @ConditionalOnMissingBean @Conditional(OnRetryNotEnabledCondition.class) public Client feignClient(okhttp3.OkHttpClient okHttpClient, LoadBalancerClient loadBalancerClient, - LoadBalancerClientFactory loadBalancerClientFactory) { + LoadBalancerClientFactory loadBalancerClientFactory, + List transformers) { OkHttpClient delegate = new OkHttpClient(okHttpClient); - return new FeignBlockingLoadBalancerClient(delegate, loadBalancerClient, loadBalancerClientFactory); + return new FeignBlockingLoadBalancerClient(delegate, loadBalancerClient, loadBalancerClientFactory, + transformers); } @Bean @@ -65,10 +70,11 @@ class OkHttpFeignLoadBalancerConfiguration { @ConditionalOnProperty(value = "spring.cloud.loadbalancer.retry.enabled", havingValue = "true", matchIfMissing = true) public Client feignRetryClient(LoadBalancerClient loadBalancerClient, okhttp3.OkHttpClient okHttpClient, - LoadBalancedRetryFactory loadBalancedRetryFactory, LoadBalancerClientFactory loadBalancerClientFactory) { + LoadBalancedRetryFactory loadBalancedRetryFactory, LoadBalancerClientFactory loadBalancerClientFactory, + List transformers) { OkHttpClient delegate = new OkHttpClient(okHttpClient); return new RetryableFeignBlockingLoadBalancerClient(delegate, loadBalancerClient, loadBalancedRetryFactory, - loadBalancerClientFactory); + loadBalancerClientFactory, transformers); } } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/RetryableFeignBlockingLoadBalancerClient.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/RetryableFeignBlockingLoadBalancerClient.java index f9c3a7c5..26dac934 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/RetryableFeignBlockingLoadBalancerClient.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/RetryableFeignBlockingLoadBalancerClient.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.net.URI; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -65,6 +66,7 @@ import static org.springframework.cloud.openfeign.loadbalancer.LoadBalancerUtils * load-balanced with Spring Cloud LoadBalancer. * * @author Olga Maciaszek-Sharma + * @author changjin wei(魏昌进) * @since 2.2.6 */ @SuppressWarnings({ "rawtypes", "unchecked" }) @@ -80,9 +82,11 @@ public class RetryableFeignBlockingLoadBalancerClient implements Client { private final LoadBalancerClientFactory loadBalancerClientFactory; + private final List transformers; + /** * @deprecated in favour of - * {@link RetryableFeignBlockingLoadBalancerClient#RetryableFeignBlockingLoadBalancerClient(Client, LoadBalancerClient, LoadBalancedRetryFactory, LoadBalancerClientFactory)} + * {@link RetryableFeignBlockingLoadBalancerClient#RetryableFeignBlockingLoadBalancerClient(Client, LoadBalancerClient, LoadBalancedRetryFactory, LoadBalancerClientFactory, List)} */ @Deprecated public RetryableFeignBlockingLoadBalancerClient(Client delegate, LoadBalancerClient loadBalancerClient, @@ -92,14 +96,31 @@ public class RetryableFeignBlockingLoadBalancerClient implements Client { this.loadBalancerClient = loadBalancerClient; this.loadBalancedRetryFactory = loadBalancedRetryFactory; this.loadBalancerClientFactory = loadBalancerClientFactory; + this.transformers = Collections.emptyList(); } + /** + * @deprecated in favour of + * {@link RetryableFeignBlockingLoadBalancerClient#RetryableFeignBlockingLoadBalancerClient(Client, LoadBalancerClient, LoadBalancedRetryFactory, LoadBalancerClientFactory, List)} + */ + @Deprecated public RetryableFeignBlockingLoadBalancerClient(Client delegate, LoadBalancerClient loadBalancerClient, LoadBalancedRetryFactory loadBalancedRetryFactory, LoadBalancerClientFactory loadBalancerClientFactory) { this.delegate = delegate; this.loadBalancerClient = loadBalancerClient; this.loadBalancedRetryFactory = loadBalancedRetryFactory; this.loadBalancerClientFactory = loadBalancerClientFactory; + this.transformers = Collections.emptyList(); + } + + public RetryableFeignBlockingLoadBalancerClient(Client delegate, LoadBalancerClient loadBalancerClient, + LoadBalancedRetryFactory loadBalancedRetryFactory, LoadBalancerClientFactory loadBalancerClientFactory, + List transformers) { + this.delegate = delegate; + this.loadBalancerClient = loadBalancerClient; + this.loadBalancedRetryFactory = loadBalancedRetryFactory; + this.loadBalancerClientFactory = loadBalancerClientFactory; + this.transformers = transformers; } @Override @@ -158,7 +179,7 @@ public class RetryableFeignBlockingLoadBalancerClient implements Client { } String reconstructedUrl = loadBalancerClient.reconstructURI(retrievedServiceInstance, originalUri) .toString(); - feignRequest = buildRequest(request, reconstructedUrl); + feignRequest = buildRequest(request, reconstructedUrl, retrievedServiceInstance); } } org.springframework.cloud.client.loadbalancer.Response lbResponse = new DefaultResponse( @@ -191,6 +212,16 @@ public class RetryableFeignBlockingLoadBalancerClient implements Client { request.charset(), request.requestTemplate()); } + protected Request buildRequest(Request request, String reconstructedUrl, ServiceInstance instance) { + Request newRequest = buildRequest(request, reconstructedUrl); + if (transformers != null) { + for (LoadBalancerFeignRequestTransformer transformer : transformers) { + newRequest = transformer.transformRequest(newRequest, instance); + } + } + return newRequest; + } + private RetryTemplate buildRetryTemplate(String serviceId, Request request, LoadBalancedRetryPolicy retryPolicy) { RetryTemplate retryTemplate = new RetryTemplate(); BackOffPolicy backOffPolicy = this.loadBalancedRetryFactory.createBackOffPolicy(serviceId); diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/loadbalancer/FeignBlockingLoadBalancerClientTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/loadbalancer/FeignBlockingLoadBalancerClientTests.java index c20b2406..5f4f61a6 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/loadbalancer/FeignBlockingLoadBalancerClientTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/loadbalancer/FeignBlockingLoadBalancerClientTests.java @@ -21,9 +21,11 @@ import java.io.IOException; import java.io.InputStreamReader; import java.net.URI; import java.nio.charset.StandardCharsets; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @@ -66,6 +68,7 @@ import static org.mockito.Mockito.when; * {@link FeignBlockingLoadBalancerClient} and its delegates. * * @author Olga Maciaszek-Sharma + * @author changjin wei(魏昌进) * @see BlockingLoadBalancerClientTests */ @@ -80,8 +83,11 @@ class FeignBlockingLoadBalancerClientTests { private final LoadBalancerProperties loadBalancerProperties = new LoadBalancerProperties(); + private final List transformers = Arrays.asList(new InstanceIdTransformer(), + new ServiceIdTransformer()); + private final FeignBlockingLoadBalancerClient feignBlockingLoadBalancerClient = new FeignBlockingLoadBalancerClient( - delegate, loadBalancerClient, loadBalancerClientFactory); + delegate, loadBalancerClient, loadBalancerClientFactory, transformers); @BeforeEach void setUp() { @@ -133,9 +139,11 @@ class FeignBlockingLoadBalancerClientTests { Request actualRequest = captor.getValue(); assertThat(actualRequest.httpMethod()).isEqualTo(Request.HttpMethod.GET); assertThat(actualRequest.url()).isEqualTo(url); - assertThat(actualRequest.headers()).hasSize(1); + assertThat(actualRequest.headers()).hasSize(3); assertThat(actualRequest.headers()).containsEntry(HttpHeaders.CONTENT_TYPE, Collections.singletonList(MediaType.APPLICATION_JSON_VALUE)); + assertThat(actualRequest.headers()).containsEntry("X-ServiceId", Collections.singletonList("test")); + assertThat(actualRequest.headers()).containsEntry("X-InstanceId", Collections.singletonList("test-1")); assertThat(new String(actualRequest.body())).isEqualTo("hello"); } @@ -253,4 +261,28 @@ class FeignBlockingLoadBalancerClientTests { } + private static class InstanceIdTransformer implements LoadBalancerFeignRequestTransformer { + + @Override + public Request transformRequest(Request request, ServiceInstance instance) { + Map> headers = new HashMap<>(request.headers()); + headers.put("X-InstanceId", Collections.singletonList(instance.getInstanceId())); + return Request.create(request.httpMethod(), request.url(), headers, request.body(), request.charset(), + request.requestTemplate()); + } + + } + + private static class ServiceIdTransformer implements LoadBalancerFeignRequestTransformer { + + @Override + public Request transformRequest(Request request, ServiceInstance instance) { + Map> headers = new HashMap<>(request.headers()); + headers.put("X-ServiceId", Collections.singletonList(instance.getServiceId())); + return Request.create(request.httpMethod(), request.url(), headers, request.body(), request.charset(), + request.requestTemplate()); + } + + } + } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/loadbalancer/RetryableFeignBlockingLoadBalancerClientTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/loadbalancer/RetryableFeignBlockingLoadBalancerClientTests.java index efde64f0..d3d27e61 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/loadbalancer/RetryableFeignBlockingLoadBalancerClientTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/loadbalancer/RetryableFeignBlockingLoadBalancerClientTests.java @@ -22,9 +22,11 @@ import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.nio.charset.StandardCharsets; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @@ -70,6 +72,7 @@ import static org.mockito.Mockito.when; * {@link RetryableFeignBlockingLoadBalancerClient} and its delegates. * * @author Olga Maciaszek-Sharma + * @author changjin wei(魏昌进) * @see BlockingLoadBalancerClientTests */ @@ -86,8 +89,11 @@ class RetryableFeignBlockingLoadBalancerClientTests { private final LoadBalancerProperties properties = new LoadBalancerProperties(); + private final List transformers = Arrays.asList(new InstanceIdTransformer(), + new ServiceIdTransformer()); + private final RetryableFeignBlockingLoadBalancerClient feignBlockingLoadBalancerClient = new RetryableFeignBlockingLoadBalancerClient( - delegate, loadBalancerClient, retryFactory, properties, loadBalancerClientFactory); + delegate, loadBalancerClient, retryFactory, loadBalancerClientFactory, transformers); private final ServiceInstance serviceInstance = new DefaultServiceInstance("test-a", "test", "testhost", 80, false); @@ -197,9 +203,11 @@ class RetryableFeignBlockingLoadBalancerClientTests { Request actualRequest = captor.getValue(); assertThat(actualRequest.httpMethod()).isEqualTo(Request.HttpMethod.GET); assertThat(actualRequest.url()).isEqualTo(url); - assertThat(actualRequest.headers()).hasSize(1); + assertThat(actualRequest.headers()).hasSize(3); assertThat(actualRequest.headers()).containsEntry(HttpHeaders.CONTENT_TYPE, Collections.singletonList(MediaType.APPLICATION_JSON_VALUE)); + assertThat(actualRequest.headers()).containsEntry("X-ServiceId", Collections.singletonList("test")); + assertThat(actualRequest.headers()).containsEntry("X-InstanceId", Collections.singletonList("test-1")); assertThat(new String(actualRequest.body())).isEqualTo("hello"); } @@ -310,4 +318,28 @@ class RetryableFeignBlockingLoadBalancerClientTests { } + private static class InstanceIdTransformer implements LoadBalancerFeignRequestTransformer { + + @Override + public Request transformRequest(Request request, ServiceInstance instance) { + Map> headers = new HashMap<>(request.headers()); + headers.put("X-InstanceId", Collections.singletonList(instance.getInstanceId())); + return Request.create(request.httpMethod(), request.url(), headers, request.body(), request.charset(), + request.requestTemplate()); + } + + } + + private static class ServiceIdTransformer implements LoadBalancerFeignRequestTransformer { + + @Override + public Request transformRequest(Request request, ServiceInstance instance) { + Map> headers = new HashMap<>(request.headers()); + headers.put("X-ServiceId", Collections.singletonList(instance.getServiceId())); + return Request.create(request.httpMethod(), request.url(), headers, request.body(), request.charset(), + request.requestTemplate()); + } + + } + }