Spring Cloud Eureka + FeignClient + Ribbon : Load balancer does not have available server for client - spring-cloud

I have Below Simple Setup.
Spring Cloud Eureka Server- and Two Services PricingService and DiscountService. Pricing Service calls-> DiscountService.
Server Setup
#SpringBootApplication
#EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
properties
spring.application.name=eureka-server
server.port=8761
eureka.client.registerWithEureka=false
eureka.client.fetchRegistry=false
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
DiscountService
#SpringBootApplication
#EnableDiscoveryClient
#Slf4j
public class DiscountServiceApplication {
public static void main(String[] args) {
SpringApplication.run(DiscountServiceApplication.class, args);
}
}
#RestController
#RequestMapping("/discount/{product}")
#Slf4j
public class DiscountController{
#GetMapping
public int getDiscountPercentage(#PathVariable("product") String product){
log.info("Getting Discount for Product {}",product);
return 50;
}
}
spring.application.name=discount-service
eureka.client.serviceUrl.defaultZone= http://localhost:8761/eureka/
server.port=8081
Pricing Service
#SpringBootApplication
#EnableDiscoveryClient
#EnableFeignClients
public class PricingServiceApplication {
public static void main(String[] args) {
SpringApplication.run(PricingServiceApplication.class, args);
}
}
#RestController
#Slf4j
class ServiceInstanceRestController {
#Autowired
private DiscountServiceClient discountServiceClient;
#GetMapping("/test/")
public String getPriceForProduct() {
log.info("getPriceForProduct");
int dscount = discountServiceClient.getDiscountPercentage("Test");
log.info("Discount is {}",dscount);
return "Price";
}
}
#FeignClient("discount-service")
interface DiscountServiceClient {
#RequestMapping(method = RequestMethod.GET, value = "/discount/{product}")
int getDiscountPercentage(#PathVariable("product") String product);
}
spring.application.name=pricing-service
eureka.client.serviceUrl.defaultZone= http://localhost:8761/eureka/
server.port=8080
eureka.client.fetchRegistry=true
While Calling Discount Service i am getting exception
com.netflix.client.ClientException: Load balancer does not have available server for client: discount-service
at com.netflix.loadbalancer.LoadBalancerContext.getServerFromLoadBalancer(LoadBalancerContext.java:483) ~[ribbon-loadbalancer-2.3.0.jar:2.3.0]
I am using Spring Boot 2.1.4.RELEASE
dependency used
spring-cloud-starter-netflix-eureka-client
spring-cloud-starter-openfeign
spring-cloud-starter-netflix-eureka-server-server (Fo Server)
I have already checked the answers for Load balancer does not have available server for client
But did not work for me...
What I am missing here ?

It also requires actuator dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
For more details, refer the following links
https://github.com/M-Thirumal/eureka-server
https://github.com/M-Thirumal/eureka-client-1
https://github.com/M-Thirumal/eureka-client-2

Related

Service registers itself with weird hostname at eureka

I'm just exploring the Spring Cloud stack. I've set up an Eureka instance and then set up a service that registers itself with Eureka.
Eureka:
appliation.properties
spring.application.name=registry
server.port=8761
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
Main class:
#SpringBootApplication
#EnableEurekaServer
public class RegistryApplication {
public static void main(String[] args) {
SpringApplication.run(RegistryApplication.class, args);
}
}
Service:
application.properties:
spring.application.name=example-service
server.port=1111
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
Main class:
#SpringBootApplication
#EnableDiscoveryClient
#RestController
public class AppServiceApplication {
public static void main(String[] args) {
SpringApplication.run(AppServiceApplication.class, args);
}
#RequestMapping("/")
public String sayHi() {
return "Hi";
}
}
Now both eureka and the service are starting up and the service registers itself with eureka as expected. However it registers itself with a rather weird hostname and I am not able to figure out what exactly is going on or why this is happening. This is a screenshot of the eureka dashboard showing the weird hostname:
I tried the same setup on my laptop and there the hostname is just 'localhost' as expected.
Does someone have an explanation for this ? Thank you in advance for your answers!
Edit: I'm using Spring Boot version 2.0.5.RELEASE

Feign client manually. Load balancer does not have available server for client

I have two services registered with eureka. Service C calls service A. Service C is feign client. I want implement feign client manually. But I catch an exception:
com.netflix.client.ClientException: Load balancer does not have
available server for client: service-test-a
Application class:
#EnableEurekaClient
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Feign interface:
#Component
public interface FeignService {
#RequestLine("GET /")
public String getServiceA();
}
Feign config:
#Configuration
#Import(FeignClientsConfiguration.class)
public class MyConfig {
}
Controller:
#RestController
public class Controller {
private FeignService feignService;
#Autowired
public void Controller() {
feignService = Feign.builder()
.client(RibbonClient.create())
.target(FeignService.class, "http://service-test-a");
}
#RequestMapping(value = "/build", method = RequestMethod.GET)
public String getServiceC() {
return feignService.getServiceA();
}
}
What am I doing wrong?
AFAIK, there is no easy way of using OpenFeign with eureka. There is no guide or example for that. Also I guess that it may require some additional implementations and configuration.
Instead, please try to use Spring Cloud Feign. It provides full integration with eureka and ribbon without any additional implementation. You can use Spring Cloud Feign with just a few changes in your above code.
Please refer to Spring Cloud Feign

Automaticle replicate microservices with Spring Cloud Netflix

I have multiple Spring Cloud microservices. I want the automatic replication of microservices to be done when microservice cannot manage to do its work.
I tried to find any solutions but I found only this: here. This solution cannot be applied to my problem as it describes only the case when we have a certain number of microservices.
I will be very grateful if you give me some examples to help me with this problem.
UPDATE: I've several microservices
Eureka:
#SpringBootApplication
#EnableEurekaServer
public class EurekaApplication {
public static void main(String[] args){
SpringApplication.run(EurekaApplication.class, args);
}
}
GatewayApplication:
#EnableZuulProxy
#EnableEurekaClient
#SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
FirstSybsystemApplication:
#SpringBootApplication
#EnableEurekaClient
#EnableFeignClients
public class FirstSubsystemApplication implements CommandLineRunner{
public static void main(String[] args) {
SpringApplication.run(FirstSubsystemApplication.class);
}
#Override
public void run(String... args) throws Exception {
}
}
I want to make a high load on FirstSubsystemApplication and to launch its copy automatically.

Ribbon with Spring Cloud and Eureka java.lang.IllegalStateException: No instances available for localhost

I am using
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix</artifactId>
<version>1.2.3.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
My main class:
#SpringBootApplication
//#Configuration
#ComponentScan(basePackages = "com.mypackage")
#EnableAutoConfiguration
#EnableEurekaClient
#EnableSwagger2
public class App
{
public static void main( String[] args )
{
SpringApplication.run(App.class, args);
}
#LoadBalanced
#Bean(name="template")
RestTemplate restTemplate() {
return new RestTemplate();
}
}
My service calling:
#Autowired
private RestTemplate template;
ResponseEntity<String> avs = template.exchange("http://localhost:7075/xyz/json/authenticate",HttpMethod.POST ,request,String.class);
It is throwing the following exception
java.lang.IllegalStateException: No instances available for localhost
at org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient.execute(RibbonLoadBalancerClient.java:90)
at org.springframework.cloud.client.loadbalancer.RetryLoadBalancerInterceptor$1.doWithRetry(RetryLoadBalancerInterceptor.java:60)
at org.springframework.cloud.client.loadbalancer.RetryLoadBalancerInterceptor$1.doWithRetry(RetryLoadBalancerInterceptor.java:48)
at org.springframework.retry.support.RetryTemplate.doExecute(RetryTemplate.java:276)
at org.springframework.retry.support.RetryTemplate.execute(RetryTemplate.java:157)
When you use a #LoadBalanced RestTemplate the hostname needs to be a serviceId not an actual hostname. In your case, it's trying to find a eureka record for localhost and can't find one. See the documentation for how to use multiple RestTemplate objects, one load balanced, one not.
#Configuration
public class MyConfiguration {
#LoadBalanced
#Bean
RestTemplate loadBalanced() {
return new RestTemplate();
}
#Primary
#Bean
RestTemplate restTemplate() {
return new RestTemplate();
}
}
public class MyClass {
#Autowired
private RestTemplate restTemplate;
#Autowired
#LoadBalanced
private RestTemplate loadBalanced;
public String doOtherStuff() {
return loadBalanced.getForObject("http://stores/stores", String.class);
}
public String doStuff() {
return restTemplate.getForObject("http://example.com", String.class);
}
}
From what I read there is some kind of problem when you try to Autowire RestTemplate while using this Netflix cloud. However I found a workaround. First declare a new #Component class and in it create a method that returns RestTemplate:
#Component
public class RestTemplateComponentFix{
#Autowired
SomeConfigurationYouNeed someConfiguration;
#LoadBalanced
public RestTemplate getRestTemplate() {
// TODO set up your restTemplate
rt.setRequestFactory( new HttpComponentsClientHttpRequestFactory() );
return rt;
}
}
After that just Autowire the restTemplateComponentFix in your class and when when you need the rest template call the restTemplate() method. Something like this:
#Service
public class someClass{
#Autowired
RestTemplateComponentFix restTemplateComponentFix;
public void methodUsingRestTemplate(){
// Some code...
RestTemplate rt = restTemplateComponentFix.getRestTemplate();
// Some code...
}
}
The cool part is that you can easily unit test this code with something like:
RestTemplate rt = Mockito.mock(RestTemplate.class)
when(restTemplateComponentFix.getRestTemplate()).thenReturn(rt);
when(rt.someMethod()).thenReturn(something);

How to used "web request by spring mvc" and "rest by jersey" in spring boot

Hi i want to handle web request by spring mvc and handle rest by jersey
in the same project (Spring-Boot)
As i test Rest service is working but web is not
How can i set Application Config ?
#SpringBootApplication
#EnableAutoConfiguration
#ComponentScan(basePackageClasses = {ProductsResource.class, MessageService.class,Web.class})
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
#Bean
public ServletRegistrationBean jerseyServlet() {
ServletRegistrationBean registration = new ServletRegistrationBean(new ServletContainer(), "/rest/*");
registration.addInitParameter(ServletProperties.JAXRS_APPLICATION_CLASS, JerseyInitialization.class.getName());
return registration;
}
#Bean
public ServletRegistrationBean webMVC() {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
applicationContext.register(ResourceConfig.class);
dispatcherServlet.setApplicationContext(applicationContext);
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(dispatcherServlet, "*.html");
servletRegistrationBean.setName("web-mvc");
return servletRegistrationBean;
}
Web Controller
#Controller
#Component
public class Web {
#RequestMapping("/foo")
String foo() {
return "foo";
}
#RequestMapping("/bar")
String bar() {
return "bar";
}
}
Rest Controller
#Path("/")
#Component
public class ProductsResource {
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path("/hello")
public String hello() {
return "Hello World";
}
}
Actually with spring boot (I'm talking about version 1.4.x), it's pretty easy to do Spring MVC and JAX-RS(rest by jersey) at the same time :). You don't need any servlet registrations. All you need to do is to add a Configuration class like the following
#Configuration
#ApplicationPath("/rest")
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
packages("your.package.with.rest.resources");
}
}
Now all your JAXRS resources are served under /rest/*
For example, your Rest Controller can be refactored as
#Path("/hello")
#Component
public class ProductsResource {
#GET
#Produces(MediaType.APPLICATION_JSON)
public String hello() {
return "Hello World";
}
}
Now if you hit url http://server:port/rest/hello, Hello World should be returned.
Finally, remember to add the following dependencies in your maven pom file
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
That should work for you.