擴(kuò)展Ribbon支持Nacos權(quán)重的三種方式

Nacos支持權(quán)重配置赖条,這是個(gè)比較實(shí)用的功能失乾,例如:

  • 把性能差的機(jī)器權(quán)重設(shè)低,性能好的機(jī)器權(quán)重設(shè)高纬乍,讓請(qǐng)求優(yōu)先打到性能高的機(jī)器上去碱茁;
  • 某個(gè)實(shí)例出現(xiàn)異常時(shí),把權(quán)重設(shè)低仿贬,排查問(wèn)題纽竣,問(wèn)題排查完再把權(quán)重恢復(fù);
  • 想要下線某個(gè)實(shí)例時(shí)诅蝶,可先將該實(shí)例的權(quán)重設(shè)為0退个,這樣流量就不會(huì)打到該實(shí)例上了——此時(shí)再去關(guān)停該實(shí)例,這樣就能實(shí)現(xiàn)優(yōu)雅下線啦调炬。當(dāng)然這是為Nacos量身定制的優(yōu)雅下線方案——Spring Cloud中,要想實(shí)現(xiàn)優(yōu)雅下線還有很多姿勢(shì)舱馅,詳見:《實(shí)用技巧:Spring Cloud中缰泡,如何優(yōu)雅下線微服務(wù)?》 代嗤,里面筆者總結(jié)了四種優(yōu)雅下線的方式棘钞。

然而測(cè)試發(fā)現(xiàn),Nacos權(quán)重配置對(duì)Spring Cloud Alibaba無(wú)效干毅。也就是說(shuō)宜猜,不管在Nacos控制臺(tái)上如何配置,調(diào)用時(shí)都不管權(quán)重設(shè)置的硝逢。

Spring Cloud Alibaba通過(guò)整合Ribbon的方式姨拥,實(shí)現(xiàn)了負(fù)載均衡绅喉。所使用的負(fù)載均衡規(guī)則是 ZoneAvoidanceRule

本節(jié)來(lái)探討如何擴(kuò)展Ribbon叫乌,讓其支持Nacos的權(quán)重配置柴罐,筆者總結(jié)了三種方案。

方案1:自己實(shí)現(xiàn)負(fù)載均衡規(guī)則

思路

自己首先一個(gè)Ribbon負(fù)載均衡規(guī)則就可以了憨奸。

  • 權(quán)重配置啥的革屠,都可以在實(shí)例信息中獲取到。
  • 自己基于權(quán)重配置排宰,計(jì)算出一個(gè)實(shí)例即可似芝。

代碼

@Slf4j
public class NacosWeightRandomV1Rule extends AbstractLoadBalancerRule {
    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {
    }

    @Override
    public Server choose(Object key) {
        List<Server> servers = this.getLoadBalancer().getReachableServers();

        List<InstanceWithWeight> instanceWithWeights = servers.stream()
                .map(server -> {
                    // 注冊(cè)中心只用Nacos,沒(méi)同時(shí)用其他注冊(cè)中心(例如Eureka)板甘,理論上不會(huì)實(shí)現(xiàn)
                    if (!(server instanceof NacosServer)) {
                        log.error("參數(shù)非法党瓮,server = {}", server);
                        throw new IllegalArgumentException("參數(shù)非法,不是NacosServer實(shí)例虾啦!");
                    }

                    NacosServer nacosServer = (NacosServer) server;
                    Instance instance = nacosServer.getInstance();
                    double weight = instance.getWeight();
                    return new InstanceWithWeight(
                            server,
                            Double.valueOf(weight).intValue()
                    );
                })
                .collect(Collectors.toList());

        Server server = this.weightRandom(instanceWithWeights);

        log.info("選中的server = {}", server);
        return server;
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    private class InstanceWithWeight {
        private Server server;
        private Integer weight;
    }

    /**
     * 根據(jù)權(quán)重隨機(jī)
     * 算法參考 https://blog.csdn.net/u011627980/article/details/79401026
     *
     * @param list 實(shí)例列表
     * @return 隨機(jī)出來(lái)的結(jié)果
     */
    private Server weightRandom(List<InstanceWithWeight> list) {
        List<Server> instances = Lists.newArrayList();
        for (InstanceWithWeight instanceWithWeight : list) {
            int weight = instanceWithWeight.getWeight();
            for (int i = 0; i <= weight; i++) {
                instances.add(instanceWithWeight.getServer());
            }
        }
        int i = new Random().nextInt(instances.size());
        return instances.get(i);
    }
}

WARNING

本段代碼存在優(yōu)化空間麻诀,只是用來(lái)演示思考的過(guò)程,不建議用于生產(chǎn)傲醉,如打算使用本方案實(shí)現(xiàn)蝇闭,請(qǐng)參考以下兩點(diǎn)優(yōu)化

  • 簡(jiǎn)單起見,我直接把double型的權(quán)重(weight)硬毕,轉(zhuǎn)成了integer計(jì)算了呻引,存在精度丟失
  • InstanceWithWeight太重了吐咳,在 weightRandom 還得再兩層for循環(huán)逻悠,還挺吃內(nèi)存的,建議百度其他權(quán)重隨機(jī)算法優(yōu)化韭脊。不過(guò)實(shí)際項(xiàng)目一個(gè)微服務(wù)一般也就三五個(gè)實(shí)例童谒,所以其實(shí)內(nèi)存消耗也能忍受。不優(yōu)化問(wèn)題也不大沪羔。

方案2:利用Nacos Client的能力[推薦]

思路

在閱讀代碼Nacos源碼的過(guò)程中饥伊,發(fā)現(xiàn)Nacos Client本身就提供了負(fù)載均衡的能力,并且負(fù)載均衡算法正是我們想要的根據(jù)權(quán)重選擇實(shí)例蔫饰!

代碼在 com.alibaba.nacos.api.naming.NamingService#selectOneHealthyInstance 琅豆,只要想辦法調(diào)用到這行代碼,就可以實(shí)現(xiàn)我們想要的功能啦篓吁!

代碼

@Slf4j
public class NacosWeightRandomV2Rule extends AbstractLoadBalancerRule {
    @Autowired
    private NacosDiscoveryProperties discoveryProperties;

    @Override
    public Server choose(Object key) {
        DynamicServerListLoadBalancer loadBalancer = (DynamicServerListLoadBalancer) getLoadBalancer();
        String name = loadBalancer.getName();
        try {
            Instance instance = discoveryProperties.namingServiceInstance()
                    .selectOneHealthyInstance(name);

            log.info("選中的instance = {}", instance);

            /*
             * instance轉(zhuǎn)server的邏輯參考自:
             * org.springframework.cloud.alibaba.nacos.ribbon.NacosServerList.instancesToServerList
             */
            return new NacosServer(instance);
        } catch (NacosException e) {
            log.error("發(fā)生異常", e);
            return null;
        }
    }

    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {
    }
}

方案3:最暴力的玩法

思路

在閱讀源碼的過(guò)程中茫因,發(fā)現(xiàn)如下代碼:

// 來(lái)自:org.springframework.cloud.alibaba.nacos.ribbon.NacosServerList#getServers
private List<NacosServer> getServers() {
  try {
    List<Instance> instances = discoveryProperties.namingServiceInstance()
      .selectInstances(serviceId, true);
    return instancesToServerList(instances);
  }
  catch (Exception e) {
    throw new IllegalStateException(
      "Can not get service instances from nacos, serviceId=" + serviceId,
      e);
  }
}

這個(gè)NacosServerList 就是給Ribbon去做負(fù)載均衡的”數(shù)據(jù)源”!如果把這里的代碼改成 com.alibaba.nacos.api.naming.NamingService#selectOneHealthyInstance 不也可以實(shí)現(xiàn)我們想要的功能嗎杖剪?

也就是說(shuō)冻押,交給Ribbon的List永遠(yuǎn)只有1個(gè)實(shí)例驰贷!這樣不管Ribbon用什么負(fù)載均衡,都隨他便了翼雀。

代碼

1 參考NacosServerList的代碼饱苟,重寫NacosRibbonServerList

/**
 * 參考o(jì)rg.springframework.cloud.alibaba.nacos.ribbon.NacosServerList
 */
@Slf4j
public class NacosRibbonServerList extends AbstractServerList<NacosServer> {

    private NacosDiscoveryProperties discoveryProperties;

    private String serviceId;

    public NacosRibbonServerList(NacosDiscoveryProperties discoveryProperties) {
        this.discoveryProperties = discoveryProperties;
    }

    @Override
    public List<NacosServer> getInitialListOfServers() {
        return getServers();
    }

    @Override
    public List<NacosServer> getUpdatedListOfServers() {
        return getServers();
    }

    private List<NacosServer> getServers() {
        try {
            Instance instance = discoveryProperties.namingServiceInstance()
                    .selectOneHealthyInstance(serviceId, true);
            log.debug("選擇的instance = {}", instance);
            return instancesToServerList(
                    Lists.newArrayList(instance)
            );
        } catch (Exception e) {
            throw new IllegalStateException(
                    "Can not get service instances from nacos, serviceId=" + serviceId,
                    e);
        }
    }

    private List<NacosServer> instancesToServerList(List<Instance> instances) {
        List<NacosServer> result = new ArrayList<>();
        if (null == instances) {
            return result;
        }
        for (Instance instance : instances) {
            result.add(new NacosServer(instance));
        }
        return result;
    }

    public String getServiceId() {
        return serviceId;
    }

    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {
        this.serviceId = iClientConfig.getClientName();
    }
}

2 編寫配置類

/**
 * 參考:org.springframework.cloud.alibaba.nacos.ribbon.NacosRibbonClientConfiguration
 */
@Configuration
public class NacosRibbonClientExtendConfiguration {
    @Bean
    public ServerList<?> ribbonServerList(IClientConfig config, NacosDiscoveryProperties nacosDiscoveryProperties) {
        NacosRibbonServerList serverList = new NacosRibbonServerList(nacosDiscoveryProperties);
        serverList.initWithNiwsConfig(config);
        return serverList;
    }
}

3 添加注解,讓上面的NacosRibbonClientExtendConfiguration成為Ribbon的默認(rèn)配置狼渊。

// ...其他注解
@RibbonClients(defaultConfiguration = NacosRibbonClientExtendConfiguration.class)
public class ConsumerMovieApplication {
  public static void main(String[] args) {
    SpringApplication.run(ConsumerMovieApplication.class, args);
  }
}

注意

務(wù)必注意箱熬,將 NacosRibbonClientExtendConfiguration 放在ComponentScan上下文(默認(rèn)是啟動(dòng)類所在包及其子包)以外!1芬亍城须!

總結(jié)與對(duì)比

  • 方案1:是最容易想到的玩法。
  • 方案2:是個(gè)人目前最喜歡的方案米苹。首先簡(jiǎn)單糕伐,并且都是復(fù)用Nacos/Ribbon現(xiàn)有的代碼——而Ribbon/Nacos本身都是來(lái)自于大公司生產(chǎn)環(huán)境,經(jīng)過(guò)嚴(yán)苛的生產(chǎn)考驗(yàn)蘸嘶。
  • 方案3:太暴力了良瞧,把Ribbon架空了。此方案中训唱,扔給Ribbon做負(fù)載均衡選擇時(shí)褥蚯,List只有1個(gè)元素,不管用什么算法去算况增,最后總是會(huì)返回這個(gè)元素赞庶!

思考

既然Nacos Client已經(jīng)有負(fù)載均衡的能力,Spring Cloud Alibaba為什么還要去整合Ribbon呢澳骤?

個(gè)人認(rèn)為歧强,這主要是為了符合Spring Cloud標(biāo)準(zhǔn)。Spring Cloud Commons有個(gè)子項(xiàng)目 spring-cloud-loadbalancer 为肮,該項(xiàng)目制定了標(biāo)準(zhǔn)摊册,用來(lái)適配各種客戶端負(fù)載均衡器(雖然目前實(shí)現(xiàn)只有Ribbon,但Hoxton就會(huì)有替代的實(shí)現(xiàn)了)颊艳。

Spring Cloud Alibaba遵循了這一標(biāo)準(zhǔn)丧靡,所以整合了Ribbon,而沒(méi)有去使用Nacos Client提供的負(fù)載均衡能力籽暇。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市饭庞,隨后出現(xiàn)的幾起案子戒悠,更是在濱河造成了極大的恐慌,老刑警劉巖舟山,帶你破解...
    沈念sama閱讀 221,331評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件绸狐,死亡現(xiàn)場(chǎng)離奇詭異卤恳,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)寒矿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,372評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門突琳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人符相,你說(shuō)我怎么就攤上這事拆融。” “怎么了啊终?”我有些...
    開封第一講書人閱讀 167,755評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵镜豹,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我蓝牲,道長(zhǎng)趟脂,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,528評(píng)論 1 296
  • 正文 為了忘掉前任例衍,我火速辦了婚禮昔期,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘佛玄。我一直安慰自己硼一,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,526評(píng)論 6 397
  • 文/花漫 我一把揭開白布翎嫡。 她就那樣靜靜地躺著欠动,像睡著了一般。 火紅的嫁衣襯著肌膚如雪惑申。 梳的紋絲不亂的頭發(fā)上具伍,一...
    開封第一講書人閱讀 52,166評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音圈驼,去河邊找鬼人芽。 笑死,一個(gè)胖子當(dāng)著我的面吹牛绩脆,可吹牛的內(nèi)容都是我干的萤厅。 我是一名探鬼主播,決...
    沈念sama閱讀 40,768評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼靴迫,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼惕味!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起玉锌,我...
    開封第一講書人閱讀 39,664評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤名挥,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后主守,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體禀倔,經(jīng)...
    沈念sama閱讀 46,205評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡榄融,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,290評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了救湖。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片愧杯。...
    茶點(diǎn)故事閱讀 40,435評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖鞋既,靈堂內(nèi)的尸體忽然破棺而出力九,到底是詐尸還是另有隱情,我是刑警寧澤涛救,帶...
    沈念sama閱讀 36,126評(píng)論 5 349
  • 正文 年R本政府宣布畏邢,位于F島的核電站,受9級(jí)特大地震影響检吆,放射性物質(zhì)發(fā)生泄漏舒萎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,804評(píng)論 3 333
  • 文/蒙蒙 一蹭沛、第九天 我趴在偏房一處隱蔽的房頂上張望臂寝。 院中可真熱鬧,春花似錦摊灭、人聲如沸咆贬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,276評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)掏缎。三九已至,卻和暖如春煤杀,著一層夾襖步出監(jiān)牢的瞬間眷蜈,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工沈自, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留酌儒,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,818評(píng)論 3 376
  • 正文 我出身青樓枯途,卻偏偏與公主長(zhǎng)得像忌怎,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子酪夷,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,442評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容