• 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===>
    name:xixi
    type:haha"
    "svcA:8087===>
    name:xixi
    type:haha"
    "svcA:8081===>
    name:xixi
    type:haha"
    "svcA:8081===>
    name:xixi
    type:haha"
    "svcA:8087===>
    name:xixi
    type:haha"
    "svcA:8087===>
    name:xixi
    type:haha"
    "svcA:8081===>
    name:xixi
    type:haha"
    "svcA:8081===>
    name:xixi
    type:haha"
    "svcA:8087===>
    name:xixi
    type:haha"
    "svcA:8087===>
    name:xixi
    type:haha"
    "svcA:8081===>
    name:xixi
    type:haha"
    "svcA:8081===>
    name:xixi
    type: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===>
    name:xixi
    type:haha"
    "svcB:8086===>
    name:xixi
    type:haha"
    "svcB:8086===>
    name:xixi
    type:haha"
    "svcB:8082===>
    name:xixi
    type:haha"
    "svcB:8082===>
    name:xixi
    type:haha"
    "svcB:8082===>
    name:xixi
    type:haha"
    "svcB:8086===>
    name:xixi
    type:haha"
    "svcB:8086===>
    name:xixi
    type:haha"
    "svcB:8086===>
    name:xixi
    type:haha"
    "svcB:8082===>
    name:xixi
    type:haha"
    "svcB:8082===>
    name:xixi
    type:haha"
    "svcB:8082===>
    name:xixi
    type:haha"
    "svcB:8086===>
    name:xixi
    type:haha"
    "svcB:8086===>
    name:xixi
    type: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===>
    name:xixi
    type:haha"
    "svcC:8083===>
    name:xixi
    type:haha"
    "svcC:8085===>
    name:xixi
    type:haha"
    "svcC:8085===>
    name:xixi
    type:haha"
    "svcC:8083===>
    name:xixi
    type:haha"
    "svcC:8085===>
    name:xixi
    type:haha"
    "svcC:8085===>
    name:xixi
    type: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也要背锅,因为使用服务名来进行服务间调用,是被大小写是被兼容过的,导致我这边疏忽大意了,还是要细心的好。 ↩︎

  • 相关阅读:
    uva514Rails(栈)
    hdu1160FatMouse's Speed
    如何截取整个屏幕
    equals 与==
    (转载)equals与==
    (转载)关于ArrayList的5道面试题
    java的一些程序
    (转载)OSI七层参考模型和TCP/IP四层参考模型
    (转载)算法面试题
    (转载)火车运煤
  • 原文地址:https://www.cnblogs.com/yw0219/p/10561121.html
Copyright © 2020-2023  润新知