Spring Cloud_7_Ribbon負載均衡器

Ribbon負載均衡器

  • Ribbon負載均衡器

1、Ribbon負載均衡器

  • Ribbon的負載均衡接口,定義了服務器的操作,主要是用於進行服務器的選擇
  • 《Spring Cloud_6_負載均衡框架Ribbon》的案例中,客戶端使用了RestClient類,在發送請求時,會使用負載均衡器(ILoadBalancer)接口,根據特定的邏輯來選擇服務器,服務器列表可以使用listOfServers進行配置,也可以使用動態更新機制
  • 試用負載均衡器

1.1、修改測試類

package com.atm.cloud;

import java.util.ArrayList;
import java.util.List;

import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;

public class MyChoseRESTClient {

    public static void main(String[] args) {

        // 創建負載均衡器
        ILoadBalancer iLoadBalancer = new BaseLoadBalancer();

        // 添加服務器
        List<Server> serverList = new ArrayList<Server>();
        serverList.add(new Server("localhost", 8080));
        serverList.add(new Server("localhost", 8081));

        iLoadBalancer.addServers(serverList);

        //進行6次服務器選擇
        for (int i = 0; i < 6; i++) {
            Server server=iLoadBalancer.chooseServer(null);
            System.out.println(server);
        }
    }
}

  • 代碼中使用了BaseLoadBalancer這個負載均衡器,將兩個服務器對象加入到負載均衡器中,再調用了6次chooseServer方法
  • 根據結果可以判定,選擇服務器的邏輯是一致的,在默認情況下,會使用RoundRobinRule的規則邏輯

1.2、自定義負載規則

  • 由前面小節可知,選擇那個服務器進行請求處理,由ILoadBalancer接口的chooserServer方法決sing,而在BaseLoadBalancer類中,則使用IRule接口的choose方法來決定選擇哪一個服務器對象
  • 如果想自定義負載均衡規定,可以編寫一個IRule接口的實現類,實現自己的負載規定
package com.atm.cloud;

import java.util.List;
import java.util.Random;

import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.Server;

public class MyRule implements IRule {

    ILoadBalancer ibBalancer;

    public MyRule() {

    }

    public MyRule(ILoadBalancer iLoadBalancer) {
        this.ibBalancer = iLoadBalancer;
    }

    public Server choose(Object key) {
        // 獲取全部的服務器
        List<Server> servers = ibBalancer.getAllServers();

        /**
        Random random=new Random();
        int randNum=random.nextInt(10);

        if(randNum>7){
            getServerByPort(servers, 8081);
        }
        **/

        // 至返回第一個Server對象
        //return getServerByPort(servers, 8080);
        return servers.get(0);
    }

    public Server getServerByPort(List<Server> servers,int port){
        for (Server server : servers) {
            if(server.getPort()==port){
                return server;
            }
        }
        return null;
    }

    public ILoadBalancer getLoadBalancer() {
        return this.ibBalancer;
    }

    public void setLoadBalancer(ILoadBalancer iLoadBalancer) {
        this.ibBalancer = iLoadBalancer;
    }

}
  • 在自定義規則類中,實現了choose方法,調用了ILoadBalancer的gerAllServers方法返回全部的服務器,爲了簡單起見,本例只返回第一個服務器
  • 爲了能在負載均衡器中使用自定義的規則,需要修改選擇服務器的代碼
package com.atm.cloud;

import java.util.ArrayList;
import java.util.List;

import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;

public class MyChoseRESTClient {

    public static void main(String[] args) {

        // 創建負載均衡器
        //ILoadBalancer iLoadBalancer = new BaseLoadBalancer();
        BaseLoadBalancer iLoadBalancer=new BaseLoadBalancer();

        //設置自定義的負載規則
        iLoadBalancer.setRule(new MyRule(iLoadBalancer));

        // 添加服務器
        List<Server> serverList = new ArrayList<Server>();
        serverList.add(new Server("localhost", 8080));
        serverList.add(new Server("localhost", 8081));

        iLoadBalancer.addServers(serverList);

        //進行6次服務器選擇
        for (int i = 0; i < 6; i++) {
            Server server=iLoadBalancer.chooseServer(null);
            System.out.println(server);
        }
    }
}

  • 可以看到,請求6次所得到的服務器均爲8080
  • 以上是直接使用編碼方式來設置負載規則,可以使用配置的方式來完成這些工作
  • 修改Ribbon的配置,讓請求的客戶端,使用我們定義的負載規則
package com.atm.cloud;

import com.netflix.client.ClientFactory;
import com.netflix.client.http.HttpRequest;
import com.netflix.client.http.HttpResponse;
import com.netflix.config.ConfigurationManager;
import com.netflix.niws.client.http.RestClient;

public class MyRESTClient {

    public static void main(String[] args) {

        // 設置請求的服務器
        ConfigurationManager.getConfigInstance().setProperty(
                "my-client.ribbon.listOfServers",
                "localhost:8080,localhost:8081");

        // *****配置規則處理類
        ConfigurationManager.getConfigInstance().setProperty(
                "my-client.ribbon.NFLoadBalancerRuleClassName",
                MyRule.class.getName());

        // 獲取REST請求客戶端
        RestClient client = (RestClient) ClientFactory
                .getNamedClient("my-client");

        // 創建請求實例
        HttpRequest request = HttpRequest.newBuilder().uri("/person/1").build();

        // 發送6次請求到服務器中
        for (int i = 0; i < 6; i++) {
            try {

                HttpResponse response = client.executeWithLoadBalancer(request);

                String result = response.getEntity(String.class);

                System.out.println(result);

            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }

}
  • 請求客戶端中,基本與前面張傑的客戶端基本一致,只是加入了“my-client.ribbon.NFLoadBalancerRuleClassName”屬性,設置了自定義規則處理類爲MyRule,這個配置項同樣可以在配置文件中使用,包括SpringCloud的配置文件等

  • 在實際環境中,如果要實現自定義的負載規則,可能還需要結合各種因素:具體業務的發生時間、服務器性能等

1.3、Ribbon自帶的負載規則

  • Ribbon提供了若干個內置的負載規則,使用者完全可以直接使用

    1. RoundRobinRule:系統默認的規則,通過簡單的輪詢服務列表來選擇服務器,其他的規則在很多情況下,仍然使用RoundRobinRule
    2. AvailablilityFilteringRule:該各種會忽略以下服務器

      無法連接的服務器:在默認情況下,如果3次連接失敗,該服務器將會被置爲“短路”的狀態,該狀態將持續30秒,如果再次連接失敗,“短路”狀態的持續時間將會以幾何級增加。可以通過修改niws.loadbalance..connerctionFailureCountThreshold屬性來配置連接失敗的次數
      併發數過高的服務器:如果連接到該服務器的併發數過高,也會被這個規則忽略,可以通過修改.ribbon.ActiveConnectionLimit屬性來設定最高併發數

    3. WeightedResponseTimeRule:爲每個服務器賦予一個權重值,服務器的響應時間越長,該權重值就越少,這個規則會隨機選擇服務器,這個權重值有可以能會決定服務器的選擇

    4. ZoneAvoidanceRule:該規則以區域、可用服務器爲基礎,進行服務器選擇。使用Zone對服務器進行分類,可以理解爲機架或者機房
    5. BestAvailiableRule:忽略“短路”的服務器,並選擇併發數較低的服務器
    6. RandomRule:隨機選擇可用服務器
    7. RetryRule:含有重試的選擇邏輯,如果使用RoundRobinRule
  • 以上提供的負載規則,基本上可以滿足大部分的需求

1.4、Ping機制

  • 在負載均衡器中,提供了Ping的機制,每隔一段時間,會去Ping服務器,判斷服務器是否存活
  • 該工作由IPing接口的實現類負責,如果單獨使用Ribbon,在默認情況下,不會激活Ping機制,默認的實現類爲DummyPing
package com.atm.cloud;

import java.util.ArrayList;
import java.util.List;

import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.PingUrl;
import com.netflix.loadbalancer.Server;

public class MyTestPingUrl {

    public static void main(String[] args) throws InterruptedException {
        // 創建負載均衡器
        BaseLoadBalancer lb = new BaseLoadBalancer();

        // 添加服務器
        List<Server> servers = new ArrayList<Server>();

        // 8080 端口連接正常
        servers.add(new Server("localhost", 8080));
        // 一個不存在的端口
        servers.add(new Server("localhost", 8888));

        lb.addServers(servers);

        // 設置 IPing 實現類
        lb.setPing(new PingUrl());

        // 設置 Ping 時間間隔爲 2 秒
        lb.setPingInterval(2);

        Thread.sleep(6000);

        for (Server s : lb.getAllServers()) {
            System.out.println(s.getHostPort() + " 狀態:" + s.isAlive());
        }
    }

}
  • 使用了代碼的方法來設置負載均衡器使用PingUrl,設置了每隔2秒,就向兩個服務器請求
  • PingUrl實際是使用的是HttpClient

  • 除了在代碼中配置IPing類外,還可以在配置中設置IPing實現類
package com.atm.cloud;

import java.util.List;

import com.netflix.client.ClientFactory;
import com.netflix.config.ConfigurationManager;
import com.netflix.loadbalancer.PingUrl;
import com.netflix.loadbalancer.Server;
import com.netflix.niws.client.http.RestClient;

public class MyPingConfig {

    public static void main(String[] args) throws InterruptedException {
        // 設置請求的服務器
        ConfigurationManager.getConfigInstance().setProperty(
                "my-client.ribbon.listOfServers",
                "localhost:8080,localhost:8888");

        // 配置 Ping 處理類
        ConfigurationManager.getConfigInstance().setProperty(
                "my-client.ribbon.NFLoadBalancerPingClassName",
                PingUrl.class.getName());

        // 配置 Ping 時間間隔
        ConfigurationManager.getConfigInstance().setProperty(
                "my-client.ribbon.NFLoadBalancerPingInterval", 2);

        // 獲取 REST 請求客戶端
        RestClient client = (RestClient) ClientFactory
                .getNamedClient("my-client");

        Thread.sleep(6000);

        // 獲取全部服務器
        List<Server> servers = client.getLoadBalancer().getAllServers();
        System.out.println(servers.size());

        // 輸出狀態
        for (Server s : servers) {
            System.out.println(s.getHostPort() + " 狀態:" + s.isAlive());
        }

    }
}
  • my-client.ribbon.NFLoadBalancerPingClassName:配置 IPing 的實現類
  • my-client.ribbon.NFLoadBalancerPingInterval:配置 Ping 操作的時間間隔。
  • 以上兩個配置,同樣可以使用在配置文件中

1.5、自定義Ping

  • 實現自定義Ping較爲簡單,先實現IPing接口,再通過配置來設定具體的Ping實現類
package com.atm.cloud;

import com.netflix.loadbalancer.IPing;
import com.netflix.loadbalancer.Server;

public class MyPing implements IPing{

    public boolean isAlive(Server server) {
        System.out.println("這是自定義 Ping 實現類:" + server.getHostPort());
        return true;
    }

}
  • 要使用自定義的Ping類,通 過 修 改client.nameSpace.NFLoadBalancerPingClassName 配置即可

1.6、其他配置

  • NFLoadBalancerClassName:指定負載均衡器的實現類,可利用該配置,實現自己的負載均衡器。
  • NIWSServerListClassName:服務器列表處理類,用來維護服務器列表,Ribbon
    已經實現動態服務器列表。

  • NIWSServerListFilterClassName:用於處理服務器列表攔截。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章