美文网首页
SpringCloud实践系列:Ribbon负载均衡

SpringCloud实践系列:Ribbon负载均衡

作者: 分布式与微服务 | 来源:发表于2023-03-08 09:29 被阅读0次

一、概述

1.1、什么是Ribbon

Ribbon是负载均衡器,用于解决服务集群的负载均衡

1.2、常见负载均衡

  • 集中式LB 即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5, 也可以是软件,如nginx), 由该设施负责把访问请求通过某种策略转发至服务的提供方;

  • 进程内LB 将LB逻辑到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器。

  • Ribbon就属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址。

1.3、Ribbon工作流程

二、快速使用

  • 导入依赖

    springcloud alibaba对Ribbon做了兼容,无需单独引用


```
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-dependencies</artifactId>
    <version>2.2.0.RELEASE</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-dependencies</artifactId>
    <version>Hoxton.RELEASE</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>

```
  • 启动类加注解

    // 启动类上开启服务注册与发现功能
    @EnableDiscoveryClient 
    
    
  • 配置文件

    可以不写,直接使用默认的

    ribbon: 
        eager-load: 
            enabled: true # 是否立即加载(项目启动是,是否拉取实例列表),默认是false(项目启动不拉取,第一次请求时拉取).
            clients: # 指定哪些服务需要立即加载,数组形式
                - cloud-goods
                - cloud-goods2
    
    
  • 使用

    在RestTemplate配置文件中,加@LoadBalanced注解

    import org.springframework.cloud.client.loadbalancer.LoadBalanced;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.http.client.ClientHttpRequestFactory;
    import org.springframework.http.client.SimpleClientHttpRequestFactory;
    import org.springframework.web.client.RestTemplate;
    
    @Configuration
    public class ApiConfig {
        @Bean
        @LoadBalanced
        public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
            return new RestTemplate(factory);
        }
    
        @Bean
        public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
            SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
            factory.setReadTimeout(5000);
            factory.setConnectTimeout(5000);
            return factory;
        }
    }
    
    

三、源码简单追踪

3.1、源码简单追踪

LoadBalancerAutoConfiguration类中有个ribbonInterceptor方法,该方法调了LoadBalancerInterceptor类

public class LoadBalancerAutoConfiguration {
    ......
    static class LoadBalancerInterceptorConfig {
        ......
        @Bean
        public LoadBalancerInterceptor ribbonInterceptor(LoadBalancerClient loadBalancerClient, LoadBalancerRequestFactory requestFactory) {
            return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
        }
        ......
    }    
}

LoadBalancerInterceptor类中有个关键的方法intercept,调用RibbonLoadBalancerClient的execute方法,获取实例列表

public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
    ......

    public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException {
        // 获得url
        URI originalUri = request.getURI();
        // 拿到serviceName
        String serviceName = originalUri.getHost();
        Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
        // 获取实例列表,实现负载均衡
        return (ClientHttpResponse)this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));
    }
}

RibbonLoadBalancerClient类的execute方法中,获取实例列表,实现负载均衡

public class RibbonLoadBalancerClient implements LoadBalancerClient {
    public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint) throws IOException {
        // 获取实例列表(先从本地缓存取,没有则从注册中心取)
        ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId);
        // 实现负载均衡(选取负载均衡策略)
        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);
        }
    }
}

3.2、Ribbon核心组件

接口 作用 默认值
IClientConfig 读取配置 DefaultClientConfigImpl
IRule 负载均衡规则,选择实例 ZoneAvoidanceRule
IPing 筛选掉ping不通的实例 DummyPing
ServerList 交给Ribbon的实例列表 Ribbon:ConfigurationBasedServerList Spring Cloud Alibaba:NacosServerList
ServerListFilter 过滤掉不符合条件的实例 ZonePreferenceServerListFilter
ILoadBalance Ribbon的入口 ZoneAwareLoadBalance
ServerListUpdater 更新交给Ribbon的List的策略 PollingServerListUpdater

四、负载均衡策略

4.1、内置的负载均衡策略

Ribbon核心组件IRule: 根据特定算法从服务列表中选取一个需要访问的服务

IRule是一个接口,有7个自带的实现类,可以实现不同的负载均衡算法规则

规则名称 特点
AvailabilityFilteringRule 过滤掉一直连接失败的被标记为circuit tripped的后端Server,并 过滤掉那些高并发的后端Server或者使用一个AvailabilityPredicate 来包含过滤server的逻辑,其实就是检查status里记录的各个server 的运行状态
BestAvailableRule 选择一个最小的并发请求的server,逐个考察server, 如果Server被tripped了,则跳过
RandomRule 随机选择一个Server
ResponseTimeWeightedRule 已废弃,作用同WeightedResponseTimeRule
WeightedResponseTimeRule 根据响应时间加权,响应时间越长,权重越小,被选中的可能性越低
RetryRule 对选定的负载均衡策略加上重试机制,在一个配置时间段内当 选择Server不成功,则一直尝试使用subRule的方式选择一个 可用的Server
RoundRobinRule 轮询选择,轮询index,选择index对应位置的Server
ZoneAvoidanceRule(默认) 默认的负载均衡策略,即复合判断Server所在区域的性能和Server的可用性 选择Server,在没有区域的环境下,类似于轮询(RandomRule)【优中选优,再轮询】

4.2、切换ribbon负载均衡策略

  • 编写配置

    在客户端配置

    注意:不能在启动类的同级/子集下配置

    import com.netflix.loadbalancer.IRule;
    import com.netflix.loadbalancer.RoundRobinRule;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class MyRibbonRule {
        @Bean
        public IRule getRule() {
            return new RoundRobinRule();
        }
    }
    
    
  • 配置启动类

    // 启动类上加该注解即可
    @RibbonClient(name = "cloud-goods", configuration = {MyRibbonRule.class})
    
    

4.3、自定义策略(使用Nacos权重)

直接在页面上修改权重,是无效的。因为负载均衡中引入了ribbon,没有改ribbon配置,还是使用的ribbon的默认负载均衡策略。

使用nacos的权重,需要自定义负载均衡策略

  • 配置权重

    • 在页面上配置
*   在代码中配置

    ```
    spring:
      application:
        name: cloud-goods # 服务名
      cloud:
        nacos:
          discovery:
            server-addr: localhost:8848 # 指定nacos-server的地址
            username: nacos
            password: nacos
            weight: 10
    server:
      port: 9001
    
    ```
  • 自定义负载均衡策略

    拿到nacos的配置,进行自定义的负载均衡策略

    注意:不能在启动类的同级/子集下配置

    import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
    import com.alibaba.cloud.nacos.ribbon.NacosServer;
    import com.alibaba.nacos.api.exception.NacosException;
    import com.alibaba.nacos.api.naming.NamingService;
    import com.alibaba.nacos.api.naming.pojo.Instance;
    import com.netflix.client.config.IClientConfig;
    import com.netflix.loadbalancer.AbstractLoadBalancerRule;
    import com.netflix.loadbalancer.BaseLoadBalancer;
    import com.netflix.loadbalancer.Server;
    import org.springframework.beans.factory.annotation.Autowired;
    
    public class NacosWeightedRule extends AbstractLoadBalancerRule {
        /**
         * NacosDiscoveryProperties内置了基于权重的负载均衡算法
         */
        @Autowired
        private NacosDiscoveryProperties nacosDiscoveryProperties;
    
        /**
         * 读取配置文件并初始化NacosWeightedRule
         * @param iClientConfig
         */
        @Override
        public void initWithNiwsConfig(IClientConfig iClientConfig) {
    
        }
    
        /**
         * 实现基于权重的负载均衡算法
         * @param o
         */
        @Override
        public Server choose(Object o) {
            try {
                BaseLoadBalancer loadBalancer = (BaseLoadBalancer)this.getLoadBalancer();
                //想要请求的微服务名称
                String name = loadBalancer.getName();
                //拿到服务发现新的相关的api
                NamingService namingService = nacosDiscoveryProperties.namingServiceInstance();
                //Nacos client自动通过基于权重的负载均衡算法,给我们选择一个实例
                Instance instance = namingService.selectOneHealthyInstance(name);
                return new NacosServer(instance);
            } catch (NacosException e) {
                return null;
            }
        }
    }
    
    
  • 引用自定义的负载均衡策略

    • 引用配置类

      import com.netflix.loadbalancer.IRule;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      
      @Configuration
      public class MyRibbonRule {
          @Bean
          public IRule getRule() {
              return new NacosWeightedRule();
          }
      }
      
      
    • 启动类

      // 启动类上加该注解即可
      @RibbonClient(name = "cloud-goods", configuration = {MyRibbonRule.class
      

相关文章

网友评论

      本文标题:SpringCloud实践系列:Ribbon负载均衡

      本文链接:https://www.haomeiwen.com/subject/radtzrtx.html