1. 程式人生 > 資訊 >已用上小米 MIX 4,雷軍回顧 MIX 系列過去 5 款機型:最喜歡 MIX 2 全陶瓷尊享版

已用上小米 MIX 4,雷軍回顧 MIX 系列過去 5 款機型:最喜歡 MIX 2 全陶瓷尊享版

Ribbon:負載均衡(基於客戶端)

Ribbon是什麼?
  • Spring Cloud Ribbon 是基於Netflix Ribbon 實現的一套客戶端負載均衡的工具
  • 簡單的說,Ribbon 是 Netflix 釋出的開源專案,主要功能是提供客戶端的軟體負載均衡演算法,將 Netflix 的中間層服務連線在一起。Ribbon 的客戶端元件提供一系列完整的配置項,如:連線超時、重試等。簡單的說,就是在配置檔案中列出 LoadBalancer (簡稱LB:負載均衡) 後面所有的及其,Ribbon 會自動的幫助你基於某種規則 (如簡單輪詢,隨機連線等等) 去連線這些機器。我們也容易使用 Ribbon 實現自定義的負載均衡演算法!

Ribbon能幹嘛?

  • LB,即負載均衡 (LoadBalancer) ,在微服務或分散式叢集中經常用的一種應用。
  • 負載均衡簡單的說就是將使用者的請求平攤的分配到多個服務上,從而達到系統的HA (高用)。
  • 常見的負載均衡軟體有 Nginx、Lvs 等等。
  • Dubbo、SpringCloud 中均給我們提供了負載均衡,SpringCloud 的負載均衡演算法可以自定義
  • 負載均衡簡單分類:
    • 集中式LB
      • 即在服務的提供方和消費方之間使用獨立的LB設施,如Nginx(反向代理伺服器),由該設施負責把訪問請求通過某種策略轉發至服務的提供方!
    • 程序式 LB
      • 將LB邏輯整合到消費方,消費方從服務註冊中心獲知有哪些地址可用,然後自己再從這些地址中選出一個合適的伺服器。
      • Ribbon 就屬於程序內LB,它只是一個類庫,集成於消費方程序,消費方通過它來獲取到服務提供方的地址!

整合Ribbon

springcloud-consumer-dept-80向pom.xml中新增Ribbon和Eureka依賴

<!--Ribbon-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-ribbon</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>
<!--Eureka: Ribbon需要從Eureka服務中心獲取要拿什麼-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>

在application.yml檔案中配置Eureka

# Eureka配置
eureka:
  client:
    register-with-eureka: false # 不向 Eureka註冊自己
    service-url: # 從三個註冊中心中隨機取一個去訪問
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/

主啟動類加上@EnableEurekaClient註解,開啟Eureka

//Ribbon 和 Eureka 整合以後,客戶端可以直接呼叫,不用關心IP地址和埠號
@SpringBootApplication
@EnableEurekaClient //開啟Eureka 客戶端
public class DeptConsumer_80 {
    public static void main(String[] args) {
        SpringApplication.run(DeptConsumer_80.class, args);
    }
}

自定義Spring配置類:ConfigBean.java 配置負載均衡實現RestTemplate

@Configuration
public class ConfigBean {
    //@Configuration -- spring  applicationContext.xml
    @LoadBalanced //配置負載均衡實現RestTemplate
    @Bean
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
}

修改conroller:DeptConsumerController.java

//Ribbon:我們這裡的地址,應該是一個變數,通過服務名來訪問
//private static final String REST_URL_PREFIX = "http://localhost:8001";
private static final String REST_URL_PREFIX = "http://SPRINGCLOUD-PROVIDER-DEPT";

使用Ribbon實現負載均衡

流程圖:

1.新建兩個服務提供者Moudle:springcloud-provider-dept-8003、springcloud-provider-dept-8002

2.參照springcloud-provider-dept-8001 依次為另外兩個Moudle新增pom.xml依賴 、resourece下的mybatis和application.yml配置,Java程式碼

3.啟動所有服務測試(根據自身電腦配置決定啟動服務的個數),訪問http://eureka7001.com:7002/檢視結果

測試訪問http://localhost/consumer/dept/list 這時候隨機訪問的是服務提供者8003

再次訪問http://localhost/consumer/dept/list這時候隨機的是服務提供者8001

以上這種每次訪問http://localhost/consumer/dept/list隨機訪問叢集中某個服務提供者,這種情況叫做輪詢,輪詢演算法在SpringCloud中可以自定義。

如何切換或者自定義規則呢?

在springcloud-provider-dept-80模組下的ConfigBean中進行配置,切換使用不同的規則

@Configuration
public class ConfigBean {
    //@Configuration -- spring  applicationContext.xml
    /**
     * IRule:
     * RoundRobinRule 輪詢策略
     * RandomRule 隨機策略
     * AvailabilityFilteringRule : 會先過濾掉,跳閘,訪問故障的服務~,對剩下的進行輪詢~
     * RetryRule : 會先按照輪詢獲取服務~,如果服務獲取失敗,則會在指定的時間內進行,重試
     */
    @Bean
    public IRule myRule() {
        return new RandomRule();//使用隨機策略
        //return new RoundRobinRule();//使用輪詢策略
        //return new AvailabilityFilteringRule();//使用輪詢策略
        //return new RetryRule();//使用輪詢策略
    }
}

也可以自定義規則,在myRule包下自定義一個配置類MyRule.java,注意:該包不要和主啟動類所在的包同級,要跟啟動類所在包同級

MyRule.java

/**
 * @Description: 自定義規則
 */
@Configuration
public class MyRule {
    @Bean
    public IRule myRule(){
        return new MyRandomRule();//預設是輪詢RandomRule,現在自定義為自己的
    }
}

主啟動類開啟負載均衡並指定自定義的MyRule配置類

//Ribbon 和 Eureka 整合以後,客戶端可以直接呼叫,不用關心IP地址和埠號
@SpringBootApplication
@EnableEurekaClient
//在微服務啟動的時候就能載入自定義的Ribbon類(自定義的規則會覆蓋原有預設的規則)
@RibbonClient(name = "SPRINGCLOUD-PROVIDER-DEPT",configuration = MyRule.class)//開啟負載均衡,並指定自定義的規則
public class DeptConsumer_80 {
    public static void main(String[] args) {
        SpringApplication.run(DeptConsumer_80.class, args);
    }
}

自定義的規則(這裡我們參考Ribbon中預設的規則程式碼自己稍微改動):MyRandomRule.java

public class MyRandomRule extends AbstractLoadBalancerRule {
    /**
     * 每個服務訪問5次則換下一個服務(總共3個服務)
     * <p>
     * total=0,預設=0,如果=5,指向下一個服務節點
     * index=0,預設=0,如果total=5,index+1
     */
    private int total = 0;//被呼叫的次數
    private int currentIndex = 0;//當前是誰在提供服務
    //@edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE")
    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 = chooseRandomInt(serverCount);//生成區間隨機數
            //server = upList.get(index);//從或活著的服務中,隨機獲取一個
            //=====================自定義程式碼=========================
            if (total < 5) {
                server = upList.get(currentIndex);
                total++;
            } else {
                total = 0;
                currentIndex++;
                if (currentIndex > upList.size()) {
                    currentIndex = 0;
                }
                server = upList.get(currentIndex);//從活著的服務中,獲取指定的服務來進行操作
            }
            //======================================================
            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;
    }
    protected int chooseRandomInt(int serverCount) {
        return ThreadLocalRandom.current().nextInt(serverCount);
    }
    @Override
    public Server choose(Object key) {
        return choose(getLoadBalancer(), key);
    }
    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {
        // TODO Auto-generated method stub
    }
}