Skip to content

Commit

Permalink
@LoadBalanced RestClient (#1294)
Browse files Browse the repository at this point in the history
  • Loading branch information
OlgaMaciaszek authored Nov 13, 2023
1 parent 42ba0ee commit dd47ca6
Show file tree
Hide file tree
Showing 17 changed files with 624 additions and 225 deletions.
275 changes: 178 additions & 97 deletions docs/modules/ROOT/pages/spring-cloud-commons/common-abstractions.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ Please see the documentation of the `ServiceRegistry` implementation you use for
For instance, Eureka's supported statuses are `UP`, `DOWN`, `OUT_OF_SERVICE`, and `UNKNOWN`.

[[rest-template-loadbalancer-client]]
== Spring RestTemplate as a Load Balancer Client
== Spring `RestTemplate` as a LoadBalancer Client

You can configure a `RestTemplate` to use a Load-balancer client.
To create a load-balanced `RestTemplate`, create a `RestTemplate` `@Bean` and use the `@LoadBalanced` qualifier, as the following example shows:
Expand All @@ -157,8 +157,8 @@ public class MyClass {
private RestTemplate restTemplate;
public String doOtherStuff() {
String results = restTemplate.getForObject("http://stores/stores", String.class);
return results;
String result = restTemplate.getForObject("http://stores/stores", String.class);
return result;
}
}
----
Expand All @@ -167,13 +167,140 @@ CAUTION: A `RestTemplate` bean is no longer created through auto-configuration.
Individual applications must create it.

The URI needs to use a virtual host name (that is, a service name, not a host name).
The BlockingLoadBalancerClient is used to create a full physical address.
The `BlockingLoadBalancerClient` is used to create a full physical address.

IMPORTANT: To use a load-balanced `RestTemplate`, you need to have a load-balancer implementation in your classpath.
IMPORTANT: To use a load-balanced `RestTemplate`, you need to have a Spring Cloud LoadBalancer implementation in your classpath.
Add xref:spring-cloud-commons/loadbalancer.adoc#spring-cloud-loadbalancer-starter[Spring Cloud LoadBalancer starter] to your project in order to use it.

[[multiple-resttemplate-objects]]
=== Multiple `RestTemplate` Objects

If you want a `RestTemplate` that is not load-balanced, create a `RestTemplate` bean and inject it.
To access the load-balanced `RestTemplate`, use the `@LoadBalanced` qualifier when you create your `@Bean`, as the following example shows:

[source,java,indent=0]
----
@Configuration
public class MyConfiguration {
@LoadBalanced
@Bean
RestTemplate loadBalanced() {
return new RestTemplate();
}
@Primary
@Bean
RestTemplate restTemplate() {
return new RestTemplate();
}
}
public class MyClass {
@Autowired
private RestTemplate restTemplate;
@Autowired
@LoadBalanced
private RestTemplate loadBalanced;
public String doOtherStuff() {
return loadBalanced.getForObject("http://stores/stores", String.class);
}
public String doStuff() {
return restTemplate.getForObject("http://example.com", String.class);
}
}
----

IMPORTANT: Notice the use of the `@Primary` annotation on the plain `RestTemplate` declaration in the preceding example to disambiguate the unqualified `@Autowired` injection.

TIP: If you see errors such as `java.lang.IllegalArgumentException: Can not set org.springframework.web.client.RestTemplate field com.my.app.Foo.restTemplate to com.sun.proxy.$Proxy89`, try injecting `RestOperations` or setting `spring.aop.proxyTargetClass=true`.

[[rest-client-loadbalancer-client]]
== Spring `RestClient` as a LoadBalancer Client

You can configure a `RestClient` to use a Load-balancer client.
To create a load-balanced `RestClient`, create a `RestClient.Builder` `@Bean` and use the `@LoadBalanced` qualifier, as the following example shows:

[source,java,indent=0]
----
@Configuration
public class MyConfiguration {
@LoadBalanced
@Bean
RestClient.Builder restClientBuilder() {
return RestClient.builder();
}
}
public class MyClass {
@Autowired
private RestClient.Builder restClientBuilder;
public String doOtherStuff() {
return restClientBuilder.build().get().uri(URI.create("http://stores/stores")).retrieve().body(String.class);
}
}
----

The URI needs to use a virtual host name (that is, a service name, not a host name).
The `BlockingLoadBalancerClient` is used to create a full physical address.

IMPORTANT: To use a load-balanced `RestClient`, you need to have a Spring LoadBalancer implementation in your classpath.
Add xref:spring-cloud-commons/loadbalancer.adoc#spring-cloud-loadbalancer-starter[Spring Cloud LoadBalancer starter] to your project in order to use it.

[[multiple-restclient-objects]]
=== Multiple `RestClient.Builder` Objects

If you want a `RestClient.Builder` that is not load-balanced, create a `RestClient.Builder` bean and inject it.
To access the load-balanced `RestClient`, use the `@LoadBalanced` qualifier when you create your `@Bean`, as the following example shows:

[source,java,indent=0]
----
@Configuration
public class MyConfiguration {
@LoadBalanced
@Bean
RestClient.Builder loadBalanced() {
return RestClient.builder();
}
@Primary
@Bean
RestClient.Builder restClientBuilder() {
return WebClient.builder();
}
}
public class MyClass {
@Autowired
private RestClient.Builder restClientBuilder;
@Autowired
@LoadBalanced
private RestClient.Builder loadBalanced;
public String doOtherStuff() {
return restClientBuilder.build().get().uri("http://stores/stores")
.retrieve().body(String.class);
}
public String doStuff() {
return restClientBuilder.build().get().uri("http://example.com")
.retrieve().body(String.class);
}
}
----

IMPORTANT: Notice the use of the `@Primary` annotation on the plain `RestTemplate` declaration in the preceding example to disambiguate the unqualified `@Autowired` injection.

[[webclinet-loadbalancer-client]]
== Spring WebClient as a Load Balancer Client
== Spring `WebClient` as a LoadBalancer Client

You can configure `WebClient` to automatically use a load-balancer client.
To create a load-balanced `WebClient`, create a `WebClient.Builder` `@Bean` and use the `@LoadBalanced` qualifier, as follows:
Expand Down Expand Up @@ -204,11 +331,55 @@ public class MyClass {
The URI needs to use a virtual host name (that is, a service name, not a host name).
The Spring Cloud LoadBalancer is used to create a full physical address.

IMPORTANT: If you want to use a `@LoadBalanced WebClient.Builder`, you need to have a load balancer
IMPORTANT: If you want to use a `@LoadBalanced WebClient.Builder`, you need to have a Spring Cloud LoadBalancer
implementation in the classpath. We recommend that you add the
xref:spring-cloud-commons/loadbalancer.adoc#spring-cloud-loadbalancer-starter[Spring Cloud LoadBalancer starter] to your project.
Then, `ReactiveLoadBalancer` is used underneath.

[[multiple-webclient-objects]]
=== Multiple `WebClient.Builder` Objects

If you want a `WebClient.Buider` that is not load-balanced, create a `WebClient` bean and inject it.
To access the load-balanced `WebClient.Builder`, use the `@LoadBalanced` qualifier when you create your `@Bean`, as the following example shows:

[source,java,indent=0]
----
@Configuration
public class MyConfiguration {
@LoadBalanced
@Bean
WebClient.Builder loadBalanced() {
return WebClient.builder();
}
@Primary
@Bean
WebClient.Builder webClientBuilder() {
return WebClient.builder();
}
}
public class MyClass {
@Autowired
private WebClient.Builder webClientBuilder;
@Autowired
@LoadBalanced
private WebClient.Builder loadBalanced;
public Mono<String> doOtherStuff() {
return loadBalanced.build().get().uri("http://stores/stores")
.retrieve().bodyToMono(String.class);
}
public Mono<String> doStuff() {
return webClientBuilder.build().get().uri("http://example.com")
.retrieve().bodyToMono(String.class);
}
}
----

[[retrying-failed-requests]]
=== Retrying Failed Requests

Expand Down Expand Up @@ -295,96 +466,6 @@ public class MyConfiguration {
}
----

[[multiple-resttemplate-objects]]
== Multiple `RestTemplate` Objects

If you want a `RestTemplate` that is not load-balanced, create a `RestTemplate` bean and inject it.
To access the load-balanced `RestTemplate`, use the `@LoadBalanced` qualifier when you create your `@Bean`, as the following example shows:

[source,java,indent=0]
----
@Configuration
public class MyConfiguration {
@LoadBalanced
@Bean
RestTemplate loadBalanced() {
return new RestTemplate();
}
@Primary
@Bean
RestTemplate restTemplate() {
return new RestTemplate();
}
}
public class MyClass {
@Autowired
private RestTemplate restTemplate;
@Autowired
@LoadBalanced
private RestTemplate loadBalanced;
public String doOtherStuff() {
return loadBalanced.getForObject("http://stores/stores", String.class);
}
public String doStuff() {
return restTemplate.getForObject("http://example.com", String.class);
}
}
----

IMPORTANT: Notice the use of the `@Primary` annotation on the plain `RestTemplate` declaration in the preceding example to disambiguate the unqualified `@Autowired` injection.

TIP: If you see errors such as `java.lang.IllegalArgumentException: Can not set org.springframework.web.client.RestTemplate field com.my.app.Foo.restTemplate to com.sun.proxy.$Proxy89`, try injecting `RestOperations` or setting `spring.aop.proxyTargetClass=true`.

[[multiple-webclient-objects]]
== Multiple WebClient Objects

If you want a `WebClient` that is not load-balanced, create a `WebClient` bean and inject it.
To access the load-balanced `WebClient`, use the `@LoadBalanced` qualifier when you create your `@Bean`, as the following example shows:

[source,java,indent=0]
----
@Configuration
public class MyConfiguration {
@LoadBalanced
@Bean
WebClient.Builder loadBalanced() {
return WebClient.builder();
}
@Primary
@Bean
WebClient.Builder webClient() {
return WebClient.builder();
}
}
public class MyClass {
@Autowired
private WebClient.Builder webClientBuilder;
@Autowired
@LoadBalanced
private WebClient.Builder loadBalanced;
public Mono<String> doOtherStuff() {
return loadBalanced.build().get().uri("http://stores/stores")
.retrieve().bodyToMono(String.class);
}
public Mono<String> doStuff() {
return webClientBuilder.build().get().uri("http://example.com")
.retrieve().bodyToMono(String.class);
}
}
----

[[loadbalanced-webclient]]
== Spring WebFlux `WebClient` as a Load Balancer Client

Expand Down
13 changes: 7 additions & 6 deletions docs/modules/ROOT/pages/spring-cloud-commons/loadbalancer.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,13 @@ NOTE: The classes you pass as `@LoadBalancerClient` or `@LoadBalancerClients` co
[[spring-cloud-loadbalancer-integrations]]
== Spring Cloud LoadBalancer integrations

In order to make it easy to use Spring Cloud LoadBalancer, we provide `ReactorLoadBalancerExchangeFilterFunction` that can be used with `WebClient` and `BlockingLoadBalancerClient` that works with `RestTemplate`.
In order to make it easy to use Spring Cloud LoadBalancer, we provide `ReactorLoadBalancerExchangeFilterFunction` that can be used with `WebClient` and `BlockingLoadBalancerClient` that works with `RestTemplate` and `RestClient`.
You can see more information and examples of usage in the following sections:

* xref:spring-cloud-commons/common-abstractions.adoc#rest-template-loadbalancer-client[Spring RestTemplate as a Load Balancer Client]
* xref:spring-cloud-commons/common-abstractions.adoc#webclinet-loadbalancer-client[Spring WebClient as a Load Balancer Client]
* xref:spring-cloud-commons/common-abstractions.adoc#webflux-with-reactive-loadbalancer[Spring WebFlux WebClient with `ReactorLoadBalancerExchangeFilterFunction`]
* xref:spring-cloud-commons/common-abstractions.adoc#rest-template-loadbalancer-client[Spring `RestTemplate` as a LoadBalancer Client]
* xref:spring-cloud-commons/common-abstractions.adoc#rest-client-loadbalancer-client[Spring `RestClient` as a LoadBalancer Client]
* xref:spring-cloud-commons/common-abstractions.adoc#webclinet-loadbalancer-client[Spring `WebClient` as a LoadBalancer Client]
* xref:spring-cloud-commons/common-abstractions.adoc#webflux-with-reactive-loadbalancer[Spring `WebFlux WebClient` with `ReactorLoadBalancerExchangeFilterFunction`]

[[loadbalancer-caching]]
== Spring Cloud LoadBalancer Caching
Expand Down Expand Up @@ -232,7 +233,7 @@ public class CustomLoadBalancerConfiguration {
----

TIP: For the non-reactive stack, create this supplier with the `withBlockingHealthChecks()`.
You can also pass your own `WebClient` or `RestTemplate` instance to be used for the checks.
You can also pass your own `WebClient`, `RestTemplate` or `RestClient` instance to be used for the checks.

WARNING: `HealthCheckServiceInstanceListSupplier` has its own caching mechanism based on Reactor Flux `replay()`. Therefore, if it's being used, you may want to skip wrapping that supplier with `CachingServiceInstanceListSupplier`.

Expand Down Expand Up @@ -336,7 +337,7 @@ public class CustomLoadBalancerConfiguration {

You can use the selected `ServiceInstance` to transform the load-balanced HTTP Request.

For `RestTemplate`, you need to implement and define `LoadBalancerRequestTransformer` as follows:
For `RestTemplate` and `RestClient`, you need to implement and define `LoadBalancerRequestTransformer` as follows:

[source,java,indent=0]
----
Expand Down
Loading

0 comments on commit dd47ca6

Please sign in to comment.