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