2

I implemented request using Spring Cloud Feign client. I tried this:

Feign client:

@FeignClient(name = "mail-service")
public interface EmailClient {

    @RequestMapping(method = RequestMethod.POST, value = "/register")
    void setUserRegistration(RegisterUserDTO registerUserDTO);

    @RequestMapping(method = RequestMethod.POST, value = "/password_reset")
    void setUserPasswordReset(PasswordResetDTO passwordResetDTO);
}

Feign configuration:

@Configuration
public class LoadBalancerConfiguration {

    @Bean
    public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(ConfigurableApplicationContext context) {
        return ServiceInstanceListSupplier.builder()
                .withBlockingDiscoveryClient()
                .withSameInstancePreference()
                .withHealthChecks()
                .build(context);
    }
}

Request DTO:

@Getter
@Setter
public class PasswordResetDTO {

    private int id;
}

Controller:

@Autowire
EmailClient emailClient;

@PostMapping("/dummy")
public ResponseEntity<?> test() {

    RegisterUserDTO obj = new RegisterUserDTO();
    obj.setId(12);

    emailClient.setUserRegistration(obj);

    return ok().build();
}

Feign configuration:

spring:
    cloud:
    loadbalancer:
        ribbon:
            enable: false
feign:
    client:
        config:
            default:
                connectTimeout: 5000
                readTimeout: 5000
                loggerLevel: basic
eureka:
    client:
        serviceUrl:
            defaultZone: ${EUREKA_URI:http://localhost:8761/eureka}
    instance:
        preferIpAddress: true

POM.xml

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-netflix-eureka-client</artifactId>
    </dependency>
    <dependency> 
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
    </dependency>

I started 3 instances of endpoint services which are consumed as endpoint from Feign client in a round-robin order. So far so good, it's working as expected. But when I shutdown for example the first service instance the Feign client is not notified of this change and continues to send requests to the inactive service. Do you know how consumer service can be notified when endpoint service is shut down and out of the list of active services? I suppose that I need to configure some health checks?

Do you know how I can solve this issue?

Source code for testing the issue: https://github.com/rcbandit111/eureka-discovery-poc/tree/master

Peter Penzov
  • 1,126
  • 134
  • 430
  • 808
  • You have Java code for Spring Cloud load balancer then have a ribbon dependency. Ribbon should be removed – spencergibb May 05 '21 at 23:14
  • ok, done. Any advice about the main problem? – Peter Penzov May 06 '21 at 00:15
  • Check eureka UI for what status is your inactive instance? Check [this](https://stackoverflow.com/questions/33921557/understanding-spring-cloud-eureka-server-self-preservation-and-renew-threshold) and [this](https://blogs.asarkar.com/technical/netflix-eureka/) it should help with an understanding of eureka works. – Grigorii Riabov May 06 '21 at 07:46
  • Are you getting 500 error code in response? – Conscript May 12 '21 at 10:35

2 Answers2

0

Ribbon - Load balancer

Out of the box it's providing you with round robin loadbalancing, but you can implement your own @RibbonClient (even for a specific service) and design your custom loadbalancing for example based on eureka metadata. The loadbalancing happens on the client side.

Feign - Http client

With @FeignClient you can rapidly develop clients for you other services (or services outside of your infrastructure). It is integrated with ribbon and eureka so you can refer to your services @FeignClient(yourServiceNameInEureka) and what you end up with is a client which loadbalances between the registered instances with your preferred logic. If you are using spring you can use the familiar @RequestMapping annotation to describe the endpoint you are using.

I have mentioned below example with RandomRule. Also can check Github here for the source.

@RibbonClient(name = "cloud-provider", configuration = CloudProviderConfiguration.class)
public class ConsumerApplication { 
    /* ... */
}

class CloudProviderConfiguration {
    @Bean
    public IRule ribbonRule(IClientConfig config) {
        return new RandomRule();
    }
}

ALso, See the Use of Feign to setup High Availability Load Balancer

Sreeram Nair
  • 2,369
  • 12
  • 27
0

According to the documentation, Ribbon will not do anything to mark the server as down when a request fails, instead it relies on pinging the servers or clients of the load balancer to notify the ribbon by calling markServerDown(Server server).

As a result you should either change the interval of which the ribbon pings the server by providing your own implementation:

public class CustomLoadBalancer {
  @Bean 
  public ILoadBalancer ribbonLoadBalancer(IClientConfig config,
                                          ServerList<Server> serverList, ServerListFilter<Server> serverListFilter,
                                          IRule rule, IPing ping) {
      return new ZoneAwareLoadBalancer<>(config, rule, ping, serverList, 
                                         serverListFilter, new PollingServerListUpdater(config));
  }
}

and then use it in your SpringBootApplication configuration like this:

@RibbonClients(defaultConfiguration = CustomLoadBalancer.class)

or you should provide an implementation to call markServerDown(Server) method of ribbon in your client code when you think the time is right.

I suggest you to read this stackoverflow post, I think is directly related to your question.

Conscript
  • 607
  • 6
  • 21
  • I checked on Google. Looks like Ribbon is deprecated `https://stackoverflow.com/questions/65469735/versions-spring-boot-spring-cloud-ribbon-not-working` It will be replaced by spring-cloud-starter-loadbalancer Do you know how to solve the problem using the new framework? – Peter Penzov May 12 '21 at 11:39
  • if you want to use spring-cloud-starter-loadbalancer instead of Ribbon, alongside eureka, you should first disable ribbon by setting `spring.cloud.loadbalancer.ribbon.enabled=false` then make sure `spring-cloud-starter-loadbalancer` is in your classpath. It will automatically use eureka to create its server list and will be used as your client-side load balancer. – Conscript May 12 '21 at 20:11
  • but according to the post above ribbon is not included into Spring parent anymore..... – Peter Penzov May 12 '21 at 20:22
  • That depends on the release train version you are using. look at table-1 in here: https://spring.io/projects/spring-cloud – Conscript May 12 '21 at 21:17
  • Also there are known issues to https://github.com/spring-cloud/spring-cloud-release/wiki/Spring-Cloud-2020.0-Release-Notes , I don't suggest using it yet if you need those features. – Conscript May 12 '21 at 21:21
  • I use the latest Spring cloud – Peter Penzov May 12 '21 at 21:27
  • as it is stated here https://github.com/spring-cloud/spring-cloud-release/wiki/Spring-Cloud-2020.0-Release-Notes in the breaking changes section ribbon support is removed from this release train. I have not tried this but only including spring-cloud-starter-loadbalancer and spring-cloud-netflix dependencies should suffice. – Conscript May 12 '21 at 21:43
  • ok, any idea how to solve the original post problem? – Peter Penzov May 12 '21 at 21:52
  • Yes, I suggest you to try my solution in the answer. – Conscript May 16 '21 at 03:16
  • But something is not clear for me here. Why have you included ribbon in your POM if you are using spring 2020 and you are not intended to use ribbon at the first place? – Conscript May 16 '21 at 03:27
  • If you are building your project from the ground up, https://start.spring.io/ is a great tool to get you started and help you to decide what dependencies and which versions can be included into your project. – Conscript May 16 '21 at 03:29