笔记:Spring Cloud Ribbon 客户端配置详解

自动化配置

由于 Ribbon 中定义的每一个接口都有多种不同的策略实现,同时这些接口之间又有一定的依赖关系,Spring Cloud Ribbon 中的自动化配置能够很方便的自动化构建接口的具体实现,接口如下:

  • IClientConfig:Ribbon 的客户端配置,默认采用 com.netflix.client.config.DefaultClientConfigImpl 实现。
  • IRule:Ribbon 的负载均衡策略,默认采用 com.netflix.loadbalancer.ZoneAvoidanceRule 实现,该策略能够在多区域环境下选择最佳区域的实例进行访问
  • IPing:Ribbon 的实例检查策略,默认采用 com.netflix.loadbalancer.NoOpPing 实现,该检查策略是一个特殊的实现,实际上他并不会检查实例是否可用,而是始终返回 true ,默认认为所有服务实例都是可以使用
  • ServerList<Server>:服务实例清单的维护机制,默认采用 com.netflix.loadbalancer.ConfigurationBasedServerList 实现。
  • ServerListFilter<Server>:服务实例清单过滤机制,默认采用 org.springframework.cloud.netflix.ribbon.ZonePreferenceServerListFilter 实现,该策略能够优先过滤出与请求调用方处理同区域的服务实现
  • ILoadBalancer:负载均衡器,默认采用 com.netflix.loadbalancer.ZoneAwareLoadBalancer 实现,他具备了区域感知的能力

这些自动化配置内容是在没有引入
Spring
Cloud
Eureka
等服务治理框架时如此,在同时引入
Eureka

Ribbon
依赖时,自动化配置会有一些不同。通过自动化配置的实现,我们可以轻松的实现客户端的负载均衡,同时,针对一些个性化的需求,我们也可以方便的替换上面的这些默认实现,示例代码:

@Configurable

public class RibbonConfiguration {

?
?

????????@Bean

????????public IRule ribbonRule(IClientConfig clientConfig) {

????????????????return new
WeightedResponseTimeRule();

????????}

}

可以使用 @RibbonClient
注解来实现更细粒度的客户端配置,创建 ConsumerHelloserviceApplication 主类,使用 @RibbonClient 注解来指定服务使用 WebServiceRibbonConfiguration 配置,代码如下:

@RibbonClient (name = "ORG.DRSOFT.WEBSERVICE.HELLOSERVICE", configuration = RibbonConfiguration.class)

@EnableDiscoveryClient

@SpringBootApplication

public class ConsumerHelloserviceApplication {

?
?

????????public static
void
main(String[] args) {

????????????????SpringApplication.run(ConsumerHelloserviceApplication.class, args);

????????}

?
?

????????@LoadBalanced

????????@Bean

????????public RestTemplate createRestTemplate() {

????????????????return new
RestTemplate();

????????}

}

注意:如果需要使服务单独配置,那么配置类不能放在@ComponentScan所扫描的当前包下以及子包下,否则我们自定义的这个配置类就会被所有的Ribbon客户端所共享,@SpringBootApplication 注解中就包含了 @ComponentScan 注解,因此必须使配置类处于不同的包以及子包。

如果需要设置默认配置或者多个@RibbonClient
注解,可以使用 @RibbonClients 注解,其 defaultConfiguration
可以设置默认的自定义配置,配置类不限定所属包,value
可以设置多个 @RibbonClient 注解,示例代码如下:

@RibbonClients (defaultConfiguration = RibbonConfiguration.class)

@EnableDiscoveryClient

@SpringBootApplication

public class ConsumerHelloserviceApplication {

?
?

????????public static
void
main(String[] args) {

????????????????SpringApplication.run(ConsumerHelloserviceApplication.class, args);

????????}

?
?

????????@Bean

????????@LoadBalanced

????????public RestTemplate restTemplate() {

????????????????return new
RestTemplate();

????????}

}


Eureka
结合

当在
Spring
Cloud
的应用中同时应用
Spring
Cloud Ribbon 和 Spring Cloud Eureka 依赖时,会触发 Eureka 中实现的对 Ribbon 的自动化配置,接口实现如下:

  • ServerList<Server>:服务实例清单的维护机制,默认采用 com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList实现,该实现会将服务清单列表交给 Eureka 的服务治理机制来进行维护
  • IPing:Ribbon 的实例检查策略,默认采用 com.netflix.niws.loadbalancer.NIWSDiscoveryPing 实现,该实现将实例检查的任务交给了服务治理框架来进行维护

负载均衡策略

IRule
接口其Ribbon
实现了非常多的选择策略,下面我们来详细说明一下IRule 接口的定义如下:

public interface IRule{

????????public Server choose(Object key);??

????????public
void
setLoadBalancer(ILoadBalancer lb);

????????public ILoadBalancer getLoadBalancer();

}

IRule
接口的具体实现:

  • AbstractLoadBalancerRule:负载均衡策略的抽象类,在该抽象类中定义了负载均衡器 ILoadBalancer 对象,该对象能够根据在具体实现选择服务策略时,获取到一些负载均衡器中维护的信息来作为分配依据,并以此设计一些算法来实习针对特定场景的高效策略
  • RandomRule:该策略实现了从服务实例清单中随机选择一个服务实例的功能,查看源码可以看到 choose 函数实现,使用 Random类的 nextInt 函数来随机获取一个随机数,并将随机数作为座位 upList 的索引值来返回具体实例

    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) {

    ????????????????????????????????return null;

    ????????????????????????}

    ????????????????????????int index = rand.nextInt(serverCount);

    ????????????????????????server = upList.get(index);

    ????????????????????????if (server == null) {

    ????????????????????????????????Thread.yield();

    ????????????????????????????????continue;

    ????????????????????????}

    ????????????????????????if (server.isAlive()) {

    ????????????????????????????????return (server);

    ????????????????????????}

    ????????????????????????server = null;

    ????????????????????????Thread.yield();

    ????????????????}

    ????????????????return server;

    ????????}

  • RoundRobinRule:该策略实现了按照线性轮询的方式依次选择每个服务实例的功能,其详细结构和
    RandomRule类似,具体实现如下:

    public Server choose(ILoadBalancer lb, Object key) {

    ????????????????if (lb == null) {

    ????????????????????????log.warn("no load balancer");

    ????????????????????????return null;

    ????????????????}

    ????????????????Server server = null;

    ????????????????int count = 0;

    ????????????????while (server == null && count++ < 10) {

    ????????????????????????List<Server> reachableServers = lb.getReachableServers();

    ????????????????????????List<Server> allServers = lb.getAllServers();

    ????????????????????????int upCount = reachableServers.size();

    ????????????????????????int serverCount = allServers.size();

    ????????????????????????if ((upCount == 0) || (serverCount == 0)) {

    ????????????????????????????????log.warn("No up servers available from load balancer: " + lb);

    ????????????????????????????????return null;

    ????????????????????????}

    ????????????????????????int nextServerIndex = incrementAndGetModulo(serverCount);

    ????????????????????????server = allServers.get(nextServerIndex);

    ????????????????????????if (server == null) {

    ????????????????????????????????/* Transient. */

    ????????????????????????????????Thread.yield();

    ????????????????????????????????continue;

    ????????????????????????}

    ????????????????????????if (server.isAlive() && (server.isReadyToServe())) {

    ????????????????????????????????return (server);

    ????????????????????????}

    ????????????????????????// Next.

    ????????????????????????server = null;

    ????????????????}

    ????????????????if (count >= 10) {

    ????????????????????????log.warn("No available alive servers after 10 tries from load balancer: "?+ lb);

    ????????????????}

    ????????????????return server;

    ????????}

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

    • 定时任务:在初始化的时候会通过 serverWeightTimer.schedule(new DynamicServerWeightTask(), 0, serverWeightTaskTimerInterval); 启动一个定时任务,用来为每个服务实例计算权重,该任务默认30秒执行一次。
    • 权重计算:维护实例的权重计算过程通过
      maintainWeights 函数实现,具体代码如下:

      public
      void
      maintainWeights() {

    ????????????????????????ILoadBalancer lb = getLoadBalancer();

    ????????????????????????if (lb == null) {

    ????????????????????????????????return;

    ????????????????????????}

    ????????????????????????if (!serverWeightAssignmentInProgress.compareAndSet(false, true)) {

    ????????????????????????????????return;

    ????????????????????????}

    ????????????????????????try {

    ????????????????????????????????logger.info("Weight adjusting job started");

    ????????????????????????????????AbstractLoadBalancer nlb = (AbstractLoadBalancer) lb;

    ????????????????????????????????LoadBalancerStats stats = nlb.getLoadBalancerStats();

    ????????????????????????????????if (stats == null) {

    ????????????????????????????????????????return;

    ????????????????????????????????}

    ????????????????????????????????double totalResponseTime = 0;

    ????????????????????????????????for (Server server : nlb.getAllServers()) {

    ????????????????????????????????????????ServerStats ss = stats.getSingleServerStat(server);

    ????????????????????????????????????????totalResponseTime += ss.getResponseTimeAvg();

    ????????????????????????????????}

    ????????????????????????????????Double weightSoFar = 0.0;

    ????????????????????????????????List<Double> finalWeights = new ArrayList<Double>();

    ????????????????????????????????for (Server server : nlb.getAllServers()) {

    ????????????????????????????????????????ServerStats ss = stats.getSingleServerStat(server);

    ????????????????????????????????????????double weight = totalResponseTime - ss.getResponseTimeAvg();

    ????????????????????????????????????????weightSoFar += weight;

    ????????????????????????????????????????finalWeights.add(weightSoFar);

    ????????????????????????????????}

    ????????????????????????????????setWeights(finalWeights);

    ????????????????????????} catch (Exception e) {

    ????????????????????????????????logger.error("Error calculating server weights", e);

    ????????????????????????} finally {

    ????????????????????????????????serverWeightAssignmentInProgress.set(false);

    ????????????????????????}

    ????????????????}

    计算规则为
    weightSoFar + totalResponseTime - 实例的平均响应时间,其中
    weightSoFar
    初始化为零,并且每计算好一个权重值需要累加到
    weightSoFar
    上供下一次计算使用,假设有4个实例A、B、C、D,他们的平均响应时间为10、40、80、100,所以总响应时间是 230,每个实例的权重为总响应时间与实例自身的平均响应时间的差的累积所得,所以A、B、C、D的权重分别如下所示:

    • 实例A:230-10=220
    • 实例B:220+(230-40)=410
    • 实例C:410+(230-80)=560
    • 实例D:560+(230-100)=690

    需要注意的是,这里的权重值只表示各实例权重区间的上限,每个实例的权重区间如下:

    • 实例A:[0,220]
    • 实例B:[220,410]
    • 实例C:[410,560]
    • 实例D:[560,690]

    权重区间的宽度越大,而权重区间的宽度越大,被选中的概率越高

    • 实例选择:生成一个[0,最大权重值]区间内的随机数,然后遍历权重列表的索引值去服务实例列表中获取具体的实例。
  • ClientConfigEnabledRoundRobinRule:该策略比较特殊,他本身并没有实现什么特殊的处理策略逻辑,在内部定义了一个RoundRobinRule 策略,我们不会直接使用该策略,但可以通过继承该策略,默认的choose实现了线性轮询策略,在子类中做一些高级策略时通常有可能存在一些无法实施的情况,那么就可以用父类的实现作为备选,后面的高级策略都是基于该类的扩展
  • BestAvailableRule:该策略继承自
    ClientConfigEnabledRoundRobinRule
    ,在实现中他注入了负载均衡器的统计对象
    LoadBalancerStats,同时在具体的
    choose
    方法中利用
    LoadBalancerStats
    保存的实例统计信息来选择满足要求的实例,通过负载均衡器中维护的所有服务实例,会过滤掉故障的实例,并找出并发请求最小的一个,所以该策略的特性是可选出最空闲的实例,代码如下:

    public Server choose(Object key) {

    ????????????????if (loadBalancerStats == null) {

    ????????????????????????return super.choose(key);

    ????????????????}

    ????????????????List<Server> serverList = getLoadBalancer().getAllServers();

    ????????????????int minimalConcurrentConnections = Integer.MAX_VALUE;

    ????????????????long currentTime = System.currentTimeMillis();

    ????????????????Server chosen = null;

    ????????????????for (Server server: serverList) {

    ????????????????????????ServerStats serverStats = loadBalancerStats.getSingleServerStat(server);

    ????????????????????????if (!serverStats.isCircuitBreakerTripped(currentTime)) {

    ????????????????????????????????int concurrentConnections = serverStats.getActiveRequestsCount(currentTime);

    ????????????????????????????????if (concurrentConnections < minimalConcurrentConnections) {

    ????????????????????????????????????????minimalConcurrentConnections = concurrentConnections;

    ????????????????????????????????????????chosen = server;

    ????????????????????????????????}

    ????????????????????????}

    ????????????????}

    ????????????????if (chosen == null) {

    ????????????????????????return super.choose(key);

    ????????????????} else {

    ????????????????????????return chosen;

    ????????????????}

    ????????}

    ?
    ?

    ?
    ?

?
?

时间: 2024-11-05 11:57:14

笔记:Spring Cloud Ribbon 客户端配置详解的相关文章

spring声明式事务配置详解

spring声明式事务配置详解 君子不器 2013年06月16日 编程世界 5273次阅读 查看评论 理解Spring的声明式事务管理实现 本节的目的是消除与使用声明式事务管理有关的神秘性.简单点儿总是好的,这份参考文档只是告诉你给你的类加上@Transactional注解,在配置文件中添加('<tx:annotation-driven/>')行,然后期望你理解整个过程是怎么工作的.此节讲述Spring的声明式事务管理内部的工作机制,以帮助你在面对事务相关的问题时不至于误入迷途,回朔到上游平静

笔记:Spring Cloud Ribbon 客户端负载均衡

Spring Cloud Ribbon 是一个基于 HTTP 和 TCP 的客户端负载均衡工具,基于 Netflix Ribbon 实现,通过Spring Cloud 的封装,可以让我们轻松的将面向服务的REST 模板请求自动转换为客户端负载均衡的服务调用.客户端负载均衡在系统架构中是一个非常重要的,并且是不得不去实施的内容,因为负载均衡是对系统的高可用.网络压力的缓解和处理能力扩容的重要手段,客户端负载均衡需要通过心跳去维护服务端清单的健康性,这个需要服务注册中心配合完成,在Spring Cl

(转)Spring boot——logback.xml 配置详解(三)&lt;appender&gt;

文章转载自:http://aub.iteye.com/blog/1101260,在此对作者的辛苦表示感谢! logback 常用配置详解(二) <appender> <appender>: <appender>是<configuration>的子节点,是负责写日志的组件. <appender>有两个必要属性name和class.name指定appender名称,class指定appender的全限定名. 1.ConsoleAppender: 把日

跟我学Spring Cloud(Finchley版)-20-Spring Cloud Config-Git仓库配置详解 原

在跟我学Spring Cloud(Finchley版)-19-配置中心-Spring Cloud Config 一节中,已实现使用Git仓库作为Config Server的后端存储,本节详细探讨如何配置Git仓库. 一.占位符支持 Config Server的占位符支持{application}.{profile}和{label}. 示例: server: port: 8080 spring: application: name: microservice-config-server cloud

CAS 与 Spring Security 3整合配置详解

一般来说,Web 应用的安全性包括用户认证(Authentication)和用户授权(Authorization)两个部分.用户认证指的是验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统.用户授权指的是验证某个用户是否有权限执行某个操作.在一个系统中,不同用户所具有的权限是不同的.比如对一个文件来说,有的用户只能进行读取,而有的用户可以进行修改.一般来说,系统会为不同的用户分配不同的角色,而每个角色则对应一系列的权限. 对于上面提到的两种应用情景,Spring Security 框

Spring MVC原理及配置详解

Spring MVC原理及配置 1.Spring MVC概述: Spring MVC是Spring提供的一个强大而灵活的web框架.借助于注解,Spring MVC提供了几乎是POJO的开发模式,使得控制器的开发和测试更加简单.这些控制器一般不直接处理请求,而是将其委托给Spring上下文中的其他bean,通过Spring的依赖注入功能,这些bean被注入到控制器中. Spring MVC主要由DispatcherServlet.处理器映射.处理器(控制器).视图解析器.视图组成.他的两个核心是

Spring框架笔记(四)——Spring容器的属性配置详解的六个专题

在spring IOC容器的配置文件applicationContext.xml里,有一些配置细节值得一提.我们将一些问题归结为以下几个专题. 专题一:字面值问题 配置的bean节点中的值,我们提出一个概念--字面值. 字面值:可用字符串表示的值. 字面值可以通过 <value> 元素标签或 value 属性进行注入. 基本数据类型及其封装类.String 等类型都可以采取字面值注入的方式 若字面值中包含特殊字符,可以使用 <![CDATA[]]> 把字面值包裹起来. 例如:(本文

Spring Cloud Ribbon——客户端负载均衡

一.负载均衡负载均衡(Load Balance): 建立在现有网络结构之上,它提供了一种廉价有效透明的方法扩展网络设备和服务器的带宽.增加吞吐量.加强网络数据处理能力.提高网络的灵活性和可用性.其意思就是分摊到多个操作单元上进行执行,例如Web服务器.FTP服务器.企业关键应用服务器和其它关键任务服务器等,从而共同完成工作任务. 1.服务端负载均衡:客户端请求到负载均衡服务器,负载均衡服务器根据自身的算法将该请求转给某台真正提供业务的服务器,该服务器将响应数据给负载均衡服务器,负载均衡服务器最后

【Spring】Spring MVC原理及配置详解

1.Spring MVC概述: Spring MVC是Spring提供的一个强大而灵活的web框架.借助于注解,Spring MVC提供了几乎是POJO的开发模式,使得控制器的开发和测试更加简单.这些控制器一般不直接处理请求,而是将其委托给Spring上下文中的其他bean,通过Spring的依赖注入功能,这些bean被注入到控制器中. Spring MVC主要由DispatcherServlet.处理器映射.处理器(控制器).视图解析器.视图组成.他的两个核心是两个核心: 处理器映射:选择使用