Spring Cloud Ribbon 自定义负载均衡策略

原由

公司项目使用Spring Cloud微服务架构,随着服务的增加,开发调试变得有些麻烦。有些同事的电脑配置不高,无法在本地启动这么多的服务。公司有自己的dev环境,对于开发当前修改的服务可以直接注册到dev环境,使用其他未修改的服务,如Eureka,config等。但是,如果这个时候有前端正在dev调试,则会出现网关转发到本地开发中的服务,出现异常。

出现上述情况的原因是因为Ribbon默认负载均衡策略是轮询,当一个服务出现多个实例的时候,网关转发或者服务消费时就会采用Ribbon的负载均衡策略,出现指向开发本地实例的情况。

知道原因之后解决方法就呼之欲出了:自定义负载均衡策略,使dev环境中的微服务消费或转发都指定到固定dev环境中服务,不让其指定到开发本地起的服务。

实现策略

com.netflix.loadbalancer.IRule是Ribbon的负载均衡策略接口:

public interface IRule{
    /*
     * choose one alive server from lb.allServers or
     * lb.upServers according to key
     * 
     * @return choosen Server object. NULL is returned if none
     *  server is available 
     */

    public Server choose(Object key);
    
    public void setLoadBalancer(ILoadBalancer lb);
    
    public ILoadBalancer getLoadBalancer();    
}

Ribbon自带有几种策略实现:

  • RandomRule:随机选取负载均衡策略,随机Random对象,在所有服务实例中随机找一个服务的索引号,然后从上线的服务中获取对应的服务。
  • RoundRobinRule:线性轮询负载均衡策略。
  • WeightedResponseTimeRule:响应时间作为选取权重的负载均衡策略,根据平均响应时间计算所有服务的权重,响应时间越短的服务权重越大,被选中的概率越高。刚启动时,如果统计信息不足,则使用线性轮询策略,等信息足够时,再切换到WeightedResponseTimeRule。
  • RetryRule:使用线性轮询策略获取服务,如果获取失败则在指定时间内重试,重新获取可用服务。
  • ClientConfigEnabledRoundRobinRule:默认通过线性轮询策略选取服务。通过继承该类,并且对choose方法进行重写,可以实现更多的策略,继承后保底使用RoundRobinRule策略。
  • BestAvailableRule:继承自ClientConfigEnabledRoundRobinRule。从所有没有断开的服务中,选取到目前为止请求数量最小的服务。
  • PredicateBasedRule:抽象类,提供一个choose方法的模板,通过调用AbstractServerPredicate实现类的过滤方法来过滤出目标的服务,再通过轮询方法选出一个服务。
  • AvailabilityFilteringRule:按可用性进行过滤服务的负载均衡策略,会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,还有并发的连接数超过阈值的服务,然后对剩余的服务列表进行线性轮询。
  • ZoneAvoidanceRule:本身没有重写choose方法,用的还是抽象父类PredicateBasedRule的choose。

其中没有我们需要的策略,那我们就自己实现一个IRule。我们参照默认的RoundRobinRule,继承AbstractLoadBalancerRule(实现了IRule的loadBanlancer相关方法):


import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.Server;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * @author hubert
 * @version 1.0
 * @date 2019/9/5 上午10:22
 */
@Slf4j
public class MyRule extends AbstractLoadBalancerRule {
    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {
    }

    @Override
    public Server choose(Object o) {
        log.info("key:" + o);
        List<Server> allServers = getLoadBalancer().getAllServers();
        log.info(allServers.toString());
        return allServers.get(0);
    }
}

我们需要实现choose方法来完成我们自己的策略,getLoadBalancer()可以获取当前服务的所有实例Server的信息,我们需要从中挑选一个作为choose方法的返回。这里就简单地返回列表第一个Server

配置

自定义策略实现之后需要配置,我们要在服务调用方(使用@FeignClient注解的类的方法)进行配置。

简单配置

如果所有调用服务的策略是相同的,我们最简单的配置就是在MyRule类上添加@Component注解,让Spring发现并注入该类。Ribbon会优先使用我们实现的策略。

如果针对不同的服务需要不同的策略,则可以参考官方实例的配置。

@RibbonClient配置

这种方式需要在启动类中添加注解:

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
@RibbonClient(value = "service-hi", configuration = RuleConfig.class)
public class ServiceFeignApplication {

RuleConfig如下:

import com.netflix.loadbalancer.IRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author hubert
 * @version 1.0
 * @date 2019/9/5 上午10:40
 */
@Configuration
public class RuleConfig {
    @Bean
    public IRule ribbonRule() {
        return new MyRule();
    }
}

简单来说就是注入我们自己实现的IRule,然后配置给RioonClient。

@RibbonClientvalue/name属性设置的是被调用的服务名(不是当前正在配置的服务名),也即使声明@FeignClient是指定的value/name。如果当前服务调用多个其他服务,可以用@RibbonClient给每个被调用服务设置不同的策略。

需要注意的是RuleConfig类需要放在启动类的上层(或者不同包名),避免Spring默认扫描到。否则会出现“简单配置”效果,即所有服务都使用这个策略,无法实现不同服务不同策略的效果。

配置文件配置

@RibbonClient类似,可以为每个微服务单独配置策略。我们在yml配置文件中添加<serverName>.ribbon.NFLoadBalancerRuleClassName

service-hi:
  ribbon:
    NFLoadBalancerRuleClassName: com.hubert.feign.MyRule

<serverName>也就是声明@FeignClient是指定的value/name

实验

配置完成之后就是启动实验了,我们依次启动2个被调用服务,以及一个调用服务:

2019-09-05 14:31:45.408  INFO 1143 --- [ix-service-hi-2] com.hubert.feign.MyRule                  : key:null
2019-09-05 14:31:45.409  INFO 1143 --- [ix-service-hi-2] com.hubert.feign.MyRule                  : [192.168.31.244:8882, 192.168.31.244:8881]

发现日志打印,说明使用了我们自定义的策略,并且效果也是始终调用第一个服务。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容