Ribbon学习笔记(二):自定义负载均衡规则_ribbon 自定义规则-程序员宅基地

技术标签: springcloud  ribbon  Spring  

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

需求

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

我希望当访问服务A时候,2个服务(端口分别是8087和8081)每两次一换,比如访问两次8087,再访问两次8081,如此反复。
当访问服务B时,与A类似,不过是3次一换。
当访问其他服务时,采用随机规则,即RandomRule,而不是默认策略1

JavaConfig

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

  • 新建针对服务A的负载均衡规则类SvcARule,实现抽象类AbstractLoadBalancerRule并重写方法,在类上只需要添加注解@Primary2,重写逻辑须要根据实际需求来定,详见下面的代码
  • 新建配置类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,是NFLoadBalancerRuleClassName3
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也要背锅,因为使用服务名来进行服务间调用,是被大小写是被兼容过的,导致我这边疏忽大意了,还是要细心的好。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Young4Dream/article/details/95942590

智能推荐

Git高级使用方法_深度学习技术前沿的博客-程序员宅基地

关注上方“深度学习技术前沿”,选择“星标公众号”,资源干货,第一时间送达!作者 : Maxence Poutord原文:New Frontend 网站如果你觉得 git 很迷惑人,那..._git高级学习

Oracle OCP笔记(31)使用闪回功能_gyming的博客-程序员宅基地

Oracle OCP笔记(31)使用闪回功能 Oracle的闪回功能受到3个数据库结构之一的支持: 撤消数据、闪回恢复区和回收站。 撤消表空间中的撤销数据不仅支持事务回滚,也支持大多数闪回表操作。Flashback Data Archives允许查询先前版本的表行,它在撤销表空间之外的一个或多个表空间中提供一个区域,支持的保留期比撤销表空间还长。闪回日志保存在闪回恢复区

测试用例的几种常见设计方法_测试用例设计方法举例_我不饿~的博客-程序员宅基地

 测试用例常见的设计方法有:等价类划分法、边界值分析法、错误推测法、判定表法、正交实验法。  一.等价类划分法  顾名思义,等价类划分,就是将测试的范围划分成几个互不相交的子集,他们的并集是全集,从每个子集选出若干个有代表性的值作为测试用例。  例如,我们要测试一个用户名是否合法,用户名的定义为:8位数字组成的字符。  我们可以先划分子集:空用户名,1-7位数字,8位数字,9位或以上数字,非数字。..._测试用例设计方法举例

夜神模拟器报错cannot connect to daemon,daemon still not running_daemon started successfully * ** daemon still not -程序员宅基地

daemon not running. starting it now on port 5037 ** daemon started successfully *** daemon still not runningerror: cannot connect to daemon根本解决方法: 设置环境变量ANDROID_ADB_SERVER_PORT 值为不常用的值参考链接 : https://blog.csdn.net/a1809032425/article/details/906142._daemon started successfully * ** daemon still not running error: cannot conn

修改Github用户名_HHHJiaWei的博客-程序员宅基地

首先登录GitHub官网进入网站首页,点击右上角的头像,如下图所示在选项中找到Setting点击进入设置。在页面左方的列表中找到Account选项,点击后会在页面右方找到change username按钮,如下图所示点击chang username后会弹出提示点击提示下方的按钮即可。然后就可以修改用户名了。在输入框输入新的用户名,点击修改按钮即可成功修改用户名啦。...

如何在Windows Server中查询.Net Framework版本信息?_查看server .net版本_Kianteck的博客-程序员宅基地

我们经常会遇到在要搭建的系统环境上查询.Net Framework 的版本号信息,本文整理出来两种查询方法和详细的版本号比对表供大家参考使用。_查看server .net版本

随便推点

php数据库访问辅助类,php+MySQL实战案例【二】php数据库辅助类_Helios-Yang的博客-程序员宅基地

前言在学习php的时候需要经常对mysql数据库进行增删改查操作,为了减少冗余代码,我们把数据操作的方法封装成一个php类。在不同的业务场景需要用到数据库表的数据操作时,只需在php文件开头引入我们的数据库辅助类,调用不用的数据操作方法,传相应的参数即可实现数据的增删改查。我们封装的数据库辅助类,主要有以下几个关键方法:02GetTotal用途: 获取当前查询SQL的总记录数。参数:查询sql字符..._php7.4 mysql 辅助类

深度学习-Tensorflow2与PaddlePaddle入门写法对比(二):专家入门_大Huoyo的博客-程序员宅基地

引言前面写了一篇Tensorflow2与PaddlePaddle小白入门级写法对比的今天来看一下所谓的专家入门写法对比(这里的专家不是本人说的,出自Tensorflow官网,求生欲满满:https://tensorflow.google.cn/overview?hl=en)数据加载器Tensorflow2import tensorflow as tf# 加载数据集(train_images, train_labels), (test_images, test_labels) = \

Java多线程----线程的同步,锁和死锁,问题以及解决方法(例子说明)_Fuzz_的博客-程序员宅基地

一、线程并发同步概念线程同步其核心就在于一个“同”。所谓“同”就是协同、协助、配合,“同步”就是协同步调昨,也就是按照预定的先后顺序进行运行,即“你先,我等, 你做完,我再做”。线程同步,就是当线程发出一个功能调用时,在没有得到结果之前,该调用就不会返回,其他线程也不能调用该方法。就一般而言,我们在说同步、异步的时候,特指那些需要其他组件来配合或者需要一定时间来完成的任务。在多线程编程...

那些年我们一起追过的缓存写法(二)_zdy0_2004的博客-程序员宅基地

那些年我们一起追过的缓存写法(二)引言感谢园子里的同学对上一篇的支持,很高兴楼主的一些经验及想法能够对大家有一些帮助。上次主要讨论缓存读写这块各种代码实现。本篇是就上次的问题接着来,继续看那些年我们各种缓存用法。目录一:缓存预热二:多级缓存 2.1 介绍 2.2 线程缓存 2.3 内存缓存 2.4 文件缓存

继承QToolButon实现Qt自定义按钮遇到的事件问题_qtoolbutton事件_iron_li的博客-程序员宅基地

目的:QToolButton的clicked()信号只能连接到某个槽函数,当用户动态配置添加一个或者多个按钮到主界面的QToolBar,并且每个按钮被点击后执行不同的行为时,clicked()信号显然就无法满足需求了,因为你不知道是哪个QToolButton被点击了。实现方式:自定义按钮继承自QToolButton,重新实现void QWidget::mouseReleaseE_qtoolbutton事件

拼多多砍价-原理_拼多多砍价原理-程序员宅基地

拼多多砍价只要人多,都能成功。1、拼多多砍价要的就是人脉,假设你朋友多,成功率很大,但是有这种人脉的究竟不是许多,所以许多时分有朋友砍到还有几十块钱的时分基本上就抛弃了,这就需求更多的人脉,假设你人脉无限多,能够直接关闭走人。2、初次砍价的朋友,都有一颗猎奇的心都想砍到0元,刚开始小编规劝我们,先拿价格比较低的,比方几十元的产品练手,这样成功率都是很高的。3、拼多多主张砍价的时间是比较要害的,小编经过总结,一般的晚上9-10点,主张砍价这个时间比较适合,因为这时候我们基本上都有时间,该忙的都忙好_拼多多砍价原理

推荐文章

热门文章

相关标签