How to find IPs of Eureka registered service on the clients - spring

I have the following services:
EurekaServer - hosts the eureka discovery server
Client-service - registers to EurekaServer
Finder-service - registers to EurekaServer
Is there a way to get Client-service's ip so I can make requests to it from the Finder-service.
I know there is a way to find InstanceInfo from EurekaServer and I was thinking of making a controller in eureka server where you pass service id and get service's instance ip. This way Finder-service would only need to know service id and eureka ip which it knows because it is registered there.
Is there another solution which is cleaner than this?

To use client discovery you first need to enable by adding either #EnableEurekaClient or #EnableDiscoveryClient to your #SpringBootApplication annotated class (or your specialized #Configuration).
Next to make use of the resolution of the actual service instance to use you need to create a RestTemplate which is load-balanced. This will add an interceptor which translates the service name into the ip-address / DNS name to send the request to (if multiple instances are found it will distribute the load between those instances).
To create a load balanced RestTemplate add the #LoadBalanced annotation to the configured RestTemplate.
#Bean
#LoadBalanced
public RestTemplate restTemplate(RestTemplateBuilder rtb) {
return rtb.build();
}
Now when doing a request through the RestTemplate it will resolve the service name client-service to the actual service instance to use. Without you having to do anything.

Related

Kubernetes and Spring Boot #Service load balancing

I have Kubernetes running on two nodes and one application deployed on the two nodes (two pods, one per node).
It's a Spring Boot application. It uses OpenFeign for service discoverability. In the app i have a RestController defined and it has a few APIs and an #Autowired #Service which is called from inside the APIs.
Whenever i do a request on one of the APIs Kubernetes uses some sort of load-balancing to route the traffic to one of the pods, and the apps RestController is called. This is fine and i want this to be load-balanced.
The problem happens once that API is called and it calls the #Autowired #Service. Somehow this too gets load-balanced and the call to the #Service might end up on the other node.
Heres and example:
we have two nodes: node1, node2
we make a request to node1's IP address.
this might get load-balanced to node2 (this is fine)
node1 gets the request and calls the #Autowired #Service
the call jumps to node2 (this is where the problem happens)
And in code:
Controller:
#Autowired
private lateinit var userService: UserService
#PostMapping("/getUser")
fun uploadNewPC(#RequestParam("userId") userId: String): User {
println(System.getEnv("hostIP")) //123.45.67.01
return userService.getUser(userId)
}
Service:
#Service
class UserService {
fun getUser(userId: String) : User {
println(System.getEnv("hostIP")) //123.45.67.02
...
}
}
I want the load-balancing to happen only on the REST requests not the internal calls of the app to its #Service components. How would i achieve this? Is there any configuration to the way Spring Boot's #service components operate in Kubernetes clusters? Can i change this?
Thanks in advance.
Edit:
After some debugging i found that It wasn't the Service that was load balanced to another node but the initial http request. Even though the request is specifically send to the url of node1... And since i was debugging both nodes at the same time, i didn't notice this.
Well, I haven't used openfeign, but in my understanding it can loadbalance only REST requests indeed.
If I've got your question right, you say that when the REST controller calls the service component (UserService in this case) the network call is issued and this is undesirable.
In this case, I believe, following points for consideration will be beneficial:
Spring boot has nothing to do with load balancing at this level by default, it should be a configured in the spring boot application somehow.
This also has nothing to do with the fact that this application runs in a Kubernetes environment, again its only a spring boot configuration.
Assuming, you have a UserService interface that obviously doesn't have any load balancing logic, spring boot must wrap it into some kind of proxy that adds these capabilities. So try to debug the application startup, place a breakpoint in the controller method and check out what is the actual type of the user service, again it must be some sort of proxy
If the assumption in 3 is correct, there must be some kind of bean post processor (possibly in spring.factories file of some dependency) class that gets registered within the application context. Probably if you'll create some custom method that will print all beans (Bean Post Processor is also a bean), you'll see the suspicious bean.

Getting No instance available for a microservice hosted in PCF

I have hosted 3 microservices in PCF. One is a eureka server and the other 2 are client and service microservices. The client is supposed to call the service through a rest template call and service will return a string.
I have 1 instance each of the eureka server and the client and 2 instances of the service.
I can see both my client and service registered in the Eureka dashboard. But when i try to access the service from the client[Using a rest template call] I get - 'No instances available for the [service name]'
But if i access my service directly from browser then its works fine and returns the string. But the same URL if called from a rest template returns the exception i mentioned above.
Any suggestions will help
Did you add the #LoadBalance on restTemplate bean.
#Bean
#LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
And when calling service you have to use the service name as below.
https://SERVICENAME/restpath

Feign Client Prioritizing URL's in yaml over Eureka

I have a Spring Boot application which serves as a Eureka client. The application has the need to call another micro-service through REST, and I wish to make this call using Feign. The issue I am having is, my application is trying to lookup the service name in Eureka, when it is only defined in my applications yaml file.
I apologize for the hard to follow explanation, hopefully the following code snippets will help clarify.
Feign client:
#FeignClient("foo")
#Component
public interface FooServiceProxy{
#RequestMapping(value = "/balance", method = RequestMethod.POST, produces = "application/json")
ServiceResponse execute(ServiceRequest serviceRequest);
}
In my controller who calls this Feign client, the FooServiceProxy is defined using #AutoWired:
#Autowired
private FooServiceProxy fooServiceProxy;
My yaml file is as follows:
spring:
application:
name: app-name
server:
port: 8080
foo:
ribbon:
listOfServers: http://hostname:8081/balance
eureka:
client:
fetchRegistry: false
serviceUrl:
defaultZone: http://eurekasrver:8761/eureka/
My issue is, during run-time, the following error is thrown:
java.lang.RuntimeException: com.netflix.client.ClientException: Load balancer does not have available server for client: foo
Interestingly, if I remove the #EnableEurekaClient annotation from the application, everything works. I believe I understand the problem which is that instead of looking up the server for foo in my yaml file, because the application is a Eureka client, Feign is going straight to Eureka to lookup a server ip, then failing as none can be found. Despite seeming to understand the problem, I have been unable to find a solution online or to think of one myself.
Any help will be appreciated.
Thank you!
Concerning this question, you should take in account that when eureka is on your classpath, all ribbon configuration are charged by eureka, so it'll use eureka server's list.
Spring Cloud uses #RibbonClient to configure the types used by ribbon, like server list. If you have eureka on the classpath, by default it uses the eureka server list (hence your need for the flag to disable eureka).
Commented by spencergibb https://github.com/spring-cloud/spring-cloud-netflix/issues/564
You can try either by adding the NIWSServerListClassName configuration:
`someservice.ribbon:
NIWSServerListClassName:com.netflix.loadbalancer.ConfigurationBasedServerList
listOfServers: server1:80`
Or try the solution proposed in this issue https://github.com/spring-cloud/spring-cloud-netflix/issues/564

How to call external non REST API from a spring cloud micro-service that use eureka to call other internal micro-service?

In our project we use Spring cloud + Eureka as service registry.
When we use the ribbon client to call internal micro-services, all URL are resolved via Eureka ... that's a problem to call external URLs.
As external API are old fashioned usage of Feign doesn't seem to be good choice.
What's the best way to call an external URL from such a service ?
Thanks in advance
Patrice
One way working:
Use two configurations.
Declare your RestTemplate Bean to call external services like this:
#Primary
#Qualifier("withoutEureka")
#Bean
public RestTemplate restTemplate(){
...
}
Inject this reference in your client this way
#Bean
public MyClientForExtCall myClientForExtCall(#Qualifier("withoutEureka")RestTemplate restTemplate)
In the other configuration use the restTemplate as usual, but don't forget to use another qualifier
#LoadBalanced
#Bean
#Qualifier("withEureka")
public RestTemplate loadBalancedEureka(){
...
}
#Bean
public MyClientForInternal myClientForInternal(#Qualifier("withoutEureka")RestTemplate restTemplate)
Patrice
You can use Ribbon without Eureka. For external APIs where you cannot configure in Eureka to abstract the discover. You can hard code their URLs in client and configure server list. The Ribbon client defaults to a configured server list, and you can supply the configuration like this:
stores:
ribbon:
listOfServers: example.com, google.com

How to manually call a microservice from a JHIPSTER gateway repository or service

I'm new to Jhipster, and wondering if it's possible to manually call a microservices from the gateway code using a RestTemplate or something else.
My first idea was to call the gateway itself... but I'm not sure it's a good idea.
My second idea was to try and call the service by it's URL. My concern is that I don't want to hardcode the port of a given node. Instead, i want to use proper loadbalancing.
I've read this article https://dzone.com/articles/spring-cloud-rest-client-with-netflix-ribbon-basic, but the injection failed.
I've read somewhere else that you now need to manually add the bean declaration
#LoadBalanced
#Bean
RestTemplate restTemplate(){
return new RestTemplate();
}
But now I'm struggling with the actual URI : what I am supposed to put as the root? (xxxxx)
final HcpVersionedhcp hcpVersionedhcp =
restTemplate.exchange("http://xxxxx/api/user-data/byLogin/", UserData.class);
The only configuration I have in my gateway application.yml is
ribbon:
eureka:
enabled: true
The "xxxxx" has to be replaced with your services name. If your service is "foo", you should write http://foo/api/user....
If you are using JWT as authentication, you need to auth using a user a in JHipster, or to pass the JWT token from request when possible. However the is no best practice for JWT auth, so I would suggest to go the JHipster UAA way.
In a few words, you have one more service responsible for authentication and authorization. To access your service from another service, you can use #AuthorizedFeignClient on interfaces, similar to JPA.
So you define:
#AuthorizedFeignClient(name = "xxxx")
interface XxxClient {
#RequestMapping(value = "/api/some-entities/{id}")
SomeEntity getSomeEntityById(Long #Path("id") id);
}
And inject it in any spring service / rest-controller like this:
#Inject
private XxxClient xxxClient;
//...
public void someAction() {
//...
xxxClient.getEntityById(id);
//..
}
Which internally implement client authorization flows ...

Resources