六. SpringCloud Ribbon 负载均衡与修改默认的负载策略

一. 基础概述

自己做的记录,推荐看大神的Ribbon的负载均衡策略及原理

  1. 什么是负载均衡: 简单来说就是根据算法指定将用户的请求平摊分片到多个服务上,或打到指定的服务上,从而达到服务的高可用,负载均衡分为软负载nginx,lvs与应负载F5等
  2. 在前面 SpringCloud 中使用注册中心,使用 RestTemplate 根据服务名称在注册中心获取指定服务的调用地址,使用 @LoadBalanced 修饰 RestTemplate ,默认是轮询算法(G版本前)
  3. SpringCloud 中有好多框架默认整合引入了 Ribbon 依赖,例如spring-cloud-starter-zookeeper-discovery 或者spring-cloud-starter-netflix-eureka-client等,也可以单独引入
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>

二. 通过 @LoadBalanced 注解了解 Ribbon 负载均衡

  1. 点开 @LoadBalanced 注解源码,会发现该注解没做其它附加操作,只是用来修饰 RestTemplate 用来标识一下,是借助 Spring 装载机制完成的负载设置
  2. 查看 spring.factories 文件,找到项目启动时默认自动装载的类 RibbonAutoConfiguration
    在这里插入图片描述
  3. RibbonAutoConfiguration 使用 @AutoConfigureBefore({LoadBalancerAutoConfiguration.class, AsyncLoadBalancerAutoConfiguration.class}) 修饰该类,表示在创建该类注入以前首先会创建 LoadBalancerAutoConfiguration 注入到容器中,通过 LoadBalancerAutoConfiguration 向 RestTemplate 添加了一个请求拦截器 LoadBalancerInterceptor
		@Bean
        @ConditionalOnMissingBean
        public RestTemplateCustomizer restTemplateCustomizer(final LoadBalancerInterceptor loadBalancerInterceptor) {
            return (restTemplate) -> {
                List<ClientHttpRequestInterceptor> list = new ArrayList(restTemplate.getInterceptors());
                //添加一个 LoadBalancerInterceptor 请求拦截器
                list.add(loadBalancerInterceptor);
                restTemplate.setInterceptors(list);
            };
        }
  1. 当发起请求时首先会执行该 LoadBalancerInterceptor 请求拦截器中的 intercept() 方法,在该方法中调用了 LoadBalancerClient 的 executr() 方法
 		public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException {
        URI originalUri = request.getURI();
        String serviceName = originalUri.getHost();
        Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
        //调用 RibbonLoadBalancerClient 中的 execute() 方法
        return (ClientHttpResponse)this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));
    }
  1. LoadBalancerClient 是父接口当前默认调用的是 RibbonLoadBalancerClient 子类中的 execute() 方法,在该方法中首先换气一个 ILoadBalancer 负载均衡器,通过 ILoadBalancer 去获取 Server 示例
public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint) throws IOException {
		//1.首先获取一个 ILoadBalancer 
        ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId);
        //2.通过获取到的 ILoadBalancer 获取 Server
        Server server = this.getServer(loadBalancer, hint);
        if (server == null) {
            throw new IllegalStateException("No instances available for " + serviceId);
        } else {
            RibbonLoadBalancerClient.RibbonServer ribbonServer = new RibbonLoadBalancerClient.RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server));
            return this.execute(serviceId, (ServiceInstance)ribbonServer, (LoadBalancerRequest)request);
        }
    }

	//获取 Server 方法
	protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
		//通过 loadBalancer 调用 chooseServer()
        return loadBalancer == null ? null : loadBalancer.chooseServer(hint != null ? hint : "default");
    }
  1. 查看 ILoadBalancer 中的 chooseServer() 方法(ILoadBalancer 是一个父接口,当前默认调用 BaseLoadBalancer 子类中的),在 chooseServer() 方法中会发现通过 IRule 调用 .choose() 方法返回的实例,而这个 IRule 就是 Ribbon 负载算法的父接口,根据算法对该接口进行了具体实现,当前默认使用 RoundRobinRule 轮询算法子类,并且ILoadBalance(BaseLoadBalancer是实现类)向 Eureka 注册中心获取服务注册列表,每10s一次向EurekaClient发送“ping”,来判断服务的可用性,如果服务的可用性发生了改变或者服务数量和之前的不一致,则从注册中心更新或者重新拉取。LoadBalancerClient有了这些服务注册列表,然后通过IRule来进行负载均衡。
	public Server chooseServer(Object key) {
        if (this.counter == null) {
            this.counter = this.createCounter();
        }

        this.counter.increment();
        if (this.rule == null) {
            return null;
        } else {
            try {
            	//通过 
                return this.rule.choose(key);
            } catch (Exception var3) {
                logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", new Object[]{this.name, key, var3});
                return null;
            }
        }
    }

二. Ribbon 核心组件 IRule 与负载算法

以负载调用指定服务为例,Ribbon实现了 IRule 接口,该接口中提供了根据 key,操作在注册中心获取到的服务数据的方法,对于这些方法,Ribbon 根据算法不同,提供了具体实现,支持 轮询(默认),随机,一致性哈希,哈希,加权

在这里插入图片描述
在这里插入图片描述

  • RoundRobinRule 轮询(默认的)策略: 取模方式每次都取下一个服务器,假设有两台服务器,第一次1%2=1,选择下标为1的,第二次2%2=0选择下标为0的,第三次3%2=1选择下标为1的…
  • RandomRule 随机策略: 使用 JDK 内部的 random 随机获取
  • WeightedResponseTimeRule 继承了 RoundRobinRule 轮询+权重: 第一次没有权重列表,采用父类的轮询方式,有一个默认每30秒更新一次权重列表的定时任务,定时任务根据实例的响应时间来更新权重列表,用一个(0,1)的随机 double 值乘以最大的权重得到 randomWeight,遍历权重列表,获取第一个比randomWeight大的实例下标
  • RetryRule 重试: 先按照RoundRobinRule 轮询获取,如果指定时间内获取失败进行重试,获取可用的服务
  • BestAvailableRule: 先过滤掉由于多次访问故障处于断路器跳闸状态的服务,然后选择请求数量最少的服务,如果没选上,使用轮询去获取
  • AvailabilityFiteringRule: 先过滤掉故障示例,在选择并发量最小的
  • ZoneAvoidanceRule: 默认规则,复合判断server所在的区域的性能和server的可用性选择服务器

三. 项目中如何修改负载策略

注意点: 在修改负载策略时,需要创建配置类,配置类不可以放在 @ComponentScan 扫描自动注入的当前包或子包路径下(@SpringBootApplication注解底层也是使用的 @ComponentScan),否则所有的 Ribbon 都会使用当前配置的这个负载策略
假设服务消费方A,调用服务提供方"CLOUD-PAYMENT-SERVICE" (注册到注册中心的名字),将默认的轮询算法修改为随机算法

  1. 服务消费方A中创建配置类(注意不要放到@ComponentScan扫描的包或子包下),将指定的负载算法对象注入到容器中,此处指定随机
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MySelfRule {
	// RandomRule 随机负载,是IRule接口的实现类
    @Bean
    public IRule myRule(){
        return new RandomRule();
    }
}
  1. 还是使用以前配置注入的 RestTemplate 对象,根据服务名称向注册中心获取服务调用列表
  2. 启动类添加 @RibbonClient 注解修饰,指定当前服务消费方调用哪个服务提供方时,使用的指定的负载策略
import myrule.MySelfRule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;

@SpringBootApplication
@EnableEurekaClient
//@EnableDiscoveryClient
//指定服务提供方"CLOUD-PAYMENT-SERVICE",使用 MySelfRule 配置类中配置的负载策略
@RibbonClient(name="CLOUD-PAYMENT-SERVICE", configuration = MySelfRule.class)
public class OrderMain80 {
    public static void main(String[] args) {
        SpringApplication.run(OrderMain80.class,args);
    }
}
  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值