Why #LoadBalanced annotation is necessary while using RestTemplate and eureka server for service discovery - spring-boot

I have two microservices deployed and a eureka server. The microservices are eureka clients, viz, eureka-client-1 and eureka-client-2 respectively.
Using, service discovery, I want to call an API from eureka-client-1 of eureka-client-2.
Following is the supported code and configurations:
discovery-server (eureka server) configuration:
server:
port: 8761
eureka:
client:
fetch-registry: false
register-with-eureka: false
spring:
application:
name: discovery-server
eureka-client-1 and eureka-client-2 have same configurations:
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
In eureka-client-1 I am using RestTemplate to communicate with the eureka-client-2
Notice that I have currently commented the LoadBalanced annotation.
#Bean
// #LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
I access the api of eureka-client-2 as follows:
restTemplate.getForObject(
"http://EUREKA-CLIENT-2/api/v1/check/{customerId}", Response.class, customer.getId());
When I deploy and test the application, I am getting UnknownHostException for EUREKA-CLIENT-2. But, as soon as I enable the annotation #LoadBalanced everything works fine and eureka-client-1 can successfully communicate with eureka-client-2.
My question is: Why #LoadBalanced annotation is absolutely compulsory? If I have deployed only single instances of each microservices, why can't the discovery-server return the information of the only service with given name that is deployed? And even if same service is deployed multiple times, why it can't internally apply the loadbalancing algorithm? Why the client has to specifically add the annotation

Related

spring-cloud-loabalancer configuring static server list

We're moving away from the spring-cloud Netflix OSS ecosystem one step at a time. Currently we're implementing spring-cloud-loadbalancer and removing Ribbon.
However we used to have a lot of static services in our integration tests, now with the move from ribbon towards spring-cloud-loadbalancer those properties are not being picked up any longer.
i.e.:
foo-service.ribbon.NIWSServerListClassName=com.netflix.loadbalancer.ConfigurationBasedServerList
foo-service.ribbon.listOfServers=localhost:9876
We've migrated towards using spring-cloud-loadbalancer in the following way
First we annotated our Webclient.Builder with #LoadBalanced like this
#Bean
#LoadBalanced
fun webClientBuilder() = WebClient.builder()
And then we've added the #LoadBalancerClient annotation on the client classes like this
#LoadBalancerClient(name = "foo-service", configuration = [FooServiceConfiguration::class])
class FooServiceClient(private val basicAuthWebClient: WebClient)
This results in our tests failing with an UnknownHostException for foo-service.
Now My question is how do we configure this static server list in the new spring-cloud-loadbalancer?
Based on #spencergibb's comment, I guess something like this should work:
spring:
cloud:
discovery:
client:
simple:
instances:
foo-service:
- instanceId: foo1
serviceId: foo-service
host: localhost
port: 9876```

Spring cloud gateway default routing doesn't work

I want enable default routing in my spring cloud gateway (no zuul) by service ids registered in eureka (application names) but I always got 404 error.
In my chat service's bootstrap.yml
I have defined application name
spring:
application:
name: chat-service
and in application properties:
eureka:
instance:
preferIpAddress: true
client:
healthcheck:
enabled: true
serviceUrl:
defaultZone: http://${EUREKA_HOST:localhost}:${EUREKA_PORT:8761}/eureka/
when I go to eureka's dashboard I can see registered my chat service and gateway as well.
Eureka's configuration in gateway application is same as chat service, but I also have this:
spring:
application:
name: gateway
cloud:
gateway:
discovery:
locator:
enabled: true
and next I also tried add explicit routes which din't work as well, but if I have discovery locator enabled set to true this shouldn't be needed right?
routes:
- id: chat-service-route
uri: lb://chat-service
predicates:
- Path=**
I created test endpoint which I tried call directly on chat service and also with gateway. Direct call works fine so issue will be with routing.
#RestController
#RequestMapping
public class TestController {
#GetMapping
public String test() {
return "chat-service ready";
}
}
What I did wrong? I am little desperate. I am using spring boot 2.2.2 and Hoxton.RELEASE cloud dependencies version
Try removing explicit routes and add below property to application yml. This works for me.
spring:
cloud:
gateway:
discovery:
locator:
lower-case-service-id: true

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 register spring boot microservices on spring cloud Netflix eureka?

We were planning to use spring cloud Netflix oss components. So I was doing a small sample project.
I developed 2 spring microservices and those services runs well on
http://localhost:9000/microsvc-one http://localhost:9001/microsvc-two
And also wrote a sample spring cloud etflix eureka maven project which runs well on
http://localhost:8761
I used annotations #EurekaDiscoveryClient and #SpringBootApplication on both the spring boot microservices main class
I used annotation #EnableEurekaServer and #SpringBootApplication
Now I am facing a problem in registering those services in eureka server. I referred some samples. I am not understanding those.
I did some changes in application.yml files of microsvc-one and microsvc-two and also application.yml file of eureka server.
But still it shows empty.
What all changes are required or missing or correct configuration to be done so that my services are being registered on eureka.
I also have other question like do i need to create a separate project which has #EnableConfigServer and #SpringBootApplication Annotations other than the above 2 microservices and eureka server project module to register the services on eureka.
I see those in most of the examples.
If yes..how do we link between all these?
If you are using springBoot application you will need the annotaion #SpringBootApplication thats why that annotation is there on the project you are seeing. #EnableConfigServer is when you are using the spring-cloud config server it is used to externalize the configuration properties but since you have the application.yml inside the project so you donot need that either.
I am thinking you have a spring boot application for both Microservices and the Eureka server. You need to annotate the eureka main class with
#SpringBootApplication
#EnableEurekaServer
#EnableDiscoveryClient
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
Additionally you need annotate you microservice's main class with..
#SpringBootApplication
#EnableDiscoveryClient
public class MicroApplication {
public static void main(String[] args) {
SpringApplication.run(MicroApplication.class, args);
}
}
Since you donot have you application.yml file in the question here is what you need.
You need the below configuration in application.yml of the microservices.
eureka:
client:
serviceUrl:
defaultZone: ${eurekaurl:http://localhost:8761/eureka/}
In the Eureka Server application.yml file I have this in mine. you might need to tweak it based on what you want.
info:
component: Registry Server
server:
port: ${port:8761}
eureka:
client:
registerWithEureka: false
fetchRegistry: false
server:
enable-self-preservation: false
waitTimeInMsWhenSyncEmpty: 0
instance:
hostname: localhost
lease-expiration-duration-in-seconds: 15
lease-renewal-interval-in-seconds: 5
Suppose you have a microservice named "LoginServer" now, let's see how to register this service with discovery server (Eureka Server) at startup.
Here Spring Boot startup class of LoginServer.java:
#EnableAutoConfiguration
#EnableDiscoveryClient
public class LoginServer {
public static void main(String[] args) {
// Will configure using login-server.yml
System.setProperty("spring.config.name", "login-server");
SpringApplication.run(LoginServer.class, args);
}
}
The #EnableDiscoveryClient - enables service registration and discovery. In this case, this process registers itself with the discovery-server service using its application name, that is configured in YML configuration file.
let's see the complete setup:
First create a login-server.yml (any name but extension should be .yml) file into src/main/resources package folder. And write those configurations and save.
# Spring properties
spring:
application:
name: login-server # This name used as ID so ("spring.config.name",
#"login-server"); must be same.
# Discovery Server Access
eureka:
client:
serviceUrl:
defaultZone: http://localhost:1111/eureka/
# HTTP Server
server:
port: 2222 # HTTP (Tomcat) port
Run the LoginServer and let it finish initializing. Open the dashboard by putting this URL http://localhost:1111 in your favorite browser and refresh. After few seconds later you should see the LOGIN-SERVER. Generally registration takes up to 30 seconds (by default) so wait or restart.
And this is the microservice complete registration process.

Eureka on spring-cloud-netflix with DNS based config, all instances showing up as unavailable

I'm trying to setup a eureka cluster on aws with DNS-based EIP configuration as described at https://github.com/Netflix/eureka/wiki/Configuring-Eureka-in-AWS-Cloud
Everything seems to work, but the eureka dashboard insists that the eureka instances are unavailable. I'm now wondering if this is only an ui problem (i think so) or if i'm missing something.
As i understand the "unavailable-replicas" logic in the dashboard this is because eureka is comparing the registration hostname and the replica hostname. The instances register with their internal VPC ip at the discovery client but with their EIP when looking for replica peers (strange enough, in the eureka log i can see internaly they are also using the internal VPC ip).
The question is: Is that only some cosmetic ui problem that i shouldn't worry about or are bigger problems waiting to step in because of some misconfiguration? If it's only an ui thing: can i "repair" that somehow?
Edit:
Maybe related https://github.com/spring-cloud/spring-cloud-netflix/issues/102#issuecomment-74446709
With the help of #rozhok in the related github issue i now have a working solution. If anyone is facing the same problem, here's what i've done:
application.yml
eureka:
datacenter: cloud
client:
eurekaServerDNSName: your.dns.name
eurekaServerPort: 8761
eurekaServerURLContext: eureka
region: eu-west-1
registerWithEureka: true
fetchRegistry: true
useDnsForFetchingServiceUrls: true
server:
waitTimeInMsWhenSyncEmpty: 0
enableSelfPreservation: true
EurekaServer
#SpringBootApplication
#EnableEurekaServer
#EnableDiscoveryClient
public class EurekaServer {
#Value("${server.port:8761}")
private int port;
public static void main(String[] args) {
SpringApplication.run(EurekaServer.class, args);
}
#Bean
#Autowired
public EurekaInstanceConfigBean eurekaInstanceConfigBean(InetUtils inetUtils) {
EurekaInstanceConfigBean config = new EurekaInstanceConfigBean(inetUtils);
AmazonInfo info = AmazonInfo.Builder.newBuilder().autoBuild("eureka");
// Don't use spring cloud's hostname here.
// See comment below by Michal
config.setHostname(
info.get(AmazonInfo.MetaDataKey.publicHostname));
config.setIpAddress(info.get(AmazonInfo.MetaDataKey.publicIpv4));
config.setNonSecurePort(port);
config.setDataCenterInfo(info);
return config;
}
}
With that configuration each eureka server sees only the other servers as available replicas:

Resources