Load balancer does not have available server for client: meeting

bibliophilsagar picture bibliophilsagar · Sep 4, 2018 · Viewed 15.6k times · Source

While I am trying to reach the service meeting via Zuul gateway, Zuul is unable to forward the request to the respective service. The following errors are what I am facing:

  1. nettflix.zuul.exception.ZuulException: Forwarding error
  2. Caused by: com.netflix.client.ClientException: Load balancer does not have available server for client: meeting

Let me share the application.yml for the service, eureka and zuul gateway.

EurekaClient: Application.yml

server:
  port: 8761

eureka:
  instance:
    hostname: localhost
    lease-renewal-interval-in-seconds: 300
  client:
    register-with-eureka: false
    fetch-registry: true
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

ZuulGateWay: application.yml

server:
  port: 8085

spring:
  application:
    name: gatekeeper


zuul:
  routes:
    meeting: /meeting/**
    serviceId: meeting

ribbon:
  eureka:
    enabled: false

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

ZuulGateWay: SpringBootApplication

package com.sagarp.gatekeeper;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

@SpringBootApplication
@EnableEurekaClient
@EnableZuulProxy
public class MeetingApplication {

    public static void main(String[] args) {
        SpringApplication.run(MeetingApplication.class, args);
    }
}

My Service class (meeting): Application.yml

server:
  port: 0
spring:
  application:
    name: meeting
  datasource:
    url: jdbc:mysql://localhost:3306/sagarp?useSSL=false
    username: myUserName
    password: myPassWord
  jpa:
    properties:
      hibernate:
        dialect: org.hibernate.dialect.MySQL5InnoDBDialect
    hibernate:
     ddl-auto: update

eureka:
  client:
    registerWithEureka: true
    fetchRegistry: true
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/
  instance:
    lease-renewal-interval-in-seconds: 5

My Service class (meeting): SpringBootApplication

package com.sagarp;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient
public class MeetingApplication {

    public static void main(String[] args) {
        SpringApplication.run(MeetingApplication.class, args);
    }
}

As you can see, the configuration ensures that all my services are discovered by eureka client.

In the eureka console, I have verified the same, the zuul gateway and my service(meeting) both are visible.

For better view, you can visit my git repo. https://github.com/sagar-patro/demo-microservices

Any help would be very much appreciable

Answer

Andrew Tobilko picture Andrew Tobilko · Sep 19, 2018

Short answer

ribbon:
  eureka:
    enabled: false

Spring Cloud Netflix Zuul uses Netflix’s Ribbon to perform client-side load balancing, and by default, Ribbon would use Netflix Eureka for service discovery. You are skipping service discovery, so you've set ribbon.eureka.enabled to false. Since Ribbon now can’t use Eureka to look up services, you must specify an url for the meeting service:

meeting:
  ribbon:
    listOfServers: localhost:8080

Expanded answer

I will make it more clear for you.

The dependency org.springframework.cloud:spring-cloud-starter-netflix-zuul, which you are currently using in the gatekeeper project, has several compile dependencies:

com.netflix.zuul:zuul-core
org.springframework.boot:spring-boot-starter-web        
org.springframework.boot:spring-boot-starter-actuator       
org.springframework.cloud:spring-cloud-netflix-zuul
org.springframework.cloud:spring-cloud-starter      
org.springframework.cloud:pring-cloud-starter-netflix-hystrix
org.springframework.cloud:spring-cloud-starter-netflix-ribbon
org.springframework.cloud:spring-cloud-starter-netflix-archaius

As you see, it constitutes many components gathered around the com.netflix.zuul:zuul-core module (including Eureka for instance discovery and Ribbon for routing):

enter image description here

When you are launching the gatekeeper application, the default ZuulProxyAutoConfiguration configuration is being applied. It imports Ribbon configuration classes:

@Configuration
@Import({ RibbonCommandFactoryConfiguration.RestClientRibbonConfiguration.class,
        RibbonCommandFactoryConfiguration.OkHttpRibbonConfiguration.class,
        RibbonCommandFactoryConfiguration.HttpClientRibbonConfiguration.class,
        HttpClientConfiguration.class })
@ConditionalOnBean(ZuulProxyMarkerConfiguration.Marker.class)
public class ZuulProxyAutoConfiguration extends ZuulServerAutoConfiguration { ... }

HttpClientRibbonConfiguration, in turn, initialises RibbonLoadBalancingHttpClient which is responsible for the error messages you saw.

That RibbonLoadBalancingHttpClient by default utilises ZoneAwareLoadBalancer which comes from a com.netflix.ribbon:ribbon-loadbalancer package:

By default Zuul load balances using the ZoneAwareLoadBalancer from Ribbon. The algorithm is a round robin of the instances available in discovery, with availability zone success tracking for resiliency. The load balancer will keep stats for each zone and will drop a zone if the failure rates are above a configurable threshold.

If you want to use your own custom load balancer you can set the NFLoadBalancerClassName property for that Ribbon client namespace or override the getLoadBalancerClass() method in the DefaultClientChannelManager. Note that your class should extend DynamicServerListLoadBalancer.

It explains that Zuul delegates routing and load balancing work to Ribbon components and proves that you are actually using Ribbon in the gatekeeper project.

Unless you choose a different load balancer, you should go for my original answer.
I hope it will help.