SpringCloud 客户端负载,简化服务通讯 Ribbon

目录

简介

客户端负载均衡

简化服务通讯

负载均衡策略

 Ribbon 负载策略配置

自定义策略


简介

        Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现。通过Spring Cloud的封装,可以让我们轻松地将面向服务的REST模版请求自动转换成客户端负载均衡的服务调用。Spring Cloud Ribbon虽然只是一个工具类框架,它不像服务注册中心、配置中心、API网关那样需要独立部署,但是它几乎存在于每一个Spring Cloud构建的微服务和基础设施中。因为微服务间的调用,API网关的请求转发等内容,实际上都是通过Ribbon来实现的,Feign它也是基于Ribbon实现的工具。所以,对Spring Cloud Ribbon的理解和使用,对于我们使用Spring Cloud来构建微服务非常重要

客户端负载均衡

        负载均衡在系统架构中是一个非常重要,并且是不得不去实施的内容。因为负载均衡是对系统的高可用、网络压力的缓解和处理能力扩容的重要手段之一。我们通常所说的负载均衡都指的是服务端负载均衡,其中分为硬件负载均衡和软件负载均衡。硬件负载均衡主要通过在服务器节点之间按照专门用于负载均衡的设备,比如F5等;而软件负载均衡则是通过在服务器上安装一些用于负载均衡功能或模块等软件来完成请求分发工作,比如Nginx等。不论采用硬件负载均衡还是软件负载均衡,只要是服务端都能以类似下图的架构方式构建起来:

SpringCloud 客户端负载,简化服务通讯 Ribbon_第1张图片

 负载均衡架构图

        硬件负载均衡的设备或是软件负载均衡的软件模块都会维护一个下挂可用的服务端清单,通过心跳检测来剔除故障的服务端节点以保证清单中都是可以正常访问的服务端节点。当客户端发送请求到负载均衡设备的时候,该设备按某种算法(比如线性轮询、按权重负载、按流量负载等)从维护的可用服务端清单中取出一台服务端端地址,然后进行转发。

        而客户端负载均衡和服务端负载均衡最大的不同点在于上面所提到服务清单所存储的位置。在客户端负载均衡中,所有客户端节点都维护着自己要访问的服务端清单,而这些服务端端清单来自于服务注册中心,比如上一章我们介绍的Eureka服务端。同服务端负载均衡的架构类似,在客户端负载均衡中也需要心跳去维护服务端清单的健康性,默认会创建针对各个服务治理框架的Ribbon自动化整合配置,比如Eureka中的org.springframework.cloud.netflix.ribbon.eureka.RibbonEurekaAutoConfiguration,Consul中的org.springframework.cloud.consul.discovery.RibbonConsulAutoConfiguration。在实际使用的时候,我们可以通过查看这两个类的实现,以找到它们的配置详情来帮助我们更好地使用它。

        通过Spring Cloud Ribbon的封装,我们在微服务架构中使用客户端负载均衡调用非常简单,只需要如下两步:

        ▪️服务提供者只需要启动多个服务实例并注册到一个注册中心或是多个相关联的服务注册中心。

        ▪️服务消费者直接通过调用被@LoadBalanced注解修饰过的RestTemplate来实现面向服务的接口调用。

        这样,我们就可以将服务提供者的高可用以及服务消费者的负载均衡调用一起实现了。

简化服务通讯

  • maven 依赖
	    
			org.springframework.cloud
			spring-cloud-starter-netflix-ribbon
		
  • 采用 RestTemplate 对象,通过在 Eureka 注册的服务名称,直接可以调用接口
  • 注入 RestTemplate 对象时,开启 @LoadBalanced 注解
  • 默认负载策略为轮询
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate (){
        return new RestTemplate();
    }

负载均衡策略

        通过上面的源码解读,我们已经对Ribbon实现的负载均衡器以及其中包含的服务实例过滤器、服务实例信息的存储对象、区域的信息快照等都有了深入的认识和理解,但是对于负载均衡器中的服务实例选择策略只是讲解了几个默认实现的内容,而对于IRule的其他实现还没有详细解读,下面我们来看看在Ribbon中都提供了哪些负载均衡的策略实现。

        如下图所示,可以看到在Ribbon中实现了非常多的选择策略,其中也包含了我们在前面内容中提到过的RoudRobinRule和ZoneAvoidanceRule。下面我们来详细可读一下IRule接口的各个实现。

ribbon的负载均衡策略

▪️AbstractLoadBalancerRule

        负载均衡策略的抽象类,在该抽象类中定义了负载均衡器ILoadBalancer对象,该对象能够在具体实现选择服务策略时,获取到一些负载均衡器中维护的信息来作为分配依据,并以此设计一些算法来实现针对特定场景的高效策略。

▪️RandomRule

        该策略实现了从服务实例清单中随机选择一个服务实例的功能。具体的选择逻辑在一个while(server==null)循环之内,而根据选择逻辑的实现,正常情况下每次选择都应该选出一个服务实例,如果出现死循环获取不到服务实例,如果出现死循环获取不到服务实例时,则很有可能存在并发的Bug。

▪️RoundRobinRule

        该策略实现了按照线性轮询的方式的方式一次选择每个服务实例的功能。它的具体实现如下,其详细结构与RandomRule非常类似。除了循环条件不同外,就是从可用列表中获取所谓的逻辑不通。从循环条件中,我们可以看到增加了一个count计数变量,该变量会在每次循环之后累加,也就是说,如果一直选择不到server超过10次,那么就会结束尝试,并打印一个警告信息No available alive servers after 10tries from load balancer : ... 。

▪️RetryRule

        该策略实现了一个具备重试机制的实例选择功能。从下面的实现中我们可以看到,在其内部还定义了一个IRule对象,默认使用了RoudRobinRule实例。而在choose方法中则实现了对内部定义的策略进行反复尝试的策略,若期间能够选择到具体的服务实例就反悔,若选择不到就根据设置结束时间为阀值(maxRetryMillis参数定义的值+choose方法开始执行的时间戳),当超过该阀值就返回null。

choose方法

▪️WeightedResponseTimeRule

        该策略是对RoundRobinRule的扩展,增加了根据实例等运行情况来计算权重,并根据权重来挑选实例,以达到更优的分配效果,它的实现主要有三个核心内容。

        定时任务

        权重计算

        实例选择

▪️ClientConfigEnabledRoundRobinRule

        该策略较为特殊,我们一般不直接使用它,因为它本身并没有实现什么特殊的处理逻辑,正如下面的源码所示,在它的内部定义了一个RoundRobinRule策略,而choose函数的实现也正是使用了RoundRobinRule的线性轮询机制,所以它实现的功能实际上与RoundRobinRule相同,那么定义它有什么特殊的用处呢?

        虽然我们不会直接使用该策略,但是通过继承该策略,默认的choose就实现了线性轮询机制,在子类中做一些高级策略时通常有可能会存在一些无法实施的情况,那么久可以用父类的实现作为备选。在后面中我们将继续介绍的高级策略均是基于ClientConfigEnabledRoundRobinRule的扩展。

ClientConfigEnabledRoundRobinRule

▪️BestAvailableRule

        该策略继承自ClientConfigEnabledRoundRobinRule,在实现中它注入了负载均衡器的统计对象LoadBalancerStats,同时在具体的choose算法中利用LoadBalancerStats保存的实例统计信息来选择满足要求的实例。从如下源码中我们可以看到,它通过遍历负载均衡器中维护的所有服务实例,会过滤掉故障的实例,并找出并发请求数最小的一个,所以该策略的特性是可选出最空闲的实例。

        同时,由于该算法的核心依据是统计对象LoadBalancerStats,当其为空的时候,该策略是无法执行的。所以从源码中我们可以看到,当loadBalancerStats为空的时候,它会采用父类的线性轮询策略,正如我们在介绍ClientConfigEnabledRoundRobinRule时那样,它的子类在无法满足实现高级策略的时候,可以使用线性轮询策略的特性。后面将要介绍的策略因为也都继承自ClientConfigEnabledRoundRobinRule,所以它们都会具有这样的特性。

▪️PredicateBasedRule

        这是一个抽象策略,它也继承了ClientConfigEnabledRoundRobinRule,从其命名中可以猜出这是一个基于Predicate实现的策略,Predicate是Google Guava Collections工具对集合进行过滤掉条件接口。

        Google Guava Collections是一个对Java Collections Framework增强和扩展的开源项目。虽然Java Collections Framework已经能够满足我们大多数抢空下使用集合的要求,但是当遇到一些特殊情况时,我们的代码会比较冗长且容易出错。Google Guava Collections可以帮助我们让集合操作代码更为简短精炼并大大增强代码的可读性。

▪️AvailabilityFilteringRule

        该策略继承自上面介绍的抽象策略PredicateBasedRule,所以它也继承了“先过滤清单,再轮询选择”的的基本处理逻辑,其中过滤条件使用了AvailabilityPredicate。简单地说,该策略通过线性抽样的方式直接尝试寻找可用且较空闲的实例来使用,优化了父类每次都要遍历所有实例的开销。

▪️ZoneAvoidanceRule

        该策略我们在介绍负载均衡器ZoneAwareLoadBalancer时已经提到过,它也是PredicateBasedRule的具体实现类,在之前的介绍中主要针对ZoneAvoidanceRule中用于选择Zone区域策略的一些静态函数,比如createSnapshot(LoadBalancerStats lbStats)、getAvailableZones(Map snapshot, double triggeringLoad,double triggeringBlackoutPercentage)。在这里我们将详细看看ZoneAvoidanceRule作为服务实例过滤条件的实现原理。从下面ZoneAvoidanceRule的源码片段中可以看到,它使用了CompositePredicate来进行服务实例清单的过滤。这是一个组合过来条件,在其构造函数中,它以ZoneAvoidanceRule为主过滤条件,AvailabilityPredicate为次过滤条件初始化了组合过滤条件的实例。

 Ribbon 负载策略配置

  • 饥饿加载
ribbon:
  eager-load:
    enabled: true
    clients: ribbon-consumer
  • yml 负载策略配置
# 全局配置
ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

# 局部服务配置
ribbon-consumer:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
  • @Configuration 配置
@Configuration
public class RibbonConfig {

    @Bean
    public IRule ribbonRule(){
        return new ZoneAvoidanceRule();
    }
}
  • 注解配置
@Configuration
@RibbonClient(name = "eureka-sonsume",configuration = com.netflix.loadbalancer.ZoneAvoidanceRule.class)
public class RibbonConfig {


}

自定义策略


public class IpUserHashRule extends AbstractLoadBalancerRule {

    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {
        // 可以搁浅
    }

    @Override
    public Server choose(Object o) {

        //实现方法,返回 server 即可
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        return null;
    }
}

你可能感兴趣的:(集群/灾备/架构,Spring,Cloud,Mr.chenyb)