Spring Cloud認知學習(二):Ribbon使用



💡接上回Spring Cloud認知學習(一):Spring Cloud介紹與Eureka使用
💡上一篇介紹了Spring Cloud,以及服務注冊與發現的組件Eureka的簡單使用。
💡這一篇我們來介紹微服務構建起來后,使用Ribbon來解決多個服務的負載均衡問題。


Ribbon負載均衡

客戶端負載均衡的意思是,讓客戶端來進行負載均衡,而不是服務端來進行負載均衡,是什么意思呢?比如說你要去排隊買東西,有三條隊,你自然而然地會選擇隊伍短的隊去排咯😀,這是由你去進行的負載均衡,而不是服務員幫你去安排你該排哪條隊。
💡為什么要采用客戶端負載均衡呢?

  • 主要是,客戶端的負載均衡是會減少服務端的資源消耗的,就好像如果有很多消費者的話,你要么就雇佣很多服務員,要么就讓消費者等服務員有空。
  • 其次呢,這也是從架構上考慮的,因為客戶端會從eureka中拉取服務可用列表,那么如果此時順便拉取了此時各種服務的負載狀態的話,那么也就順手地可以使用這些信息來進行客戶端負載均衡了。


簡單使用步驟:

下面的代碼可以參考:Ribbon負載均衡簡單實驗

1.新建模塊,用於負載均衡

新建模塊spring-cloud-user-service-8002spring-cloud-user-service-8003


2.修改模塊代碼:

  • 給模塊spring-cloud-user-service-8002spring-cloud-user-service-8003導入pom.xml、修改application.yml和修改主啟動類。
  • 代碼基本與模塊spring-cloud-user-service-8001一樣的。
  • pom.xml與spring-cloud-user-service-8001一樣的。
  • 控制器,mapper,都跟spring-cloud-user-service-8001一樣的。只是主程序類的名字問題
  • 主要是修改服務端口和數據庫。
    20200511220322

由於新建了服務提供者,所以為了讓不同的服務使用不同的數據庫,所以要執行下面的SQL來創建額外的數據庫:

sql:

-- db2
DROP DATABASE IF EXISTS cloud02;
CREATE DATABASE cloud02 CHARACTER SET UTF8;
USE cloud03;
CREATE TABLE user
(
  id int PRIMARY KEY AUTO_INCREMENT,
  username VARCHAR(255),
  fullName  VARCHAR(255)
);


INSERT INTO user(username,fullName) VALUES('zhangsan','張三2');
INSERT INTO user(username,fullName) VALUES('lisi','李四2');
INSERT INTO user(username,fullName) VALUES('wangwu','王五2');
INSERT INTO user(username,fullName) VALUES('zhaoliu','趙六2');
INSERT INTO user(username,fullName) VALUES('lidazhuang','李大壯2');

 
SELECT * FROM user;

--- db3
DROP DATABASE IF EXISTS cloud03;
CREATE DATABASE cloud03 CHARACTER SET UTF8;
USE cloud03;
CREATE TABLE user
(
  id int PRIMARY KEY AUTO_INCREMENT,
  username VARCHAR(255),
  fullName  VARCHAR(255)
);


INSERT INTO user(username,fullName) VALUES('zhangsan','張三3');
INSERT INTO user(username,fullName) VALUES('lisi','李四3');
INSERT INTO user(username,fullName) VALUES('wangwu','王五3');
INSERT INTO user(username,fullName) VALUES('zhaoliu','趙六3');
INSERT INTO user(username,fullName) VALUES('lidazhuang','李大壯3');

 
SELECT * FROM user;


3.啟動模塊

3.1啟動服務生產者:
spring-cloud-eureka-server-7001spring-cloud-user-service-8001spring-cloud-user-service-8002spring-cloud-user-service-8003
查看eureka內部注冊的服務實例有多少個,你可以看到我們啟動的三個服務實例都顯示出來了。
20200410223800

3.2啟動服務消費者:
現在對於USERSERIVE服務有三個服務實例了,然后我們啟動服務消費者模塊,看多次調用下他是怎么調用的吧。
由於上面對不同服務的數據庫有了小修改(為了有區分,所以我修改了一下數據,但實際業務中他們應該是相同的),所以可以根據數據來判斷當前調用了哪個數據庫。
多次調用http://localhost/user/list,你應該能看到數據在變化,說明默認是有負載均衡的。


⚪你可能有點疑惑,默認的負載均衡是什么時候配置的呢?還記得我們之前在配置消費者從eureka中獲取服務列表時配置了什么嗎?我們給我們的restTemplate加了一個注解@LoadBalanced,而LoadBalanced就是負載均衡的意思。

當你調用服務之后,你會看到消費者拉取服務列表的時候會拉取到一些服務的健康信息。
20200419011434

默認情況下,從eureka中拉取的服務會使用輪詢調用(加入有ABC三個服務實例,會順序的逐一的調用,比如說可能會是不斷按BCA的順序來調用;),但ribbon能幫我們做更多。

下面使用Ribbon來進行客戶端的負載均衡。



4.修改消費者模塊

由於Ribbon是客戶端的負載均衡,所以要修改消費者模塊spring-cloud-user-consumer-80
4.1修改pom.xml,添加ribbon依賴:

        <!--增加ribbon依賴 start-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
            <!--舊版的需要去掉-netflix-->
        </dependency>
        <!--增加ribbon依賴 end-->

4.2.修改負載均衡策略:
⚪當導入ribbon的時候,如果你不做其他操作,默認的負載均衡還是輪詢。下面我們修改一下負載均衡策略:
20200419012057

@Configuration
public class AppConfig {
    @Bean
    @LoadBalanced // eureka與這個配合,要使用LoadBalanced才會調用eureka中注冊的服務
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    @Bean
    public IRule myRule() {
//        return new RoundRobinRule();
		return  new RandomRule();
//        return new RetryRule();
    }
}

然后你重新調用幾次http://localhost/user/list,你應該能看到負載均衡策略變了。

上面的例子中已經演示了修改負載均衡策略。下面來講負責均衡相關的東西。



負載均衡算法:

Ribbon中負責負載均衡策略的就是IRule,所以我們上面的代碼就新創建了一個IRule的bean。
下面講一下這個IRule的幾個常見的實現類。

  • RandomRule:隨機調用服務
  • RoundRobinRule:輪詢調用服務
  • WeightedResponseTimeRule:是加權策略,某個服務權重高的話調用的次數就會多。內部會維護一個權重表,每隔一段時間就依據服務的響應時間來更新權重,響應時間短的權重高。(剛啟動的時候由於沒有服務調用的相關信息,會先使用輪詢策略。)
  • RetryRule:默認內部是輪詢調用(注意這個策略可以更改),不過是帶重試機制的輪詢,ABC三個服務實例,如果B服務實例突然掛了,那么默認的輪詢策略輪詢到B的時候應該會調用失敗,而如果采用了RetryRule,那么B調用不了的時候,就會嘗試調用C,以保證此次調用是成功的。
@Configuration
public class AppConfig {
    @Bean
    @LoadBalanced // eureka與這個配合,要使用LoadBalanced才會調用eureka中注冊的服務
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    @Bean
    public IRule myRule(){
//        return new RandomRule(); // 隨機調用服務
//        return  new RoundRobinRule();// 輪詢策略
        return new RetryRule();// 帶重試的輪詢
    }
}

自定義負載規則:

除了以上的規則,你還可以自定義負載均衡的規則。
你可能會想,那么我應該可以參考一下RandomRule或RoundRobinRule的實現重寫一下,然后像上面指定IRule這個Bean的實現對象就行了。
💡 但要注意一點,如果你是在主程序類的同級目錄或下級目錄下(也就是能被主程序類的ComponentScan掃描到的目錄),那么這個規則會對這個消費者的所有調用的服務生效
1.寫一個負載均衡規則:只調用8003的服務。

package com.progor.study.myrule;

import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;

import java.util.List;

public class MyLoadBalancedRule extends AbstractLoadBalancerRule {

    // 這個choose方法就是選擇哪個服務來進行調用
    public Server choose(ILoadBalancer lb, Object key) {
        // ILoadBalancer是服務注冊列表
        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) {
                // 服務數為0
                return null;
            }

            // 看一下下面的代碼(此段代碼來自RandomRule),應該能判斷出來,就是這里指定了返回的server,
            // 所以我們也在這里修改策略,假如說我們指定只調用8003的
//            int index = rand.nextInt(serverCount);
//            server = upList.get(index);
            // 修改 start
            if (upList.size()==0){
                return null;
            }
            for (int i = 0; i < upList.size() ; i++) {
                Server item = upList.get(i);
                int httpPort = item.getPort();
                // 你可以通過Server和ILoadBalancer的各種參數來自定義你的規則()
                if (httpPort == 8003){
                    server = item; // 這里由於只是示例,所以就隨便寫了,所以安全邏輯並沒有做完全。
                }
            }
            // 修改 end

            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

    }
}

2.在主程序類的外部目錄創建一個Configuration:
20200419024200

package com.progor.config;

import com.netflix.loadbalancer.IRule;
import com.progor.study.myrule.MyLoadBalancedRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyConfig {

    @Bean
    public IRule myRule(){
        return new MyLoadBalancedRule();// 使用我們自定義的規則
    }
}

3.修改主程序類,增加@RibbonClient
如果你的Configuration放在了主程序類外部的時候就要加上這個才能掃描到外部的Configuration:
💡這個注解用來指定某個服務的負載均衡規則,如果不使用這個注解來給對應服務配置負載均衡策略,並且你在內層目錄的配置類中指定了IRule的實例為我們創建的實現類實例,那么所有的服務都會采用這個策略。

package com.progor.study;

import com.progor.config.MyConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;

@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name="USERSERIVE",configuration= MyConfig.class)
public class UserConsumer80Application {
    public static void main(String[] args) {
        SpringApplication.run(UserConsumer80Application.class, args);
    }
}


4.調用http://localhost/user/list,你會發現現在只會有8003的數據返回了,這說明我們的負載均衡生效了。


🐶對於不同服務的負載均衡規則的問題,也可以參考代碼中的MessageService。這里面我寫了一個Message服務的兩個服務實例8004和8005,並且讓消費者來調用了這個服務,效果如下:

  • 當不使用@RibbonClient時:由於那個Configuration並沒有被掃描到,所以不會生效,此時應該所有的服務都使用內部配置的負載均衡規則
    如果在內部配置中采用內部的定義規則:
    @Bean
    public IRule myRule(){s
//        return new RandomRule(); // 隨機調用服務
//        return  new RoundRobinRule();// 輪詢策略
        return new RetryRule();// 帶重試的輪詢
    }

當在內部配置規則的額時候,如果使用我們定義的規則:由於我們之前為USERSERVICE定義了只使用8003服務的規則,此時MESSAGESERVICE也會使用這個規則,那么這時候MESSAGESERVICE會一直請求不到。

    @Bean
    public IRule myRule() {
//		return  new RoundRobinRule();
//		return  new RandomRule();
//        return new RetryRule();
        return new MyLoadBalancedRule();
    }

  • 當使用@RibbonClient時:對於指定的服務,會使用指定的負載均衡規則。【從其他資料(不確定😓)中了解到以前的@RibbonClient的name應該是可以為空的,從這個注解的name有default默認值,我覺得這個說法應該是對的。但現在不允許了,應該是為了避免專屬的負載均衡規則覆蓋全局規則的問題】
//  自定義負載均衡的代碼
package com.progor.config;

import com.netflix.loadbalancer.IRule;
import com.progor.study.myrule.MyLoadBalancedRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyConfig {

    @Bean
    public IRule myRule() {
        return new MyLoadBalancedRule();// 使用我們自定義的規則
    }
}

//  主程序類代碼
@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "USERSERIVE", configuration = MyConfig.class) 
public class UserConsumer80Application {
    public static void main(String[] args) {
        SpringApplication.run(UserConsumer80Application.class, args);
    }
}



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM