Spring-Cloud-Ribbon学习笔记(二):自定义负载均衡规则
Ribbon自定义负载均衡策略有两种方式,一是JavaConfig,一是通过配置文件(yml或properties文件)。
需求
假设我有包含A和B服务在内的多个微服务,它们均注册在一个Eureka上,信息如下:
我希望当访问服务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的可用性选择服务器
对应类为ZoneAvoidanceRule,直译过来是区域回避规则,我也不知道为啥这样叫:D ↩︎
如果不加此注解,会报错,内容是找类型为IRule的Bean,但是找到了俩,一个commonRule,一个svcARule。如果你不嫌麻烦,可以选择网上流行的较为复杂的形式,比如springcloud-04-自定义ribbon的配置方式 ↩︎
可以在类
org.springframework.cloud.netflix.ribbon.PropertiesFactory
中查看 ↩︎这个Eureka也要背锅,因为使用服务名来进行服务间调用,是被大小写是被兼容过的,导致我这边疏忽大意了,还是要细心的好。 ↩︎
Spring-Cloud-Ribbon学习笔记(二):自定义负载均衡规则的更多相关文章
- 《Spring Cloud》学习(二) 负载均衡!
第二章 负载均衡 负载均衡是对系统的高可用.网络压力的缓解和处理能力扩容的重要手段之一.Spring Cloud Ribbon是一个基于 HTTP 和 TCP 的客户端负载均衡工具,它基于Netfli ...
- 基于Spring cloud Ribbon和Eureka实现客户端负载均衡
前言 本案例将基于Spring cloud Ribbon和Eureka实现客户端负载均衡,其中Ribbon用于实现客户端负载均衡,Eureka主要是用于服务注册及发现: 传统的服务端负载均衡 常见的服 ...
- Spring Cloud Ribbon 中的 7 种负载均衡策略
负载均衡通器常有两种实现手段,一种是服务端负载均衡器,另一种是客户端负载均衡器,而我们今天的主角 Ribbon 就属于后者--客户端负载均衡器. 服务端负载均衡器的问题是,它提供了更强的流量控制权,但 ...
- Spring Cloud Alibaba学习笔记(7) - Sentinel规则持久化及生产环境使用
Sentinel 控制台 需要具备下面几个特性: 规则管理及推送,集中管理和推送规则.sentinel-core 提供 API 和扩展接口来接收信息.开发者需要根据自己的环境,选取一个可靠的推送规则方 ...
- spring cloud(学习笔记)高可用注册中心(Eureka)的实现(二)
绪论 前几天我用一种方式实现了spring cloud的高可用,达到两个注册中心,详情见spring cloud(学习笔记)高可用注册中心(Eureka)的实现(一),今天我意外发现,注册中心可以无限 ...
- 【Ribbon篇四】自定义负载均衡策略(4)
官方文档特别指出:自定义的负载均衡配置类不能放在 @componentScan 所扫描的当前包下及其子包下,否则我们自定义的这个配置类就会被所有的Ribbon客户端所共享,也就是说我们达不到特殊化定制 ...
- Spring Cloud Alibaba学习笔记(3) - Ribbon
1.手写一个客户端负载均衡器 在了解什么是Ribbon之前,首先通过代码的方式手写一个负载均衡器 RestTemplate restTemplate = new RestTemplate(); // ...
- Spring Cloud Alibaba学习笔记(4) - Feign配置与使用
什么是Feign Feign是一个声明式Web Service客户端. 使用Feign能让编写Web Service客户端更加简单, 它的使用方法是定义一个接口,然后在上面添加注解,同时也支持JAX- ...
- Spring Cloud Alibaba学习笔记(1) - 整合Spring Cloud Alibaba
Spring Cloud Alibaba从孵化器版本毕业:https://github.com/alibaba/spring-cloud-alibaba,记录一下自己学习Spring Cloud Al ...
随机推荐
- 通过Quartz 配置定时调度任务:使用cron表达式配置时间点
Cron官网入口 在后台经常需要一些定时处理的任务,比如微信相关应用所需的access_token,就要定时刷新,官方返回的有效性是7200s,也就是2小时,但是为了保险起见,除了在发现access_ ...
- Yii2 DetailView小部件
DetailView小部件 Yii 提供了一套数据库小部件 widgets,这些小部件可以用于显示数据 DetailView 小部件用于显示一条记录数据 ListView 和 GridView 可以用 ...
- 3ds max学习笔记(十三)-- (锥化,扭曲,晶格)
锥化: 新建一个长方体,在修改器列表中选择--锥化: 参数同弯曲有些类似 参数里: 数量:控制上界面的缩放 曲线:橘黄色的线(但模型未发生变化,此时,可以回到物体身上,调整对应方向上的分段数) 组(有 ...
- 一步步完成Maven+SpringMVC+SpringFox+Swagger整合示例
本文给出一个整合Maven+SpringMVC+SpringFOX+Swagger的示例,并且一步步给出完成步骤. 本人在做实例时发现 http://blog.csdn.net/zth1002/art ...
- JS_高程5.引用类型(5)Array类型的操作方法
一.操作方法 1.concat()方法 基于当前数组中的所有项创建一个新数组.具体说,是先创建当前数组的一个副本,然后将接收到的参数添加到这个副本的末尾,最后返回新构建的数组.在没有给concat() ...
- Google C++ 单元测试 GTest
from : http://www.cnblogs.com/jycboy/p/6057677.html 一.设置一个新的测试项目 在用google test写测试项目之前,需要先编译gtest到lib ...
- CSS魔法堂:改变单选框颜色就这么吹毛求疵!
前言 是否曾经被业务提出"能改改这个单选框的颜色吧!让它和主题颜色搭配一下吧!",然后苦于原生不支持换颜色,最后被迫自己手撸一个凑合使用.若抛开input[type=radio] ...
- 关于cxf生成客户端代码中的JAXBElement<String>
1.使用自动生成的java文件中的 ObjectFactory构造入参 关于cxf生成客户端代码中的JAXBElement<String> 在使用cxf或者x-fire进行webse ...
- RabbitMQ 可靠投递
RabbitMQ 可靠投递 标签: RabbitMQ shovel-plugin ConfirmCallback RabbitMQ消息投递 背景 confirmCallback 确认模式 return ...
- Google Maps V3 之 路线服务
概述 您可以使用 DirectionsService 对象计算路线(使用各种交通方式).此对象与 Google Maps API 路线服务进行通信,该服务会接收路线请求并返回计算的结果.您可以自行处理 ...