Spring cloud multiple RestTemplate - spring

I am using spring cloud: Spring Boot Application with Eureka + Ribbon default configuration.
I am using 2 RestTemplate configurations, both are #LoadBalanced currently and both of them have the same UriTemplateHandler.
I declared both the #SpringBootApplication and also the #RibbonClient(name="${service.name}") annotations.
My problem is:
When I am trying to access the first configured RestTemplate, the RestTemplate resolvs (by eureka and load balancing by ribbon) to a server , not as I requested as configured in the UriTemplateHandler.
For example: in the UriTemplateHandler I configured "A-Service" and in real time the restTemplate sends the httpRequest to "B-Service"
This behavior happens often, not just for a specific request, but it looks like it only happens when I'm accessing the first configured RestTemplate.
Is it a problem to use 2 RestTemplate with the same uri?
I have no idea why it happens, please advise.

When creating these rest templates as beans, name them uniquely, like e.g.
#LoadBalanced
#Bean("integrationRestTemplate")
public RestTemplate restTemplate() throws Exception {
// build and return your rest template
return ....
}
Then, the other one might be without any specific name e.g.
#Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
Now, if you have these two distinctive rest templates, you can inject the former one e.g. like that:
#Service
public class MyService {
private final RestTemplate restTemplate;
public ApplicantService(#Qualifier("integrationRestTemplate") RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
// service methods
...
}
Basically, the point is you can choose whatever rest template you want, by specifying a #Qualifier.

Related

Eureka server and UnknownHostException

I have installed an Eureka server and have registered a service called pricing-service.
The Eureka dashboard shows
UP pricing-service:4ac78ca47bdbebb5fec98345c6232af0
under status.
Now I have a completely separate Spring boot web service which calls (through a WebClient instance) the pricing-service as http://pricing-service but I get "reactor.core.Exceptions$ReactiveException: java.net.UnknownHostException: No such host is known (pricing-service)"
exception.
So the Controller can't find the pricing-service by hostname.Further, how is the controller aware of the Eureka server in order to get to pricing-service? Shouldn't there be a reference to it in the application.properties of the web service? I couldn't find anything around the web.
WebClient doesn't know anything about Eureka out of the box. You need to use #LoadBalancerClient and #LoadBalanced to wire it up through the load balancer. See the docs here:
https://spring.io/guides/gs/spring-cloud-loadbalancer/
Now I have a completely separate Spring boot web service which calls (through a WebClient instance) the pricing-service as http://pricing-service
This separate service (the WebClient Service) of yours must also register itself with Eureka Server.
By default, webclient is not aware of having to use load-balancer to make calls to other eureka instances.
Here is one of the ways to enable such a WebClient bean:
#Configuration
public class MyBeanConfig {
#Bean
WebClient webClient(LoadBalancerClient lbClient) {
return WebClient.builder()
.filter(new LoadBalancerExchangeFilterFunction(lbClient))
.build();
}
}
Then, you can use this webClient bean to make calls as:
#Component
public class YourClient {
#Autowired
WebClient webClient;
public Mono<ResponseDto> makeCall() {
return webClient
.get()
.uri("http://pricing-service/")
// <-- change your body and subscribe to result
}
Note: Initializing a Bean of WebClient can be explored further here.
I had the issue that WebClient was not working with #LoadBalanced for me when I created a bean that returned WebClient. You have to create a bean for WebClient.Builder instead of just WebClient, otherwise the #LoadBalanced annotation is not working properly.
#Configuration
public class WebClientConfig {
#Bean
#LoadBalanced
public WebClient.Builder loadBalancedWebClientBuilder() {
return WebClient.builder();
}
}

Unable to use #LoadBalanced with OAuth2RestTemplate configured on ClientCredentials

I want to use OAuth2 ClientCredentials flow for inter service communication between two Resource Servers. Everything works fine except that i am not able to use service name (Ribbon Load Balancer feature) instead of hostname in my OAuth2RestTemplate calls to remote resource server.
One of my Resource Server (that calls another Resource Server) has below configuration:
Spring Boot 1.5.13
Spring Cloud Edgware.SR3
build.gradle contains entries for eureka and ribbon
compile('org.springframework.cloud:spring-cloud-starter-ribbon')
compile('org.springframework.cloud:spring-cloud-starter-eureka')
#Configuration
class RestTemplateConfig {
#Bean
#ConfigurationProperties("security.oauth2.client")
public ClientCredentialsResourceDetails oauth2ClientCredentialsResourceDetails() {
return new ClientCredentialsResourceDetails();
}
#LoadBalanced
#Bean(name = "oauthRestTemplate")
public OAuth2RestOperations oAuthRestTemplate(ClientCredentialsResourceDetails oauth2ClientCredentialsResourceDetails) {
return new OAuth2RestTemplate(oauth2ClientCredentialsResourceDetails);
}
}
Service Consuming this OAuth2RestTemplate
#Service
class TestService {
#Autowired
#Qualifier("oauthRestTemplate")
private OAuth2RestOperations oAuth2RestOperations;
public void notifyOrderStatus(long orderId, OrderStatus newStatus) {
oAuth2RestOperations.exchange("http://notification-service/api/order/{id}/status/{status}", HttpMethod.POST, null, Void.class, orderId, newStatus.name());
}
}
Exception appears while invoking remote service using service name i.e. http://notification-service instead of actual hostname and port of remote resource server. If I use actual hostname + port, then everything works fine but I don't want my one resource to know host/post of another resource server.
Exception:
Caused by: java.net.UnknownHostException: notification-service
I have few questions:
If my RestTemplate is annotated with #LoadBalanced, then everything works fine. Does OAuth2RestTemplate support this annotation and can we use service name instead of hostname? If yes, any reference or documentation would be appreciated.
Is it a good idea to use oauth2 client credentials for inter service security between two resource servers? I do not see any samples for the same in documentation?
#LoadBalanced RestTemplate works when we use RestTemplateCustomizer to customize the newly created OAuth2RestTemplate, as shown in the below code:
#Bean(name = "MyOAuthRestTemplate")
#LoadBalanced
public OAuth2RestOperations restTemplate(RestTemplateCustomizer customizer, ClientCredentialsResourceDetails oauth2ClientCredentialsResourceDetails) {
OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(oauth2ClientCredentialsResourceDetails);
customizer.customize(restTemplate);
return restTemplate;
}
Using service name instead of actual host name works fine using this RestTemplate.

spring boot 1.3.3 create multiple resttemplate per env

I am on spring boot 1.3.3 version, I have a requirement where my spring boot application need to call endpoint(s) based on env passed,
which means if env passed as Dev i would need to call devendpoint,
if env passed as Dev1 then need to call dev1endpoint and so on.
So how can I do this ?
Do I need to create multiple restTemplate instances ?
Should I construct the resttemplate dynamically based on env passed ?
As part of constructing resttemplate i would also need to add appllicable interceptor based on env selected.
Plesae suggest.
You can have two beans of the same class. One can be labeled as the primary, and the use on the #Autowired can specify which one to use with the #Qualifier.
Example:
#Configuration
public class MyConfig {
#Bean
#Primary
public RestTemplate typicalConfig() {
// various configs on your rest template
return new RestTemplate();
}
#Bean
public RestTemplate lessTypical() {
// various alternate configurations
return new RestTemplate();
}
}
Now in your service class:
#Service
public class MyService {
#Autowired
RestTemplate typicalRestTemplate;
#Autowired
#Qualifier("lessTypical")
private RestTemplate alternateRestTemplate;
...
}
correct me if I didn't understand your question. I understand that you are going to have different environments but you are going to change this endpoints in runtime depends on some information or whatever, but I don't understand the part when you said you have to create multiple instances of restTemplate for that environments, from my experience on spring boot applications you don't have to do things like that, You just have to create your restTemplate configuration bean.
#Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
And then injected that object on your services class and do whatever you want with them. I recommend you to read the follow article about restTempalte may this could help you http://www.baeldung.com/rest-template

Whether it is possible to write AOP for Spring RestTemplate class and any external jar classes using spring AOP or Aspectj

Is it possible to write AOP for Spring RestTemplate class using spring AOP or Aspectj. EX:
#Around("execution(* org.springframework.web.client.RestTemplate.getFor*(..))")  
Thanks
I had the same issue and couldn't make it work with AOP.
However, in this case, there is a workaround. Since RestTemplate extends InterceptingHttpAccessor, you can intercept all requests coming through theRestTemplate object.
Sample configuration that logs all HTTP requests :
#Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
// (...)
// setup code for the RestTemplate object
restTemplate.getInterceptors().add((request, body, execution) -> {
logger.info("HTTP {} request to {}", request.getMethod(), request.getURI());
return execution.execute(request, body);
});
return restTemplate;
}
While this is not equivalent to using an aspect, you can get similar functionality with interceptors and pretty minimal configuration.

Spring RestTemplate as a Spring Service

I have developed a REST server with our app specific APIs. we also have deployed a different rest Job server into another location. Currently the way I am doing is .
#RestController
public class SparkJobController {
#Autowired
private IJobSchedulerService jobService;
...
And the Service Implementation is
#Service(value="jobService")
public class JobSchedulerServiceImpl implements IJobSchedulerService {
#Override
public Map triggerJob(String context) {
Map<String, ?> s = new HashMap<String,Object>();
RestTemplate restTemplate = new RestTemplate();
// restTemplate call to other REST API. and returns Map.
...
}
My question is , Is my approach correct ? Or Does Spring framework enables us to use some predefined APIs which can help to use RESTTemplate as a Service
[EDIT] : the deployed REST service is third party application.
I did some research and havent' seen yet a way to implement RestTemplate as a service.
I have seen RestTemplate defined in the bean config and auto wired in - https://www.informit.com/guides/content.aspx?g=java&seqNum=546
To summarize, most of the examples I have seen use Resttemplate, similar to how you have implemented in your code.

Resources