how to do client side load balancing in spring integration - spring-boot

we can add client-side load balancing in spring boot applications by,
#Bean
#LoadBalanced
public RestTemplate restTemplate() {
final RestTemplate restTemplate = new RestTemplate();
return restTemplate;
}
This will take care of the microservice resolution as well. ie. identifying service by the URL like "http://service_name/api/v1/endpoint/".
Is there any similar mechanism for name resolution in Spring integration?

See this ctor for Spring Integration HTTP Outbound Channel Adapter:
/**
* Create a handler that will send requests to the provided URI using a provided RestTemplate
* #param uri The URI.
* #param restTemplate The rest template.
*/
public HttpRequestExecutingMessageHandler(String uri, RestTemplate restTemplate) {
So, when you configure a #ServiceActivator (handle() in Java DSL), you just inject your load-balanced RestTemplate and everything should work as expected.

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();
}
}

Spring cloud multiple RestTemplate

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.

How to expose both a SOAP web-service and RESTful API at the same time in Spring Boot?

In Spring Boot 1.4.3 I exposed a SOAP web service endpoint which works successfully on port 8080.
For the purpose of running a health check I also need to expose a RESTful API. I tried both using Actuator and a rest controller:
#RestController
public class RESTapis {
#RequestMapping(method = {RequestMethod.GET, RequestMethod.POST}, value = "/health")
public String healthCheck() {
return "ACK";
}
}
but in both cases I get the same response: HTTP 405 (method not allowed).
The REST api returns HTTP 200 if I disable the web-service.
How can I have both the SOAP web-service and REST working at the same time?
Here is the web-service configuration:
#EnableWs
#Configuration
public class WebServiceConfig extends WsConfigurerAdapter {
#Bean
public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) {
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(applicationContext);
servlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean(servlet, "/*");
}
}
So going off the messageDispatcherServlet method it looks like you are binding your servlet to all the incoming request due to the wildcard registration:
return new ServletRegistrationBean(servlet, "/*");
Hence the MessageDispatcher is intercepting all of your incoming requests and trying to find the /health and throwing http 405.
Fix:
return new ServletRegistrationBean(servlet, "/soap-api/*");
Explanation:
By binding the Message Dispatcher to a specific URI namespace we can ensure that all the request fired on the /soap-api/* namespace ONLY will be intercepted by the MessageDispatcher. And all the other requests will be intercepted by the DispatcherServlet making it possible to run a Rest interface in parallel to your Soap WS.
Suggestion:
Not knowing the reasons / specifics of the app, but going off the name of the method healthcheck(), you can look at using spring boot actuator to generate health checks, metrics for you app. You can also override it for customisations.
Reference for actuator: https://spring.io/guides/gs/actuator-service/

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