Search code examples
springspring-bootspring-cloudspring-cloud-feignspring-cloud-loadbalancer

Change spring cloud loadbalancer default RoundRobin Algorithm


I'm trying to migrate from Ribbon to Spring Cloud LoadBalancer because Ribbon is now in maintenance mode, and I can't add ribbon dependency in spring initialize using the default spring-boot 2.4.2 version. I'm currently using OpenFeign and I'm doing some test trying to change the default RoundRobin Load Balancer configuration to Random Rule. This is what I have

When using Ribbon I just had to change the default configuration

@Configuration
public class RibbonConfiguration {

    @Bean
    public IRule loadBalancingRule() {
//        return new RoundRobinRule();
        return new RandomRule();
    }

}

And add the annotation @RibbonClient in the client (also works adding it in the MainApplication class)

@FeignClient("first-service")
@RibbonClient(name = "first-service", configuration = RibbonConfiguration.class)
public interface HelloClient {
    @GetMapping("/hello")
    String hello();
}

But I can't make it works using spring-cloud-loadbalancer. I know is included by default in the spring-cloud-starter-openfeign dependency so my pom is like this

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.demo</groupId>
    <artifactId>edge-service</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>edge-service</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>11</java.version>
        <spring-cloud.version>2020.0.1</spring-cloud.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

Having my FeignClients like this (without any Ribbon annotation)

@FeignClient("first-service")
public interface HelloClient {
    @GetMapping("/hello")
    String hello();
}

@FeignClient("second-service")
public interface WorldClient {
    @GetMapping("/world")
    String world();
}

And my RestController like this

@RestController
public class HelloWorldController {
    @Autowired
    private HelloClient helloClient;
    @Autowired
    private WorldClient worldClient;

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    
    @GetMapping("/hello-world")
    public String helloWorld() {
        logger.info("Calling the clients");
        return helloClient.hello() + " - " + worldClient.world();
    }
}

Without doing anything else it seems to works the load balancing (with two instance of hello-service running still working and is calling alternate), but I want to change the default loadbalancer algorithm (RoundRobin) to anotherone (like random or anyone custom) to test the difference. I suppose I have to change something in the FeignConfiguration class, but I have searched without success.

Hope you can help me so I can get rid of Ribbon and start using Spring Cloud LoadBalancer without problems ! :C


Solution

  • Ribbon configuration will not work for Spring Cloud LoadBalancer. If you add spring-cloud-starter-loadbalancer, the default RoundRobin bean will be created for you, so you do not need to set it up. However, if you want switch to the Random algorithm, you will need to add the bean to override the default one to your configuration, like so:

    public class CustomLoadBalancerConfiguration {
    
    @Bean
    ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,
            LoadBalancerClientFactory loadBalancerClientFactory) {
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
        return new RandomLoadBalancer(loadBalancerClientFactory
                .getLazyProvider(name, ServiceInstanceListSupplier.class),
                name); 
      }
    }
    

    You can read more about it here.

    Once you have your LoadBalancer configuration class with the new bean added, you can set your clients to use it with the @LoadBalancerClient or @LoadBalancerClients annotation. You can read in detail how to do it in the docs.