Eureka server and UnknownHostException - spring

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

Related

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 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.

Using gRPC communicate Spring Boot application together with consul or Eureka

How can we use grcp communication with spring boot application. And how we can use common service discovery method for use with grpc to get end point of spring boot application.
For service discovery, implement your own NameResolver.
I used below code to get instance discovery client. It help to call all servers using revers poxing.
#Autowired
private DiscoveryClient discoveryClient;
and below method help to call all the services using service name
#RequestMapping(method = RequestMethod.GET, value = "/senduser")
public ResponseEntity<?> sendMessageToAllServices() {
user u=null;
List<ServiceInstance> server=discoveryClient.getInstances("grpc-server");
for (ServiceInstance serviceInstance : server) {
String hostName=serviceInstance.getHost();
int gRpcPort=Integer.parseInt(serviceInstance.getMetadata().get("grpc.port"));
ManagedChannel channel=ManagedChannelBuilder.forAddress(hostName,gRpcPort).usePlaintext(true).build();
UserServiceBlockingStub stub=UserServiceGrpc.newBlockingStub(channel);
UserDetail user=UserDetail.newBuilder()
.setName("Thamira")
.setEmail("Thamira1005#gmail.com")
.setAge(24).setGender(Gender.Male)
.setPassword("password").build();
u=stub.createUser(user);
}
return ResponseEntity.ok("User "+u);
}
we can change register as a consul or eureka. This method support both of that.

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/

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