brief introduction
Spring Cloud Ribbon is a client load balancing tool based on Netflix Ribbon. After registering the Ribbon client in the registry, Ribbon can automatically help service consumers call interfaces based on some load balancing algorithm, such as polling (default), random, weighted polling, weighted random, etc.
Project introduction
- sc-parent, parent module (see Spring Cloud Learning Notes (1): Eureka Registry)
- sc-eureka, Registry (see Spring Cloud Learning Notes (1): Eureka Registry)
- sc-provider-random, provider of random ports
- sc-consumer, a consumer using Ribbon load balancing
Provider of random ports
1. Create the sub-module project sc-provider-random, pom.xml under the parent module:
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.cf</groupId> <artifactId>sc-parent</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <artifactId>sc-provider-random</artifactId> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> </dependencies> </project>
2. Create the startup class provider.ProviderApplication:
package provider; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ProviderApplication { public static void main(String[] args) { SpringApplication.run(ProviderApplication.class, args); } }
3. Create Controller: provider.controller.BookController
package provider.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RequestMapping("/book") @RestController public class BookController { @GetMapping("/list") public String getBookList(){ System.out.println("------------I was interviewed.-----------"); return "[\"Java Beginning to Abandoning\",\"C++Beginning to Abandoning\",\"Python Beginning to Abandoning\",\"C Beginning to Abandoning\"]"; } }
4. Create application.yml:
server: port: 0 #By default 8080, the configuration of random ports needs to be set to 0 spring: application: name: sc-provider-random eureka: client: serviceUrl: defaultZone: http://localhost:8080/eureka/ instance: instance-id: ${spring.application.name}:${random.value} #Random generation of instance names
5. Start the registry sc-eureka and two providers sc-provider-random in turn. The providers sc-provider-random have different start ports each time. You can see that two instances of sc-provider-random are registered in the registry:
Consumers using Ribbon load balancing
1. Create the sub-module project sc-consumer, pom.xml under the parent module:
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.cf</groupId> <artifactId>sc-parent</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <artifactId>sc-consumer</artifactId> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!-- spring-cloud-starter-netflix-eureka-client Dependency is already included ribbon --> <!-- <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency> --> </dependencies> </project>
2. Create the startup class consumer.ConsumerApplication:
package consumer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @SpringBootApplication public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } //Integrating Ribbon for RestTemplate to provide load balancing capabilities @LoadBalanced @Bean public RestTemplate restTemplate(){ return new RestTemplate(); } }
3. Create Controller: consumer.controller.ConsumerController that invokes provider services
package consumer.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @RestController public class ConsumerController { @Autowired private RestTemplate restTemplate; @GetMapping("/getBookList") public String getBookList(){ //sc-provider-random is the provider service name return restTemplate.getForObject("http://sc-provider-random/book/list", String.class); } }
Note: When using Ribbon load balancing, service names cannot be underlined, otherwise valid hostname exceptions will occur
4. Create application.yml:
server: port: 8083 spring: application: name: sc-consumer eureka: client: registerWithEureka: false serviceUrl: defaultZone: http://localhost:8080/eureka/
5. Start the registry sc-eureka, two providers sc-provider-random and consumer sc-consumer in turn, and visit http://localhost:8083/getBook List:
After multiple visits, through the log printing of the console, it can be found that consumers access two provider instances by polling.
Change load balancing policy
By default, Ribbon uses Round Robin Rule (polling) as a load balancing strategy. We can implement the IRule interface or replace the default polling strategy with the existing load balancing strategy provided by Ribbon. As follows, the default polling is replaced by a random access policy and added to the consumer startup class:
@Bean public IRule randomRule(){ return new RandomRule();//Replace default polling with random access policy }
Custom Load Balancing Policy
A small example of a custom load balancing policy is still a polling access policy, which only accesses the next service instance five times per service instance.
1. Create the class consumer.rule.MyRule:
package consumer.rule; import java.util.List; import com.netflix.client.config.IClientConfig; import com.netflix.loadbalancer.AbstractLoadBalancerRule; import com.netflix.loadbalancer.ILoadBalancer; import com.netflix.loadbalancer.Server; public class MyRule extends AbstractLoadBalancerRule { private int currIndex = 0;//Current Service Instance Index private int currCount = 0;//Number of visits to the current service instance private static final int MAX_COUNT = 5;//Maximum number of visits per service instance is 5 public Server choose(ILoadBalancer lb, Object key) { if (lb == null) { return null; } Server server = null; while (server == null) { if (Thread.interrupted()) { return null; } List<Server> upList = lb.getReachableServers(); List<Server> allList = lb.getAllServers(); int serverCount = allList.size(); if (serverCount == 0) { /* * No servers. End regardless of pass, because subsequent passes * only get more restrictive. */ return null; } int index = chooseRandomInt(upList.size()); server = upList.get(index); if (server == null) { /* * The only time this should happen is if the server list were * somehow trimmed. This is a transient condition. Retry after * yielding. */ Thread.yield(); continue; } if (server.isAlive()) { return (server); } // Shouldn't actually happen.. but must be transient or a bug. server = null; Thread.yield(); } return server; } protected synchronized int chooseRandomInt(int serverCount) { currCount++; if(currCount > MAX_COUNT){ currIndex++; if(currIndex >= serverCount){ currIndex = 0; } currCount = 1; } return currIndex; } @Override public Server choose(Object key) { return choose(getLoadBalancer(), key); } @Override public void initWithNiwsConfig(IClientConfig clientConfig) { // TODO Auto-generated method stub } }
2. Modify configuration:
@Bean public IRule randomRule(){ return new MyRule(); }
Start the registry sc-eureka, two providers sc-provider-random, and consumer sc-consumer in turn, and visit http://localhost:8083/getBook List for testing.