Spring Cloud中的Ribbon是什么?
我們通常說的負載均衡是指將一個請求均勻地分攤到不同的節點單元上執行,負載均和分為硬件負載均衡和軟件負載均衡:
硬件負載均衡:比如 F5、深信服、Array 等;
軟件負載均衡:比如 Nginx、LVS、HAProxy 等;
硬件負載均衡或是軟件負載均衡,他們都會維護一個可用的服務端清單,通過心跳檢測來剔除故障的服務端節點以保證清單中都是可以正常訪問的服務端節點。當客戶端發送請求到負載均衡設備的時候,該設備按某種算法(比如輪詢、權重、 最小連接數等)從維護的可用服務端清單中取出一臺服務端的地址,然后進行轉發。
Ribbon是Netflix發布的開源項目,主要功能是提供客戶端的軟件負載均衡算法,是一個基于HTTP和TCP的客戶端負載均衡工具。
Spring Cloud對Ribbon做了二次封裝,可以讓我們使用RestTemplate的服務請求,自動轉換成客戶端負載均衡的服務調用。
Ribbon支持多種負載均衡算法,還支持自定義的負載均衡算法。
Ribbon只是一個工具類框架,比較小巧,Spring Cloud對它封裝后使用也非常方便,它不像服務注冊中心、配置中心、API網關那樣需要獨立部署,Ribbon只需要在代碼直接使用即可;
Ribbon 與 Nginx 的區別
Ribbon是客戶端的負載均衡工具,而客戶端負載均衡和服務端負載均衡最大的區別在于服務清單所存儲的位置不同,在客戶端負載均衡中,所有客戶端節點下的服務端清單,需要自己從服務注冊中心上獲取,比如Eureka服務注冊中心。同服務端負載均衡的架構類似,在客戶端負載均衡中也需要心跳去維護服務端清單的健康性,只是這個步驟需要與服務注冊中心配合完成。在Spring Cloud中,由于Spring Cloud對Ribbon做了二次封裝,所以默認會創建針對Ribbon的自動化整合配置;
在Spring Cloud中,Ribbon主要與RestTemplate對象配合起來使用,Ribbon會自動化配置RestTemplate對象,通過@LoadBalanced開啟RestTemplate對象調用時的負載均衡。
由于Spring Cloud Ribbon的封裝, 我們在微服務架構中使用客戶端負載均衡調用非常簡單, 只需要如下兩步:
1、啟動多個服務提供者實例并注冊到一個服務注冊中心或是服務注冊中心集群。
2、服務消費者通過被@LoadBalanced注解修飾過的RestTemplate來調用服務提供者。
這樣,我們就可以實現服務提供者的高可用以及服務消費者的負載均衡調用。
Ribbon的負載均衡策略是由IRule接口定義, 該接口由如下實現:
RandomRule | 隨機 |
RoundRobinRule | 輪詢 |
AvailabilityFilteringRule | 先過濾掉由于多次訪問故障的服務,以及并發連接數超過閾值的服務,然后對剩下的服務按照輪詢策略進行訪問; |
WeightedResponseTimeRule | 根據平均響應時間計算所有服務的權重,響應時間越快服務權重就越大被選中的概率即越高,如果服務剛啟動時統計信息不足,則使用RoundRobinRule策略,待統計信息足夠會切換到該WeightedResponseTimeRule策略; |
RetryRule | 先按照RoundRobinRule策略分發,如果分發到的服務不能訪問,則在指定時間內進行重試,分發其他可用的服務; |
BestAvailableRule | 先過濾掉由于多次訪問故障的服務,然后選擇一個并發量最小的服務; |
ZoneAvoidanceRule | 綜合判斷服務節點所在區域的性能和服務節點的可用性,來決定選擇哪個服務; |
當我們從服務消費端去調用服務提供者的服務的時候,使用了一個極其方便的對象叫RestTemplate,當時我們只使用了RestTemplate中最簡單的一個功能getForEntity發起了一個get請求去調用服務端的數據,同時,我們還通過配置@LoadBalanced注解開啟客戶端負載均衡,RestTemplate的功能非常強大,那么接下來我們就來詳細的看一下RestTemplate中幾種常見請求方法的使用。
在日常操作中,基于Rest的方式通常是四種情況,它們分表是:
GET請求 --查詢數據
POST請求 –添加數據
PUT請求 – 修改數據
DELETE請求 –刪除數據
下面我們逐一解讀。
有兩種方式:
第一種:getForEntity
該方法返回一個ResponseEntity對象,ResponseEntity是Spring對HTTP請求響應的封裝,包括了幾個重要的元素,比如響應碼、contentType、contentLength、響應消息體等;
ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://01-SPRINGCLOUD-SERVICE-PROVIDER/service/hello", String.class);
String body = responseEntity.getBody();
HttpStatus statusCode = responseEntity.getStatusCode();
int statusCodeValue = responseEntity.getStatusCodeValue();
HttpHeaders headers = responseEntity.getHeaders();
System.out.println(body);
System.out.println(statusCode);
System.out.println(statusCodeValue);
System.out.println(headers);
以上代碼:
getForEntity方法第一個參數為要調用的服務的地址,即服務提供者提供的http://01-SPRINGCLOUD-SERVICE-PROVIDER/service/hello接口地址,注意這里是通過服務名調用而不是服務地址,如果改為服務地址就無法使用Ribbon實現客戶端負載均衡了。
getForEntity方法第二個參數String.class表示希望返回的body類型是String類型,如果希望返回一個對象,也是可以的,比如User對象;
另外兩個重載方法:
public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables) throws RestClientException
比如:
restTemplate.getForEntity("http://01-SPRINGCLOUD-SERVICE-PROVIDER/service/hello?id={1}&name={2}", String.class, "{1, '張無忌'}").getBody();
public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException
比如:
Map<String, Object> paramMap = new ConcurrentHashMap<>();
paramMap.put("id", 1);
paramMap.put("name", "張無忌");
restTemplate.getForEntity("http://01-SPRINGCLOUD-SERVICE-PROVIDER/service/hello?id={id}&name={name}", String.class, paramMap).getBody();
第二種:getForObject()
與getForEntity使用類似,只不過getForObject是在getForEntity基礎上進行了再次封裝,可以將http的響應體body信息轉化成指定的對象,方便我們的代碼開發;
當你不需要返回響應中的其他信息,只需要body體信息的時候,可以使用這個更方便;
它也有兩個重載的方法,和getForEntity相似;
<T> T getForObject(URI url, Class<T> responseType) throws RestClientException;
<T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException;
<T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException;
RestTemplate的POST請求:
Post與Get請求非常類似:
restTemplate.postForObject()
restTemplate.postForEntity()
restTemplate.postForLocation()
RestTemplate的PUT請求:
restTemplate.put();
restTemplate.delete();