SpringCloud-Ribbon: load balancing (client-based)

1 Load balancing and Ribbon

What is Ribbon?

  • Spring Cloud Ribbon is a set of client load balancing tools based on Netflix Ribbon .

  • Simply put, Ribbon is an open source project released by Netflix. Its main function is to provide a client-side software load balancing algorithm to connect Netflix's middle-tier services together. Ribbon's client component provides a complete set of configuration items, such as connection timeout, retry, etc. Simply put, it is to list all the following LoadBalancer (abbreviated as LB: load balancing) in the configuration file, and Ribbon will automatically help you to connect these machines based on certain rules (such as simple polling, random connection, etc.) . We also easily implement custom load balancing algorithms using Ribbon!

What can Ribbon do?

img

  • LB, or LoadBalancer, is an application often used in microservices or distributed clusters.

  • Simply put, load balancing is to evenly distribute user requests to multiple services, so as to achieve HA (high usage) of the system.

  • Common load balancing software includes Nginx, Lvs and so on.

  • Both Dubbo and SpringCloud provide us with load balancing, and SpringCloud's load balancing algorithm can be customized .

  • Simple classification of load balancing:

    • Centralized LB

      • That is, an independent LB facility is used between the service provider and the consumer, such as Nginx (reverse proxy server) , which is responsible for forwarding the access request to the service provider through a certain policy!
    • Process LB

      • The LB logic is integrated into the consumer, and the consumer learns which addresses are available from the service registry, and then selects a suitable server from these addresses.
      • Ribbon belongs to the in-process LB. It is just a class library, which is integrated in the consumer process. The consumer uses it to obtain the address of the service provider!

2 Integrated Ribbon

springcloud-consumer-dept-80 adds Ribbon and Eureka dependencies to pom.xml

<!--Ribbon-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-ribbon</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>
<!--Eureka: Ribbon需要从Eureka服务中心获取要拿什么-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>

Configure Eureka in application.yml file

# Eureka配置
eureka:
  client:
    register-with-eureka: false # 不向 Eureka注册自己
    service-url: # 从三个注册中心中随机取一个去访问
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/

The main startup class is annotated with @EnableEurekaClient to enable Eureka

//Ribbon 和 Eureka 整合以后,客户端可以直接调用,不用关心IP地址和端口号
@SpringBootApplication
@EnableEurekaClient //开启Eureka 客户端
public class DeptConsumer_80 {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(DeptConsumer_80.class, args);
    }
}

Custom Spring configuration class: ConfigBean.java configures load balancing to implement RestTemplate

@Configuration
public class ConfigBean {
    
    
    //@Configuration -- spring  applicationContext.xml
    @LoadBalanced //配置负载均衡实现RestTemplate
    @Bean
    public RestTemplate getRestTemplate() {
    
    
        return new RestTemplate();
    }
}

Modify the controller: DeptConsumerController.java

//Ribbon:我们这里的地址,应该是一个变量,通过服务名来访问
//private static final String REST_URL_PREFIX = "http://localhost:8001";
private static final String REST_URL_PREFIX = "http://SPRINGCLOUD-PROVIDER-DEPT";

If you can't get up, delete the ribbon dependency (it may not be needed)

3 Use Ribbon to achieve load balancing

flow chart:

img

1. Create two new service providers Moudle: springcloud-provider-dept-8003, springcloud-provider-dept-8002

2. Referring to springcloud-provider-dept-8001, add pom.xml dependencies, mybatis and application.yml configurations under resourcece, and Java code for the other two Moudles in turn

3. Start all service tests (determine the number of services to start according to your own computer configuration), visit http://eureka7001.com:7002/ to view the resultsimg

Test access http://localhost/consumer/dept/list At this time, the random access is the service provider 8003

img

Visit http://localhost/consumer/dept/list again. This time the random service provider is 8001

img

Each visit to http://localhost/consumer/dept/list above randomly accesses a service provider in the cluster. This situation is called polling, and the polling algorithm can be customized in SpringCloud.

How to switch or customize the rules?

Configure in the ConfigBean under the springcloud-provider-dept-80 module and switch to use different rules

@Configuration
public class ConfigBean {
    
    
    //@Configuration -- spring  applicationContext.xml
    /**
     * IRule:
     * RoundRobinRule 轮询策略
     * RandomRule 随机策略
     * AvailabilityFilteringRule : 会先过滤掉,跳闸,访问故障的服务~,对剩下的进行轮询~
     * RetryRule : 会先按照轮询获取服务~,如果服务获取失败,则会在指定的时间内进行,重试
     */
    @Bean
    public IRule myRule() {
    
    
        return new RandomRule();//使用随机策略
        //return new RoundRobinRule();//使用轮询策略
        //return new AvailabilityFilteringRule();//使用轮询策略
        //return new RetryRule();//使用轮询策略
    }
}

You can also customize the rules and customize a configuration class MyRule.java under the myRule package. Note: This package should not be at the same level as the package where the main startup class is located, but at the same level as the package where the startup class is located :

img

MyRule.java

/**
 * @Auther: csp1999
 * @Date: 2020/05/19/11:58
 * @Description: 自定义规则
 */
@Configuration
public class MyRule {
    
    
    @Bean
    public IRule myRule(){
    
    
        return new MyRandomRule();//默认是轮询RandomRule,现在自定义为自己的
    }
}

The main startup class enables load balancing and specifies a custom MyRule configuration class

//Ribbon 和 Eureka 整合以后,客户端可以直接调用,不用关心IP地址和端口号
@SpringBootApplication
@EnableEurekaClient
//在微服务启动的时候就能加载自定义的Ribbon类(自定义的规则会覆盖原有默认的规则)
@RibbonClient(name = "SPRINGCLOUD-PROVIDER-DEPT",configuration = MyRule.class)//开启负载均衡,并指定自定义的规则
public class DeptConsumer_80 {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(DeptConsumer_80.class, args);
    }
}

Custom rules (here we refer to the default rule code in Ribbon and change it slightly): MyRandomRule.java

public class MyRandomRule extends AbstractLoadBalancerRule {
    
    
    /**
     * 每个服务访问5次则换下一个服务(总共3个服务)
     * <p>
     * total=0,默认=0,如果=5,指向下一个服务节点
     * index=0,默认=0,如果total=5,index+1
     */
    private int total = 0;//被调用的次数
    private int currentIndex = 0;//当前是谁在提供服务
    //@edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE")
    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(serverCount);//生成区间随机数
            //server = upList.get(index);//从或活着的服务中,随机获取一个
            //=====================自定义代码=========================
            if (total < 5) {
    
    
                server = upList.get(currentIndex);
                total++;
            } else {
    
    
                total = 0;
                currentIndex++;
                if (currentIndex > upList.size()) {
    
    
                    currentIndex = 0;
                }
                server = upList.get(currentIndex);//从活着的服务中,获取指定的服务来进行操作
            }
            //======================================================
            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 int chooseRandomInt(int serverCount) {
    
    
        return ThreadLocalRandom.current().nextInt(serverCount);
    }
    @Override
    public Server choose(Object key) {
    
    
        return choose(getLoadBalancer(), key);
    }
    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {
    
    
        // TODO Auto-generated method stub
    }
}

Guess you like

Origin blog.csdn.net/qq_41355222/article/details/123983020