1. 程式人生 > >【五】Ribbon負載均衡

【五】Ribbon負載均衡

dna 關心 beans 應用 har 獲取 cond under conf

1、概述
1.1、是什麽
  Spring Cloud Ribbon 是基於Netflix Ribbon實現的一套客戶端負載均衡的工具。
  簡單的說, Ribbon是Netflix發布的開源項目,主要功能是提供客戶端的軟件負載均衡算法,將Netflix的中間層服務連接在一起。Ribbon客戶端組件提供一系列完善的配置項如連接超時,重試等。簡單的說,就是在配置文件中列出Load BalanCer(簡稱LB)後面所有的機器,Ribbon會自動的幫助你基於某種規則(如簡單輪詢,隨機連接等)去連接這些機器。我們也很容易使用Ribbon實現自定義的負載均衡算法。

1.2、能幹什麽
LB(負載均衡)

LB,即負載均衡( Load Balanoe ),在微服務或分布式集群中經常用的一種應用。


負載均衡簡單的說就是將用戶的請求平攤的分配到多個服務上,從而達到系統的HA。
常見的負載均衡有軟件nginx , LVS ,硬件 F5 等。
相應的在中間件,例如:dubbo。和 SpringCloud 中均給我們提供了負載均衡,SpringCloud的負載均衡算法可以自定義。

兩種LB

1、集中式LB(偏硬件)

集中式 LB
即在服務的消費方和提供方之間使用獨立的LB設施(可以是硬件,如F5,也可以是軟件,如nginx ) ,由該設施負責把訪問請求通過某種策略轉發至服務的提供方;

2、進程內LB(偏軟件)

進程內 LB
將LB邏輯集成到消費方,消費方從服務註冊中心獲知有哪些地址可用,然後自己再從這些地址中選擇出一個合適的服務器。


Ribbon就屬於進程內LB,它只是一個類庫,集成於消費方進程,消費方通過它來獲取到服各提供方的地址。

1.3、官方資料
https://github.com/Netflix/ribbon/wiki

2、Ribbon配置初步
1、修改microservicecloud-consumer-dept-80工程POM,添加:

<!-- Ribbon相關  -->
<!--Ribbon  需要 eureka -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId
>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency>
2、修改YML,加入Ribbon配置
eureka:
  client:
    register-with-eureka: false  #自己不能註冊
    service-url: 
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/  
3、在cn.hfbin.springcloud.cfgbeans.ConfigBean.java中的getRestTemplate()方法上上加入註解@LoadBalanced
@Configuration
public class ConfigBean
{ 
    @Bean
    @LoadBalanced   //Spring Cloud Ribbon是基於Netflix Ribbon實現的一套客戶端  負載均衡的工具。
    public RestTemplate getRestTemplate()
    {
        return new RestTemplate();
    }
    
}
4、主啟動類DeptConsumer80_App添加 @EnableEurekaClient
5、修改客戶端的程序類DeptController_Consumer.java
//把下面這行 
private static final String REST_URL_PREFIX = "http://localhost:8001";
//改成下面這行
private static final String REST_URL_PREFIX = "http://MICROSERVICECLOUD-DEPT";
6、測試

先啟動單個eureka三個集群再啟動8001,最後啟動80

技術分享圖片

7、總結

Ribbon整合後80可以直接調用微服務而不再關心地址和端口

技術分享圖片

Ribbon 在工作時分成兩步第一步先選擇 EurekaServer,它優先選擇在同一個區域內負載較少的 server。
第二步再根據用戶指定的策略,在從server取到的服務註冊列表中選擇一個地址。
其中Ribbon 提供了多種策略:比如輪詢、隨機和根據響應時間加權。

2、新建兩個工程microservicecloud-provider-dept-8002和microservicecloud-provider-dept-8003,參考8001
2.1、將8001 POM 內容拷貝到8002 、 8003

2.2、將8001下的java代碼拷貝到8002 、 8003,並修改主啟動類的名稱

2.3、將8001 YML復制到8002 、 8003資源文件下、修改部分如圖:
技術分享圖片

3.3、新建兩個數據庫springclouddb02 、 springclouddb03

#記得修改數據庫名

CREATE SCHEMA `springclouddb01` DEFAULT CHARACTER SET utf8 ;

create table springclouddb01.dept
(
    deptno bigint not null primary key auto_increment,
    dname varchar(60),
    db_source varchar(60)
);

insert into dept(dname , db_source) values(開發部 , database());
insert into dept(dname , db_source) values(人事部 , database());
insert into dept(dname , db_source) values(財務部 , database());
insert into dept(dname , db_source) values(市場部 , database());
insert into dept(dname , db_source) values(運維部 , database());

select * from springclouddb01.dept;

3.4、測試:
啟動7001—> 7002 —> 7003 —> 8001 —> 8002 ----> 8003 —> 80

訪問連接:http://localhost/consumer/dept/list 註意看返回的json數據,發現每次刷新得到的數據是不一樣的,輪詢的返回數據,這時通過Ribbon完成了負載均衡。

3.5、總結:

技術分享圖片

Ribbon默認自帶了七種算法,默認是輪詢。

我們也可以自定義自己的算法。

4.1、修改訪問服務的算法方式
* 切換 訪問的算法 很簡單只需要換成我們要返回算法的實例即可
* 默認有七個算法,可以也自定義自己的算法
package cn.hfbin.myrule;

/**
 * 該配置類不可以放在與註解 @ComponentScan 的同包或者子包下,否則不起作用 (自定義算法也是一樣)
 */
@Configuration
public class MySelfRule
{
    /*
    * 切換 訪問的算法 很簡單只需要換成我們要返回算法的實例即可
    * 默認有七個算法,可以自定義自己的算法
    * */
    @Bean
    public IRule myRule()
    {
        //如果 突然間一個服務掛了 訪問帶掛的服務器會報錯,出現錯誤頁面
        //return new RoundRobinRule();
        //return new RandomRule(); //達到的目的,用我們重新選擇的隨機算法替代默認的輪詢。
        //如果 突然間一個服務掛了 訪問帶掛的服務器會報錯,出現錯誤頁面,但是過一下子他不會再訪問掛的機器,不會顯示出錯誤的頁面。
        return new RetryRule();
    }
}

4.2、自定義IRule算法
1、在主程序添加@RibbonClient(name=“MICROSERVICECLOUD-DEPT”,configuration=MySelfRule.class)

(註意:自定義算法不可以放在與註解 @ComponentScan 的同包或者子包下,否則不起作用 )

2、MySelfRule.java

@Configuration
public class MySelfRule
{
    @Bean
    public IRule myRule()
    {
        //return new RandomRule();// Ribbon默認是輪詢,我自定義為隨機
        //return new RoundRobinRule();// Ribbon默認是輪詢,我自定義為隨機
        //return new RetryRule();
        return new RandomRule();// 我自定義為每臺機器5次
    }
}

3、自定義算法必須繼承抽象類 AbstractLoadBalancerRule

package cn.hfbin.myrule;

/*
* 參考隨機數的源碼來修改
*
* https://github.com/Netflix/ribbon/blob/master/ribbon-loadbalancer/src/main/java/com/netflix/loadbalancer/RandomRule.java
* */
public class RandomRule extends AbstractLoadBalancerRule {

    // total = 0 // 當total==5以後,我們指針才能往下走,
    // index = 0 // 當前對外提供服務的服務器地址,
    // total需要重新置為零,但是已經達到過一個5次,我們的index = 1
    // 分析:我們5次,但是微服務只有8001 8002 8003 三臺,OK?
    //


    private int total = 0;            // 總共被調用的次數,目前要求每臺被調用5次
    private int currentIndex = 0;    // 當前提供服務的機器號

    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) {
                /*
                 * No servers. End regardless of pass, because subsequent passes only get more
                 * restrictive.
                 */
                return null;
            }

//            int index = rand.nextInt(serverCount);// java.util.Random().nextInt(3);
//            server = upList.get(index);

            if (total < 5) {
                //獲取服務的第幾個
                server = upList.get(currentIndex);
                total++;
            } else {
                total = 0;
                currentIndex++;
                if (currentIndex >= upList.size()) {
                    currentIndex = 0;
                }
            }


            if (server == null) {
                /*
                 * The only time this should happen is if the server list were somehow trimmed.
                 * This is a transient condition. Retry after yielding.
                 */
                Thread.yield();
                continue;
            }

            if (server.isAlive()) {
                return (server);
            }

            // Shouldn‘t actually happen.. but must be transient or a bug.
            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

    }

}

4、測試

測試與上面啟動順序一樣

【五】Ribbon負載均衡