Spring-Cloud-Ribbon学习笔记(二):自定义负载均衡规则

时间:2022-09-10 22:10:02

Ribbon自定义负载均衡策略有两种方式,一是JavaConfig,一是通过配置文件(yml或properties文件)。

需求

假设我有包含A和B服务在内的多个微服务,它们均注册在一个Eureka上,信息如下:

Spring-Cloud-Ribbon学习笔记(二):自定义负载均衡规则

我希望当访问服务A时候,2个服务(端口分别是8087和8081)每两次一换,比如访问两次8087,再访问两次8081,如此反复。

当访问服务B时,与A类似,不过是3次一换。

当访问其他服务时,采用随机规则,即RandomRule,而不是默认策略[1]

JavaConfig

使用这种方式,总共分3步(以服务A的规则为例):

  • 新建针对服务A的负载均衡规则类SvcARule,实现抽象类AbstractLoadBalancerRule并重写方法,在类上只需要添加注解@Primary[2],重写逻辑须要根据实际需求来定,详见下面的代码
  • 新建配置类LBConfig,添加类型为IRule的Bean,此处我们选RandomRule
  • 在配置类LBConfig上,通过注解@RibbonClients(只有一种自定义规则,则使用@RibbonClient),添加不适用公共规则的其他自定义规则

实现

SvcARule

import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import org.springframework.context.annotation.Primary; import java.util.List;
import java.util.concurrent.atomic.AtomicInteger; @Primary
public class SvcARule extends AbstractLoadBalancerRule { private AtomicInteger total = new AtomicInteger();
private AtomicInteger index = new AtomicInteger(); 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;
}
if (total.get() < 2) {
server = upList.get(index.get());
total.incrementAndGet();
} else {
total.set(0);
index.set(index.incrementAndGet() % upList.size());
} if (server == null) {
Thread.yield();
continue;
} if (server.isAlive()) {
return (server);
} server = null;
Thread.yield();
}
return server; } @Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
} @Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
// TODO Auto-generated method stub
}
}

SvcBRule

略,只需要把if (total.get() < 2)改成if (total.get() < 3)即可

LBConfig

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import com.yan.ribbon.rule.SvcARule;
import com.yan.ribbon.rule.SvcBRule;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.netflix.ribbon.RibbonClients;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; @Configuration
@RibbonClients(
value = {
@RibbonClient(name = "SVCA", configuration = SvcARule.class),
@RibbonClient(name = "SVCB", configuration = SvcBRule.class)
},
defaultConfiguration = LBConfig.class)
public class LBConfig {
@Bean
public IRule commonRule() {
return new RandomRule();
}
}

测试

通过postman多次访问服务A、B和C,得到日志如下:

2019-03-19 20:22:09.511  INFO 13372 --- [nio-8004-exec-4] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2019-03-19 20:22:09.511 INFO 13372 --- [nio-8004-exec-4] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2019-03-19 20:22:09.519 INFO 13372 --- [nio-8004-exec-4] o.s.web.servlet.DispatcherServlet : Completed initialization in 8 ms
2019-03-19 20:22:09.693 INFO 13372 --- [nio-8004-exec-4] c.n.u.concurrent.ShutdownEnabledTimer : Shutdown hook installed for: NFLoadBalancer-PingTimer-SVCA
2019-03-19 20:22:09.708 INFO 13372 --- [nio-8004-exec-4] c.netflix.loadbalancer.BaseLoadBalancer : Client: SVCA instantiated a LoadBalancer: DynamicServerListLoadBalancer:{NFLoadBalancer:name=SVCA,current list of Servers=[],Load balancer stats=Zone stats: {},Server stats: []}ServerList:null
2019-03-19 20:22:09.712 INFO 13372 --- [nio-8004-exec-4] c.n.l.DynamicServerListLoadBalancer : Using serverListUpdater PollingServerListUpdater
2019-03-19 20:22:09.729 INFO 13372 --- [nio-8004-exec-4] c.n.l.DynamicServerListLoadBalancer : DynamicServerListLoadBalancer for client SVCA initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=SVCA,current list of Servers=[YanWei:8087, YanWei:8081],Load balancer stats=Zone stats: {defaultzone=[Zone:defaultzone; Instance count:2; Active connections count: 0; Circuit breaker tripped count: 0; Active connections per server: 0.0;]
},Server stats: [[Server:YanWei:8087; Zone:defaultZone; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 08:00:00 CST 1970; First connection made: Thu Jan 01 08:00:00 CST 1970; Active Connections:0; total failure count in last (1000) msecs:0; average resp time:0.0; 90 percentile resp time:0.0; 95 percentile resp time:0.0; min resp time:0.0; max resp time:0.0; stddev resp time:0.0]
, [Server:YanWei:8081; Zone:defaultZone; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 08:00:00 CST 1970; First connection made: Thu Jan 01 08:00:00 CST 1970; Active Connections:0; total failure count in last (1000) msecs:0; average resp time:0.0; 90 percentile resp time:0.0; 95 percentile resp time:0.0; min resp time:0.0; max resp time:0.0; stddev resp time:0.0]
]}ServerList:org.springframework.cloud.netflix.ribbon.eureka.DomainExtractingServerList@7f0246c6
"svcA:8087===>\nname:xixi\ntype:haha"
"svcA:8087===>\nname:xixi\ntype:haha"
"svcA:8081===>\nname:xixi\ntype:haha"
"svcA:8081===>\nname:xixi\ntype:haha"
"svcA:8087===>\nname:xixi\ntype:haha"
"svcA:8087===>\nname:xixi\ntype:haha"
"svcA:8081===>\nname:xixi\ntype:haha"
"svcA:8081===>\nname:xixi\ntype:haha"
"svcA:8087===>\nname:xixi\ntype:haha"
"svcA:8087===>\nname:xixi\ntype:haha"
"svcA:8081===>\nname:xixi\ntype:haha"
"svcA:8081===>\nname:xixi\ntype:haha"
2019-03-19 20:22:37.173 INFO 13372 --- [nio-8004-exec-5] c.n.u.concurrent.ShutdownEnabledTimer : Shutdown hook installed for: NFLoadBalancer-PingTimer-SVCB
2019-03-19 20:22:37.174 INFO 13372 --- [nio-8004-exec-5] c.netflix.loadbalancer.BaseLoadBalancer : Client: SVCB instantiated a LoadBalancer: DynamicServerListLoadBalancer:{NFLoadBalancer:name=SVCB,current list of Servers=[],Load balancer stats=Zone stats: {},Server stats: []}ServerList:null
2019-03-19 20:22:37.177 INFO 13372 --- [nio-8004-exec-5] c.n.l.DynamicServerListLoadBalancer : Using serverListUpdater PollingServerListUpdater
2019-03-19 20:22:37.178 INFO 13372 --- [nio-8004-exec-5] c.n.l.DynamicServerListLoadBalancer : DynamicServerListLoadBalancer for client SVCB initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=SVCB,current list of Servers=[YanWei:8086, YanWei:8082],Load balancer stats=Zone stats: {defaultzone=[Zone:defaultzone; Instance count:2; Active connections count: 0; Circuit breaker tripped count: 0; Active connections per server: 0.0;]
},Server stats: [[Server:YanWei:8082; Zone:defaultZone; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 08:00:00 CST 1970; First connection made: Thu Jan 01 08:00:00 CST 1970; Active Connections:0; total failure count in last (1000) msecs:0; average resp time:0.0; 90 percentile resp time:0.0; 95 percentile resp time:0.0; min resp time:0.0; max resp time:0.0; stddev resp time:0.0]
, [Server:YanWei:8086; Zone:defaultZone; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 08:00:00 CST 1970; First connection made: Thu Jan 01 08:00:00 CST 1970; Active Connections:0; total failure count in last (1000) msecs:0; average resp time:0.0; 90 percentile resp time:0.0; 95 percentile resp time:0.0; min resp time:0.0; max resp time:0.0; stddev resp time:0.0]
]}ServerList:org.springframework.cloud.netflix.ribbon.eureka.DomainExtractingServerList@485ed210
"svcB:8086===>\nname:xixi\ntype:haha"
"svcB:8086===>\nname:xixi\ntype:haha"
"svcB:8086===>\nname:xixi\ntype:haha"
"svcB:8082===>\nname:xixi\ntype:haha"
"svcB:8082===>\nname:xixi\ntype:haha"
"svcB:8082===>\nname:xixi\ntype:haha"
"svcB:8086===>\nname:xixi\ntype:haha"
"svcB:8086===>\nname:xixi\ntype:haha"
"svcB:8086===>\nname:xixi\ntype:haha"
"svcB:8082===>\nname:xixi\ntype:haha"
"svcB:8082===>\nname:xixi\ntype:haha"
"svcB:8082===>\nname:xixi\ntype:haha"
"svcB:8086===>\nname:xixi\ntype:haha"
"svcB:8086===>\nname:xixi\ntype:haha"
2019-03-19 20:23:01.319 INFO 13372 --- [nio-8004-exec-8] c.n.u.concurrent.ShutdownEnabledTimer : Shutdown hook installed for: NFLoadBalancer-PingTimer-SVCC
2019-03-19 20:23:01.320 INFO 13372 --- [nio-8004-exec-8] c.netflix.loadbalancer.BaseLoadBalancer : Client: SVCC instantiated a LoadBalancer: DynamicServerListLoadBalancer:{NFLoadBalancer:name=SVCC,current list of Servers=[],Load balancer stats=Zone stats: {},Server stats: []}ServerList:null
2019-03-19 20:23:01.320 INFO 13372 --- [nio-8004-exec-8] c.n.l.DynamicServerListLoadBalancer : Using serverListUpdater PollingServerListUpdater
2019-03-19 20:23:01.322 INFO 13372 --- [nio-8004-exec-8] c.n.l.DynamicServerListLoadBalancer : DynamicServerListLoadBalancer for client SVCC initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=SVCC,current list of Servers=[YanWei:8083, YanWei:8085],Load balancer stats=Zone stats: {defaultzone=[Zone:defaultzone; Instance count:2; Active connections count: 0; Circuit breaker tripped count: 0; Active connections per server: 0.0;]
},Server stats: [[Server:YanWei:8083; Zone:defaultZone; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 08:00:00 CST 1970; First connection made: Thu Jan 01 08:00:00 CST 1970; Active Connections:0; total failure count in last (1000) msecs:0; average resp time:0.0; 90 percentile resp time:0.0; 95 percentile resp time:0.0; min resp time:0.0; max resp time:0.0; stddev resp time:0.0]
, [Server:YanWei:8085; Zone:defaultZone; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 08:00:00 CST 1970; First connection made: Thu Jan 01 08:00:00 CST 1970; Active Connections:0; total failure count in last (1000) msecs:0; average resp time:0.0; 90 percentile resp time:0.0; 95 percentile resp time:0.0; min resp time:0.0; max resp time:0.0; stddev resp time:0.0]
]}ServerList:org.springframework.cloud.netflix.ribbon.eureka.DomainExtractingServerList@77b35c9
"svcC:8085===>\nname:xixi\ntype:haha"
"svcC:8083===>\nname:xixi\ntype:haha"
"svcC:8085===>\nname:xixi\ntype:haha"
"svcC:8085===>\nname:xixi\ntype:haha"
"svcC:8083===>\nname:xixi\ntype:haha"
"svcC:8085===>\nname:xixi\ntype:haha"
"svcC:8085===>\nname:xixi\ntype:haha"

配置文件方式

配置文件方式须要遵守以下格式:

<serviceName>.ribbon.<key>=<value>

几个变量解释如下:

serviceName就是服务名

key是指Ribbon为实现不同领域(我们这里只说IRule,其实还有IPing等)所指定的关联类名,这里我需要自定义IRule,其关联类名,也就是所谓的key,是NFLoadBalancerRuleClassName[3]

value是我所要指定的自定义规则,比如,SvcB的负载均衡策略SvcBRule

因此,在配置文件中添加如下配置:

svcB:
ribbon:
NFLoadBalancerRuleClassName: com.yan.ribbon.rule.SvcBRule

重新启动Ribbon服务后,发现此配置并没有生效,原因有两个:

一是我的服务名字写的是svcB,理由是我的配置spring.appliaction.name=svcB,但是使用Ribbon调用时使用了http://SVCB/...,SVCB是大写的,所以没有得到期望的效果[4]

二是我已经有了用来替换默认规则的LBConfig,使得IRule的默认配置没有生效,因为查看源码org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration中对于IRule的定义如下:

	@Bean
@ConditionalOnMissingBean
public IRule ribbonRule(IClientConfig config) {
if (this.propertiesFactory.isSet(IRule.class, name)) {
return this.propertiesFactory.get(IRule.class, config, name);
}
ZoneAvoidanceRule rule = new ZoneAvoidanceRule();
rule.initWithNiwsConfig(config);
return rule;
}

由于我自定义的commonRule已经存在了,所以此处不会触发,更不会走到判断那一步,就是说,我的上面的关于SvcB的自定义规则的配置,是没有什么地方去读的,因此,为了方便演示,直接将LBConfig全部注掉,再次重启后,把访问Uri中的大写改成一致的svcB,发现对于服务B的配置生效了,但是没有指定的服务,全部都采用了默认的策略ZoneAvoidanceRule

总结

配置文件方式,比较麻烦,且不能-没必要也懒得研究如何-修改默认规则,而JavaConfig可以完美替代,因此我选JavaConfig。

进阶

关于Ribbon中对于IRule的实现,总共有如下几种,其简略说明也附注如下:

  • RoundRobinRule: 轮询
  • RandomRule:随机
  • AvailabilityFilteringRule:会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,还有并发的连接数超过阈值的服务,然后对剩余的服务列表按照轮询策略进行访问
  • WeightedResponseTimeRule:根据平均响应时间计算所有服务的权重,响应时间越快服务权重越大,被选中的概率越高。刚启动时如果统计信息不足,则使用RoundRobinRule策略,等统计信息足够,会切换到WeightedResponseTimeRule
  • RetryRule: 先按照RoundRobinRule的策略获取服务,如果获取服务失败,则在指定时间内会进行重试,获取可用服务
  • BestAvailableRule: 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
  • ZoneAvoidanceRule: 默认规则,符合判断server所在区域的性能和server的可用性选择服务器

  1. 对应类为ZoneAvoidanceRule,直译过来是区域回避规则,我也不知道为啥这样叫:D ↩︎

  2. 如果不加此注解,会报错,内容是找类型为IRule的Bean,但是找到了俩,一个commonRule,一个svcARule。如果你不嫌麻烦,可以选择网上流行的较为复杂的形式,比如springcloud-04-自定义ribbon的配置方式 ↩︎

  3. 可以在类org.springframework.cloud.netflix.ribbon.PropertiesFactory中查看 ↩︎

  4. 这个Eureka也要背锅,因为使用服务名来进行服务间调用,是被大小写是被兼容过的,导致我这边疏忽大意了,还是要细心的好。 ↩︎

Spring-Cloud-Ribbon学习笔记(二):自定义负载均衡规则的更多相关文章

  1. 《Spring Cloud》学习(二) 负载均衡!

    第二章 负载均衡 负载均衡是对系统的高可用.网络压力的缓解和处理能力扩容的重要手段之一.Spring Cloud Ribbon是一个基于 HTTP 和 TCP 的客户端负载均衡工具,它基于Netfli ...

  2. 基于Spring cloud Ribbon和Eureka实现客户端负载均衡

    前言 本案例将基于Spring cloud Ribbon和Eureka实现客户端负载均衡,其中Ribbon用于实现客户端负载均衡,Eureka主要是用于服务注册及发现: 传统的服务端负载均衡 常见的服 ...

  3. Spring Cloud Alibaba学习笔记(7) - Sentinel规则持久化及生产环境使用

    Sentinel 控制台 需要具备下面几个特性: 规则管理及推送,集中管理和推送规则.sentinel-core 提供 API 和扩展接口来接收信息.开发者需要根据自己的环境,选取一个可靠的推送规则方 ...

  4. spring cloud(学习笔记)高可用注册中心(Eureka)的实现(二)

    绪论 前几天我用一种方式实现了spring cloud的高可用,达到两个注册中心,详情见spring cloud(学习笔记)高可用注册中心(Eureka)的实现(一),今天我意外发现,注册中心可以无限 ...

  5. 【Ribbon篇四】自定义负载均衡策略(4)

    官方文档特别指出:自定义的负载均衡配置类不能放在 @componentScan 所扫描的当前包下及其子包下,否则我们自定义的这个配置类就会被所有的Ribbon客户端所共享,也就是说我们达不到特殊化定制 ...

  6. Spring Cloud Alibaba学习笔记(3) - Ribbon

    1.手写一个客户端负载均衡器 在了解什么是Ribbon之前,首先通过代码的方式手写一个负载均衡器 RestTemplate restTemplate = new RestTemplate(); // ...

  7. Spring Cloud Alibaba学习笔记(4) - Feign配置与使用

    什么是Feign Feign是一个声明式Web Service客户端. 使用Feign能让编写Web Service客户端更加简单, 它的使用方法是定义一个接口,然后在上面添加注解,同时也支持JAX- ...

  8. Spring Cloud Alibaba学习笔记(1) - 整合Spring Cloud Alibaba

    Spring Cloud Alibaba从孵化器版本毕业:https://github.com/alibaba/spring-cloud-alibaba,记录一下自己学习Spring Cloud Al ...

  9. Spring Cloud Alibaba学习笔记(21) - Spring Cloud Gateway 自定义全局过滤器

    在前文中,我们介绍了Spring Cloud Gateway内置了一系列的全局过滤器,本文介绍如何自定义全局过滤器. 自定义全局过滤需要实现GlobalFilter 接口,该接口和 GatewayFi ...

随机推荐

  1. CSS布局——居中

    参考文章1.CSS布局奇淫技巧之--各种居中 2.http://www.imooc.com/article/2235 1.行内元素水平居中text-align:center对图片,按钮,文字等行内元素 ...

  2. n人比赛,可轮空,比赛轮数和场数

    #include<stdio.h> int chang(int x,int s){ ) return s; ) ; !=){ s+=(x-)/; )/,s); } else{ s+=x/; ...

  3. HDU 1710-Binary Tree Traversals(二进制重建)

    Binary Tree Traversals Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/O ...

  4. visual studio 2012 链接Mysql 5&period;1

    首先在nuGet 下载MySql.Data.Entity 安装 mysql for visual studio http://www.mysql.com/why-mysql/windows/visua ...

  5. remove方法

    1.jQuery的remove()方法 http://www.365mini.com/page/jquery-remove.htm ①返回值是jquery对象本身 所以可以做删除再添加的操作 // 移 ...

  6. Ambari安装HDP问题:User root is not allowed to impersonate anonymous&period;User&colon; hcat is not allowed to impersonate ambari-qa

    User root is not allowed to impersonate anonymous 修改hadoop 配置文件 etc/hadoop/core-site.xml,加入如下配置项 &lt ...

  7. Docker 命令查询

    Docker — 从入门到实践 Docker 命令查询 基本语法 Docker 命令有两大类,客户端命令和服务端命令.前者是主要的操作接口,后者用来启动 Docker Daemon. 客户端命令:基本 ...

  8. Blender 2&period;78 突然卡顿

    之前一直好好的直到这两天打开就一直延迟半拍,重启重装无效. 解决方法: NVIDIA控制面板→管理3d设置→程序设置→选择blender,更改显卡为集成显卡→应用 亲测有效.

  9. Tomcat的三种部署方式

    Tomcat是目前web开发中非常流行的Web 服务器,也就是tomcat在部署项目的时候,必须要把应用程序中所用到的jar包放到tomcat的lib目录下,然后再一起部署到服务器上. 那么tomca ...

  10. 收藏一下mybatis全局参数配置

    http://blog.csdn.net/shaoduo/article/details/54285981