SpringCloud 服務負載均衡和呼叫 Ribbon、OpenFeign

發哥講發表於2020-09-04

1、Ribbon

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

目前Ribbon和Eureka進入到維護模式。停更不停用

負載均衡:

LB負載均衡(Load Balance)是什麼?
簡單的說就是將使用者的請求平攤的分配到多個服務上,從而達到系統的HA(高可用)。1
常見的負載均衡有軟體Nginx,LVS,硬體F5等。
Ribbon本地負載均衡客戶端VS Nginx服務端負載均衡區別
Nginx是伺服器負載均衡,客戶端所有請求都會交給nginx,然後由nginx實現轉發請求。即負載均衡是由服務端實現的。
Ribbon本地負載均衡,在呼叫微服務介面時候,會在註冊中心上獲取註冊資訊服務列表之後快取到VM本地,從而在本地實現RPC遠
程服務呼叫技術。

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

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

負載均衡 +RestTemplate 配合使用

架構

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

image-20200822132451805

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

pom 依賴 
spring-cloud-starter-netflix-eureka-client
自身 就整合了 ribbon 。

RestTemplate說明:

image-20200822133134902

Ribbon負載均衡規則

繼承結構

image-20200822134205708

自帶7種:

image-20200822134245453

如何使用?負載均衡策略替換。

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

增加rule配置類

包路徑: com.fage.rules
    
@Configuration
public class MyRibbonRule {
    @Bean
    public IRule randomRule() {
        return new RandomRule();
    }

}

boot啟動類增加註解

@RibbonClient(name = "cloud-payment-service", configuration = {MyRibbonRule.class})

負載均衡輪詢演算法原理

負載均衡演算法: rest介面第幾次請求數%伺服器叢集總數量=實際呼叫伺服器位置下標,每次服務重啟動後rest介面計數從1開始。
List instances = discoveryClient.getlnstances(CLOUD-PAYMENT-SERVICE");
如:List [o] 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=O對應下標位置為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
如此類推......

原始碼:

從0開始取餘獲取提供者服務。

內部使用cas+自旋鎖。

手寫一個負載均衡演算法,實現輪詢

1、服務提供者增加介面

    @GetMapping("/payment/loadBalanced")
    public CommonResult<Object> getLoadBalanced() {
        return new CommonResult<>(200, "呼叫成功", port);
    }

2、服務消費者改造

將 @LoadBalanced 註解去掉

增加MyLoadBalanced介面,只有一個方法ServiceInstance instance(List serviceInstances);用於得到當前演算法後要使用的例項物件。

實現類MyLib原始碼如下:

@Component
@Slf4j
public class MyLib implements MyLoadBalanced {

    private final AtomicInteger nextServerCyclicCounter = new AtomicInteger(0);

    public final int getAndIncrement() {
        // 方案 1 cas 
//        int current;
//        int next;
//        do {
//            current = this.nextServerCyclicCounter.get();
//            next = current >= 2147483647 ? 0 : current + 1;
//        } while (!this.nextServerCyclicCounter.compareAndSet(current, next));
//        log.info("**********第幾次訪問,次數next:" + next);
//        return next;

        // 方案 2 JUC 提供的 自增方法 
        log.info("**********第幾次訪問,次數next:" + nextServerCyclicCounter.getAndIncrement());
        return nextServerCyclicCounter.get();
    }

    /**
     * 負載均衡演算法:rest介面第幾次請求書 % 伺服器叢集總數量 = 實際呼叫伺服器位置下標,每次服務重啟動後rest介面計數從1開始
     *
     * @param serviceInstances 叢集中的 服務 例項
     * @return 叢集中的一個例項
     */
    @Override
    public ServiceInstance instance(List<ServiceInstance> serviceInstances) {
        return serviceInstances.get(getAndIncrement() % serviceInstances.size());
    }
}

3、改造消費者呼叫方法

    @GetMapping(value = "/consumer/payment/loadBalanced")
    public CommonResult getLoadBalanced() {
        ServiceInstance instance = myLoadBalanced.instance(discoveryClient.getInstances(PAYMENT_URL.split("//")[1]));
        return restTemplate.getForObject(instance.getUri()+"/payment/loadBalanced", CommonResult.class);
    }

2、OpenFeign

只需要一個介面並在介面上新增註解即可。

feign不再更新,直接學習openFeign。都是用於負載均衡。

Feign是一個宣告式WebService客戶端。使用Feign能讓編寫Web Service客戶端更加簡單。
它的使用方法是定義一個服務介面然後在上面新增註解。Feign也支援可拔括式的編碼羆和解碼器。Spring Cloud對Feign進行了封裝,使其支援了Spring MVC標準註解和HttpMessageConverters。Feign和ribbon組合使用以支援負載均衡。

Feign能幹什麼

Feign旨在使編寫Java Http客戶端變得更容易。
前面在使用Ribbon+RestTemplate時,利用RestTemplate對http請求的封裝處理,形成了一套模版化的呼叫方法。但是在實際開發
中,由於對服務依賴的呼叫可能不止一處,往往一個介面會被多處呼叫,所以通常都會針對每個微服務自行封裝一些客戶端類來包裝
這些依賴服務的呼叫。所以,Feign在此基礎上做了進一步封裝,由他來幫助我們定義和實現依賴服務介面的定義。在Feign的實現下
,我們只需建立一個介面並使用註解的方式來配置它(以前是Dao介面上面標註Mapper註解,現在是一個微服務介面上面標註一個
Feign註解即可),即可完成對服務提供方的介面繫結,簡化了使用Spring cloud Ribbon時,自動封裝服務呼叫客戶端的開發量。

Feign整合了Ribbon

利用Ribbon維護了Payment的服務列表資訊,並且通過輪詢實現了客戶端的負載均衡。而與Ribbon不同的是,通過feign只需要定義
服務繫結介面且以宣告式的方法,優雅而簡單的實現了服務呼叫

Feign和openFeign的區別

image-20200825164601061

搭建openFeign模組 cloud-consumer-openfeign-order80

image-20200825170608310

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

啟動類增加註解@EnableFeignClients

增加feign介面

@Component
@FeignClient(value = "cloud-payment-service")
public interface OpenFeignService {
    @GetMapping("/payment/loadBalanced")
    public CommonResult<Object> getLoadBalanced();
}

增加controller

@RestController
@Slf4j
public class OrderController implements OpenFeignService {
    @Resource
    OpenFeignService openFeignService;

    @Override
    @GetMapping("/consumer/payment/loadBalanced")
    public CommonResult<Object> getLoadBalanced() {
        return openFeignService.getLoadBalanced();
    }
}

註冊中心使用eureka。跟之前一樣。

OpenFeign超時控制

OpenFeign預設等待時間為1ms。

設定超時時間

image-20200825172459483

###設定 超時時間 方式 1
# 設定 feign客戶端超時時間 ( openFeign 預設支援 ribbon )
ribbon:
  # 指的是建立連線所用的時間,適用於網路狀況正常的情況下,倆端連線所用的時間 單位是秒
  ReadTimeout: 6000
  # 指的是建立連線後從伺服器讀取到可用資源的時間
  ConnectTimeout: 5000
  
  
###設定 超時時間 方式 2
#feign:
#  client:
#    config:
#      default:
#        connectTimeout: 5000
#        readTimeout: 6000
#        loggerLevel: full  

OpenFeign日誌增強

Feign提供了日誌列印功能,我們可以通過配置來調整日誌級別,從而瞭解Feign中Http請求的細節。
說白了就是對Feign介面的呼叫情況進行監控和輸出

日誌級別:

NONE:預設的,不顯示任何日誌;
BASIC:僅記錄請求方法、URL、響應狀態碼及執行時間;
HEADERS:除了BASIC中定義的資訊之外,還有請求和響應的頭資訊;
FULL:除HEADERS中定義的資訊之外,還有請求和響應的正文及後設資料。

配置日誌bean:

    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }

yml開啟日誌

image-20200825174833260

# 設定 feign客戶端超時時間 ( openFeign 預設支援 ribbon )
ribbon:
  # 指的是簡歷連線所用的時間,適用於網路狀況正常的情況下,倆端連線所用的時間 單位是秒
  ReadTimeout: 8000
  # 指的是建立連線後從伺服器讀取到可用資源的時間
  ConnectTimeout: 6000

# 開啟 feign 日誌列印
logging:
  level:
    ## feign 日誌 以什麼級別 監控那個介面
    com.fage.springcloud.feign.OpenFeignService: debug

結果:

2020-08-25 17:55:26.667 DEBUG 56624 --- [p-nio-80-exec-2] c.f.springcloud.feign.OpenFeignService   : [OpenFeignService#getLoadBalanced] ---> GET http://cloud-payment-service/payment/loadBalanced HTTP/1.1
2020-08-25 17:55:26.667 DEBUG 56624 --- [p-nio-80-exec-2] c.f.springcloud.feign.OpenFeignService   : [OpenFeignService#getLoadBalanced] ---> END HTTP (0-byte body)
2020-08-25 17:55:26.682 DEBUG 56624 --- [p-nio-80-exec-2] c.f.springcloud.feign.OpenFeignService   : [OpenFeignService#getLoadBalanced] <--- HTTP/1.1 200 (14ms)
2020-08-25 17:55:26.682 DEBUG 56624 --- [p-nio-80-exec-2] c.f.springcloud.feign.OpenFeignService   : [OpenFeignService#getLoadBalanced] connection: keep-alive
2020-08-25 17:55:26.682 DEBUG 56624 --- [p-nio-80-exec-2] c.f.springcloud.feign.OpenFeignService   : [OpenFeignService#getLoadBalanced] content-type: application/json
2020-08-25 17:55:26.682 DEBUG 56624 --- [p-nio-80-exec-2] c.f.springcloud.feign.OpenFeignService   : [OpenFeignService#getLoadBalanced] date: Tue, 25 Aug 2020 09:55:26 GMT
2020-08-25 17:55:26.682 DEBUG 56624 --- [p-nio-80-exec-2] c.f.springcloud.feign.OpenFeignService   : [OpenFeignService#getLoadBalanced] keep-alive: timeout=60
2020-08-25 17:55:26.682 DEBUG 56624 --- [p-nio-80-exec-2] c.f.springcloud.feign.OpenFeignService   : [OpenFeignService#getLoadBalanced] transfer-encoding: chunked
2020-08-25 17:55:26.682 DEBUG 56624 --- [p-nio-80-exec-2] c.f.springcloud.feign.OpenFeignService   : [OpenFeignService#getLoadBalanced] 
2020-08-25 17:55:26.683 DEBUG 56624 --- [p-nio-80-exec-2] c.f.springcloud.feign.OpenFeignService   : [OpenFeignService#getLoadBalanced] {"code":200,"message":"呼叫成功","data":"8001"}
2020-08-25 17:55:26.683 DEBUG 56624 --- [p-nio-80-exec-2] c.f.springcloud.feign.OpenFeignService   : [OpenFeignService#getLoadBalanced] <--- END HTTP (51-byte body)

公眾號發哥講

這是一個稍偏基礎和偏技術的公眾號,甚至其中包括一些可能閱讀量很低的包含程式碼的技術文,不知道你是不是喜歡,期待你的關注。

程式碼分享

https://gitee.com/naimaohome

微信公眾號 點選關於我,加入QQ群,即可獲取到程式碼以及高階進階視訊和電子書!!

img

如果你覺得文章還不錯,就請點選右上角選擇傳送給朋友或者轉發到朋友圈~

● 掃碼關注我們

據說看到好文章不推薦的人,伺服器容易當機!

本文版權歸 發哥講部落格園 共有,原創文章,未經允許不得轉載,否則保留追究法律責任的權利。

相關文章