1. 程式人生 > 實用技巧 >【SpringCloud】Ribbon負載均衡呼叫

【SpringCloud】Ribbon負載均衡呼叫

Ribbon負載均衡呼叫

概述

是什麼

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

官網資料

https://github.com/Netflix/ribbon/wiki/Getting-Started

Ribbon目前也進入維護模式

未來替換方案
SpringCloud loadBalancer

能幹嘛

LB(負載均衡)

LB負載均衡(Load Balance)是什麼
簡單的說就是將使用者的請求平攤的分配到多個服務上,從而達到系統的HA (高可用)。
常見的負載均衡有軟體Nginx, LVS, 硬體F5等。

Ribbon本地負載均衡客戶端 VS Nginx服務端負載均衡區別
Nginx是伺服器負載均衡,客戶端所有請求都會交給nginx,然後由nginx實現轉發請求。即負載均衡是由服務端實現的。

Ribbon本地負載均衡,在呼叫微服務介面時候,會在註冊中心上獲取註冊資訊服務列表之後快取到VM本地,從而在本地實現RPC遠端服務呼叫技術。

集中式LB

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

程序內LB

程序內LB
將LB邏輯整合到消費方,消費方從服務註冊中心獲知有哪些地址可用,然後自己再從這些地址中選擇出一個合適的伺服器。
Ribbon就屬於程序內LB,它只是一個類庫, 集成於消費方程序,消費方通過它來獲取到服務提供方的地址。

前面我們講解過了80通過輪詢負載訪問8001/8002

一句話:負載均衡+RestTemplate呼叫

Ribbon負載均衡演示

架構說明


Ribbon在工作時分成兩步
第一步先選擇EurekaServer ,它優先選擇在同一個區域內負載較少的server.

第二步再根據使用者指定的策略,在從server取到的服務註冊列表中選擇一個地址。
其中Ribbon提供了多種策略:比如輪詢、隨機和根據響應時間加權。

總結: Ribbon其實就是一個軟負載均衡的客戶端元件,#他可以和其他所需請求的客戶端結合使用,和eureka結合只是其中一個例項。

POM


二說RestTemplate的使用

官網

getForObject方法/getForEntity方法

postForObject/postEntity

GET請求方法

POST請求方法

Ribbon核心元件IRule

IRule:根據特定演算法從服務列表中選取一個要訪問的服務

  • com.netflix.loadbalancer.RoundRobinRule:輪詢
  • com.netflix.loadbalancer.RandomRule:隨機
  • com.netflix.loadbalancer.RetryRule:先按照RoundRobinRule的策略獲取服務,如果獲取服務失敗則在指定時間內進行重試,獲取可用的服務
  • WeightedResponseTimeRule:對RoundRobinRule的擴充套件,響應速度越快的例項選擇權重越多大,越容易被選擇
  • BestAvailableRule:會先過濾掉由於多次訪問故障而處於斷路器跳閘狀態的服務,然後選擇一個併發量最小的服務
  • AvailabilityFilteringRule:先過濾掉故障例項,再選擇併發較小的例項
  • ZoneAvoidanceRule:預設規則,複合判斷server所在區域的效能和server的可用性選擇伺服器

如何替換

修改cloud-consumer-order80

注意配置細節

官方文件明確給出了警告:
這個自定義配置類不能放在@ComponentScan所掃描的當前包下以及子包下,否則我們自定義的這個配置類就會被所有的Ribbon客戶端所共享,達不到特殊化定製的目的了。


新建package

com.atguigu.myrule

上面包下新建MySelfRule規則類

package com.atguigu.myrule;

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RoundRobinRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 自定義負載均衡路由規則類
 *
 * @author zzyy
 * @date 2020/3/6 15:15
 **/
@Configuration
public class MySelfRule {

    @Bean
    public IRule myRule() {
        // 定義為隨機
        return new RoundRobinRule();
    }
}

主啟動類新增@RibbonClient

package com.atguigu.springcloud;

import com.atguigu.myrule.MySelfRule;
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;

/**
 * @author zzyy
 * @date 2020/02/18 17:20
 **/
@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE", configuration = MySelfRule.class)
public class OrderMain80 {
    public static void main(String[] args) {
        SpringApplication.run(OrderMain80.class, args);
    }
}

測試

http://localhost/consumer/payment/get/31

Ribbon負載均衡演算法

原理

負載均衡演算法: rest介面第幾次請求數%伺服器叢集總數量=實際呼叫伺服器位置下標,每次服務 重啟動後rest介面計數從1開始。

List instances = discoveryClient. getInstances("CLOUD PAYMENT- SERVICE");

如:
List [0] instances = 127.0.0.1:8002
List [1] instances = 127.0.0.1:8001

8001+8002組合成為叢集,它們共計2臺機器,叢集總數為2,按照輪詢演算法原理:

當總請求數為1時: 1 % 2 = 1 對應下標位置為1,則獲得服務地址為127.0.0.1:8001
當總請求數位2時: 2 % 2 = 0 對應下標位置為0,則獲得服務地址為127.0.0.1:8002
當總請求數位3時: 3 % 2 = 1 對應下標位置為1,則獲得服務地址為127.0.0.1:8001
當總請求數位4時: 4 % 2 = 0 對應下標位置為0,則獲得服務地址為127.0.0.1:8002
如此類推....

原始碼

手寫

自己試著寫一個本地負載均衡器試試

7001/7002叢集啟動

8001/8002微服務改造

controller

@GetMapping(value = "/payment/lb")
public String getPaymentLB() {
    return serverPort;
}

80訂單微服務改造

1.ApplicationContextBean去掉註解@LoadBalanced
2.LoadBalancer介面
public interface LoadBalancer {

    ServiceInstance instances(List<ServiceInstance> serviceInstances);

}

這個介面主要是為了獲取當前訪問的這個服務叢集中有多少臺伺服器例項

3.MyLB
@Component
public class MyLB implements LoadBalancer{

    private AtomicInteger atomicInteger = new AtomicInteger(0);

    public final int getAndIncrement() {
        int current;
        int next;
        do {
            current = this.atomicInteger.get();
            next = current >= 2147483647 ? 0 : current + 1;
        } while(!this.atomicInteger.compareAndSet(current,next));
        System.out.println("****第幾次訪問,次數next: " + next);
        return  next;
    }

    @Override
    public ServiceInstance instances(List< ServiceInstance > serviceInstances) {
        int index = getAndIncrement() % serviceInstances.size();
        return serviceInstances.get(index);
    }
}
4.OrderController

不變

5.測試

http://localhost/consumer/payment/lb

效果