SpringCloud Netfilx Ribbon負(fù)載均衡工具使用方法介紹
一、介紹
Spring Cloud Ribbon是一個(gè)基于HTTP和TCP的客戶端負(fù)載均衡工具,它基于Netflix Ribbon實(shí)現(xiàn)。通過(guò)Spring Cloud的封裝,可以讓我們輕松地將面向服務(wù)的REST模版請(qǐng)求自動(dòng)轉(zhuǎn)換成客戶端負(fù)載均衡的服務(wù)調(diào)用。Spring Cloud Ribbon雖然只是一個(gè)工具類框架,它不像服務(wù)注冊(cè)中心、配置中心、API網(wǎng)關(guān)那樣需要獨(dú)立部署,但是它幾乎存在于每一個(gè)Spring Cloud構(gòu)建的微服務(wù)和基礎(chǔ)設(shè)施中。因?yàn)槲⒎?wù)間的調(diào)用,API網(wǎng)關(guān)的請(qǐng)求轉(zhuǎn)發(fā)等內(nèi)容,實(shí)際上都是通過(guò)Ribbon來(lái)實(shí)現(xiàn)的,包括后續(xù)我們將要學(xué)習(xí)的OpenFeign,它也是基于Ribbon實(shí)現(xiàn)負(fù)載均衡的遠(yuǎn)程服務(wù)調(diào)用工具。所以,對(duì)Spring Cloud Ribbon的理解和使用,對(duì)于我們使用Spring Cloud來(lái)構(gòu)建微服務(wù)非常重要.
二、使用
@Service
public class ApplicationClientServiceImpl implements ApplicationClientService {
/**
* Ribbon提供的負(fù)載均衡器
*/
@Autowired
private LoadBalancerClient loadBalancerClient;
@Override
public String client() {
ServiceInstance si = loadBalancerClient.choose("application-service");
// 獲取Application Service IP。
System.out.println(si.getHost());
// 獲取Ip及端口。
System.out.println(si.getInstanceId());
// 端口
System.out.println(si.getPort());
// 應(yīng)用程序名 application-service
System.out.println(si.getServiceId());
// URI http://Application Service IP:端口
System.out.println(si.getUri().toString());
return null;
}
}三、SpringWeb之RestTemplate基于Http協(xié)議的遠(yuǎn)程訪問(wèn)
要想使用RestRemplate必須自己配置
package com.bjsxt.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class MyConfig {
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}(1)控制器
@RestController
public class ServerController {
@Value("${server.port}")
private int port;
/**
* 返回類型為集合,泛型為自定類型。
*/
@RequestMapping("/returnUsers")
public List<User> returnUsers(int nums){
List<User> result = new ArrayList<>();
for(int i = 0; i < nums; i++){
result.add(new User(100 + i, "姓名-" + i, 20+i));
}
return result;
}
/**
* 任意請(qǐng)求方式, 返回值類型是集合。相對(duì)復(fù)雜的Java類型。
* @return
*/
@RequestMapping("/returnList")
public List<String> returnList(int nums){
List<String> result = new ArrayList<>();
for(int i = 0; i < nums; i++){
result.add("返回結(jié)果 - " + i);
}
return result;
}
/**
* 任意請(qǐng)求,傳遞路徑地址參數(shù)
* @param name
* @param age
* @return
*/
@RequestMapping("/restfulParams/{name}/{age}")
public String restfulParams(@PathVariable("name") String name,
@PathVariable int age){
System.out.println("端口號(hào): " + port + ", 任意請(qǐng)求方式,restful參數(shù), name = " +
name + " ; age = " + age);
return "restful參數(shù), name = " + name + " ; age = " + age;
}
/**
* post請(qǐng)求,請(qǐng)求體傳遞參數(shù)。參數(shù)使用@RequestBody處理。
*/
@PostMapping("/postBodyParams")
public String postBodyParams(@RequestBody Map<String, String> params){
System.out.println("端口號(hào): " + port + " , post請(qǐng)求,有請(qǐng)求體參數(shù), params = " +
params);
return "post請(qǐng)求,請(qǐng)求體參數(shù), params = " + params;
}
/**
* post請(qǐng)求,有參數(shù)
*/
@PostMapping("/postWithParams")
public String postWithParams(String name, int age){
System.out.println("端口號(hào): " + port + " , post請(qǐng)求,有參數(shù), name = " +
name + " ; age = " + age);
return "post請(qǐng)求有參數(shù) : name = " +
name + " ; age = " + age;
}
/**
* post請(qǐng)求,沒(méi)有參數(shù)
*/
@PostMapping("/postNoParams")
public String postNoParams(){
System.out.println("端口號(hào): " + port + " , post請(qǐng)求,沒(méi)有參數(shù)");
return "post請(qǐng)求,沒(méi)有參數(shù)";
}
/**
* get請(qǐng)求,包含參數(shù)
*/
@GetMapping("/getWithParams")
public String getWithParams(String name, int age){
System.out.println("端口號(hào): " + port + " 。 get請(qǐng)求,有參數(shù), name = "
+ name + " ; age = " + age);
return "get請(qǐng)求,包含參數(shù) : name = " + name + " ; age = " + age;
}
/**
* get請(qǐng)求,沒(méi)有參數(shù)
* @return
*/
@GetMapping("/getNoParams")
public String getNoParams(){
System.out.println("端口號(hào):" + port + "。 get請(qǐng)求,無(wú)參數(shù)。");
return "get請(qǐng)求,無(wú)參數(shù)。";
}
}(2)無(wú)參數(shù)GET請(qǐng)求-getForObject
/**
* 發(fā)get請(qǐng)求。
* 沒(méi)有請(qǐng)求參數(shù)
*
* <T> T getForObject(String url, Class<T> returnValueType, Object... params)
* url - 要訪問(wèn)的具體地址
* returnValueType - 服務(wù)器返回的響應(yīng)體數(shù)據(jù)類型
* params - 可選的請(qǐng)求參數(shù)
* return - 響應(yīng)體中的具體內(nèi)容。
*/
@Test
public void testGetNoParams(){
// 定義要訪問(wèn)的地址是什么
String url = baseUrl + "/getNoParams";
// 發(fā)get請(qǐng)求。沒(méi)有參數(shù)
String result = restTemplate.getForObject(url, String.class);
System.out.println("服務(wù)器返回:" + result);
}(3)有參數(shù)GET請(qǐng)求-getForObject
/**
* get請(qǐng)求,有參數(shù)
* <T> T getForObject(String url, Class<T> returnValueType, Object... params)
* <T> T getForObject(String url, Class<T> returnValueType, Map params)
* 傳遞參數(shù),就是處理請(qǐng)求地址url,并傳遞需要的參數(shù)。
* 要傳遞參數(shù),則處理請(qǐng)求地址url。使用{名字}作為占位變量。傳遞參數(shù)的時(shí)候,
* 可以根據(jù)占位變量從左至右的順序,使用可變長(zhǎng)數(shù)組依次傳遞。
* 也可以根據(jù)占位變量名稱,做指定傳遞,使用map集合傳遞,map的key就是占位變量名,
* map的value,是要傳遞的請(qǐng)求參數(shù)。
*/
@Test
public void testGetWithParams(){
String url = baseUrl + "/getWithParams?name={x}&age={y}";
String result1 =
restTemplate.getForObject(url, String.class,
"張三", "20");
System.out.println("可變長(zhǎng)數(shù)組傳遞參數(shù),服務(wù)器返回:" + result1);
System.out.println("==========================================");
Map<String, Object> params = new HashMap<>();
params.put("x", "李四");
params.put("y", 25);
String result2 =
restTemplate.getForObject(url, String.class, params);
System.out.println("Map集合傳遞參數(shù),服務(wù)器返回:" + result2);
}(4)GET請(qǐng)求-getForEntity
/**
* 在RestTemplate中。除方法getForObject以外,還有g(shù)etForEntity。
* 方法除返回值類型,其他一致。
* <T> ResponseEntity<T> getForEntity(String url, Class<T> returnValueType,
* Object... params)
* ResponseEntity - 包含響應(yīng)頭和響應(yīng)體。
* 如果需要對(duì)響應(yīng)頭做特殊處理,使用getForEntity方法。
*/
@Test
public void testGetForEntity(){
String url = baseUrl + "/getNoParams";
ResponseEntity<String> entity =
restTemplate.getForEntity(url, String.class);
HttpHeaders headers = entity.getHeaders();
for(String headerName : headers.keySet()){
System.out.println("響應(yīng)頭: " + headerName + " = "
+ headers.get(headerName));
}
System.out.println("響應(yīng)狀態(tài)碼: " + entity.getStatusCodeValue());
System.out.println("響應(yīng)體數(shù)據(jù): " + entity.getBody());
}(5)無(wú)參數(shù)POST請(qǐng)求
/**
* post請(qǐng)求,沒(méi)有參數(shù)
* <T> T postForObject(String url, Object body, Class<T> returnBodyType,
* Object... params);
* <T> T postForObject(String url, Object body, Class<T> returnBodyType,
* Map params);
* body參數(shù) - 使用post請(qǐng)求方式發(fā)請(qǐng)求的時(shí)候,請(qǐng)求體是什么?
* 建議傳遞的對(duì)象類型是HttpEntity。
* 如果對(duì)請(qǐng)求體沒(méi)有要求,可以傳遞null。
*/
@Test
public void testPostNoParams(){
String url = baseUrl + "/postNoParams";
String result =
restTemplate.postForObject(url, null, String.class);
System.out.println("post請(qǐng)求,無(wú)參數(shù),服務(wù)器返回:" + result);
}(6)post請(qǐng)求路徑地址傳遞參數(shù)
/**
* post請(qǐng)求,有參數(shù)
* 1. 請(qǐng)求地址傳遞參數(shù)。 請(qǐng)求頭傳參。 傳參方式和get一樣。
* 2. 請(qǐng)求體表單參數(shù)。 請(qǐng)求體傳參。 需要提供請(qǐng)求體數(shù)據(jù)
*/
@Test
public void testPostWithParamsPath(){
String url = baseUrl + "/postWithParams?name={1}&age={2}";
String result =
restTemplate.postForObject(url, null, String.class,
"王五", 30);
System.out.println(result);
}(7)post請(qǐng)求表單傳遞參數(shù)
/**
* spring web在處理請(qǐng)求頭和請(qǐng)求體的時(shí)候,
* 需要HttpMessageConverter提供數(shù)據(jù)轉(zhuǎn)換處理。
* HttpMessageConverter是接口,由Spring WEB定義。
* 具體實(shí)現(xiàn),需要依賴不同的具體技術(shù)實(shí)現(xiàn)。
* 一般都使用jackson做實(shí)現(xiàn)。請(qǐng)求頭和體數(shù)據(jù),都是基于JSON轉(zhuǎn)換的。
*/
@Test
public void testPostWithParamsForm(){
String url = baseUrl + "/postWithParams";
// 創(chuàng)建請(qǐng)求體信息。
// 請(qǐng)求頭, 表單請(qǐng)求。 application/x-www-form-urlencoded
HttpHeaders headers = new HttpHeaders();
headers.add("content-type", "application/x-www-form-urlencoded");
// 表單
MultiValueMap<String, Object> form =
new LinkedMultiValueMap<>();
// add 提供一個(gè) 請(qǐng)求參數(shù) 名 = 值。
form.add("name", "尼古拉斯.趙四");
form.add("age", 40);
// put 提供 鍵值對(duì)
//form.put("name", Arrays.asList("尼古拉斯.趙四"));
//List<Object> ages = new ArrayList<>();
//ages.add(40);
//form.put("age", ages);
HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity<>(form, headers);
String result =
restTemplate.postForObject(url, entity, String.class);
System.out.println(result);
}(8)post請(qǐng)求請(qǐng)求體傳遞參數(shù)
/**
* post請(qǐng)求,有參數(shù)
* 使用請(qǐng)求體傳遞參數(shù)。@RequestBody處理請(qǐng)求參數(shù)。
* 1. 請(qǐng)求參數(shù)只能在請(qǐng)求體中,以字符串描述,且是一個(gè)完整的請(qǐng)求參數(shù)。此參數(shù)沒(méi)有名稱。如:JSON
* 2. 請(qǐng)求頭 content-type的設(shè)置,和具體的參數(shù)值的格式相關(guān),如: JSON對(duì)應(yīng)的content-type是application/json
* 如:xml字符串對(duì)應(yīng)的content-type是application/xml
* 3. 請(qǐng)求體傳遞的參數(shù),只能由唯一的一個(gè)完整參數(shù)??梢酝瑫r(shí)攜帶表單參數(shù)和地址欄參數(shù)。
*/
@Test
public void testPostBodyParams(){
String url = baseUrl + "/postBodyParams";
// 創(chuàng)建請(qǐng)求參數(shù), 使用JSON格式的字符串,描述一個(gè)Map集合。
String params = "{\"key1\":\"value1\", \"key2\":\"value2\"}";
// 創(chuàng)建請(qǐng)求時(shí),使用的請(qǐng)求體描述
HttpHeaders headers = new HttpHeaders();
headers.add("content-type", "application/json;charset=utf-8");
HttpEntity<String> entity = new HttpEntity<>(params, headers);
String result = restTemplate.postForObject(url, entity, String.class);
System.out.println("返回結(jié)果:" + result);
}
@Test
public void testPostBodyParams2(){
// 可以使用簡(jiǎn)單的處理方案,實(shí)現(xiàn)請(qǐng)求體傳遞JSON格式的數(shù)據(jù)
// 使用postForObject(),第二個(gè)參數(shù),直接傳遞一個(gè)Java對(duì)象,作為請(qǐng)求體中傳遞的參數(shù)。
// 參數(shù)沒(méi)有名字,由RestTemplate做數(shù)據(jù)轉(zhuǎn)換,默認(rèn)使用JSON格式字符串做轉(zhuǎn)換結(jié)果。
String url = baseUrl + "/postBodyParams";
// 創(chuàng)建Map類型的參數(shù)對(duì)象
Map<String, String> params = new HashMap<>();
params.put("name", "測(cè)試");
params.put("gender", "男");
// 請(qǐng)求服務(wù)器。 直接傳遞java對(duì)象,默認(rèn)請(qǐng)求頭content-type=application/json;charset=utf-8
String result = restTemplate.postForObject(url, params, String.class);
System.out.println("返回結(jié)果:" + result);
}(9)RestFUL傳遞參數(shù)
/**
* 使用get請(qǐng)求方式,傳遞restful參數(shù)
*/
@Test
public void testRestfulParams(){
String url = baseUrl + "/restfulParams/{name}/{age}";
// 訪問(wèn)
String result = restTemplate.getForObject(url, String.class, "restful", "15");
System.out.println(result);
}(10)Exchange通用處理方案(處理相對(duì)復(fù)雜的結(jié)果類型例如自定義類型數(shù)組)
/**
* RestTemplate類型中的通用方法,exchange??梢蕴峤蝗我夥绞降恼?qǐng)求。
* 且可以定義相對(duì)復(fù)雜的返回類型。如:帶有泛型要求的集合。
*
* <T> ResponseEntity<T> exchange(String url, HttpMethod requestMethod, HttpEntity http,
* Class<T> returnType, Object... params)
* <T> ResponseEntity<T> exchange(String url, HttpMethod requestMethod, HttpEntity http,
* Class<T> returnType, Map params)
* <T> ResponseEntity<T> exchange(String url, HttpMethod requestMethod, HttpEntity http,
* ParameterizedTypeReference<T> returnType, Object... params)
* <T> ResponseEntity<T> exchange(String url, HttpMethod requestMethod, HttpEntity http,
* ParameterizedTypeReference<T> returnType, Map... params)
*/
@Test
public void testExchangeMethod(){
String url = baseUrl + "/returnList?nums={1}";
// 相對(duì)復(fù)雜的返回結(jié)果類型描述對(duì)象
url = baseUrl + "/returnUsers?nums={1}";
ParameterizedTypeReference<List<User>> type =
new ParameterizedTypeReference<List<User>>() {};
// 訪問(wèn)遠(yuǎn)程
ResponseEntity<List<User>> entity =
restTemplate.exchange(url, HttpMethod.GET, null, type, 3);
List<User> body = entity.getBody();
System.out.println(body);
}四、調(diào)用Application Service集群
基于RestTemplate和Ribbon實(shí)現(xiàn)Application Client調(diào)用Application Service集群
(1)編寫配置類
@Configuration
public class AppClientConfiguration {
/**
* 創(chuàng)建RestTemplate對(duì)象的方法,增加注解
* LoadBalanced - 把Spring Cloud封裝的LoadBalancerClient于RestTemplate整合。
* 讓RestTemplate自帶負(fù)載均衡能力。僅在當(dāng)前的Ribbon環(huán)境中生效。
* 邏輯是:
* 請(qǐng)求 http://服務(wù)名稱/具體地址. RestTemplate解析請(qǐng)求地址。
* 把服務(wù)名稱解析出,作為參數(shù),調(diào)用LoadBalancerClient中的choose方法。
* 把返回的ServiceInstance.getUri().toASCIIString()作為服務(wù)器地址,拼接上
* 請(qǐng)求的具體地址。訪問(wèn)遠(yuǎn)程。
* @return
*/
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}(2)發(fā)起遠(yuǎn)程調(diào)用
@Service
public class ApplicationClientServiceImpl implements ApplicationClientService {
@Autowired
private RestTemplate restTemplate;
//http://服務(wù)名
private final String baseUrl = "http://eureka-client-app-service";
@Override
public String getNoParams() {
String url = baseUrl + "/getNoParams";
// 訪問(wèn)
String result = restTemplate.getForObject(url, String.class);
System.out.println(result);
return result;
}
}五、Ribbon負(fù)載均衡算法
Ribbon的負(fù)載均衡策略是通過(guò)不同的類型來(lái)實(shí)現(xiàn)的(都是IRule接口的實(shí)現(xiàn)),下表詳細(xì)介紹一些常用負(fù)載均衡策略及對(duì)應(yīng)的Ribbon策略類。
| 編號(hào) | 策略名稱 | 策略對(duì)應(yīng)的類名 | 實(shí)現(xiàn)原理 |
|---|---|---|---|
| 1 | 輪詢策略(默認(rèn)) | RoundRobinRule | 輪詢策略表示每次都按照順序取下一個(gè)application service,比如一共有5個(gè)application service,第1次取第1個(gè),第2次取第2個(gè),第3次取第3個(gè),以此類推 |
| 2 | 權(quán)重輪詢策略(常用,中小型項(xiàng)目使用) | WeightedResponseTimeRule | 1.根據(jù)每個(gè)application service的響應(yīng)時(shí)間分配一個(gè)權(quán)重,響應(yīng)時(shí)間越長(zhǎng),權(quán)重越小,被選中的可能性越低。 2.原理:一開始為輪詢策略,并開啟一個(gè)計(jì)時(shí)器,每30秒收集一次每個(gè)application service的平均響應(yīng)時(shí)間,當(dāng)信息足夠時(shí),給每個(gè)application service附上一個(gè)權(quán)重,并按權(quán)重隨機(jī)選擇application service,權(quán)重越高的application service會(huì)被高概率選中。 |
| 3 | 隨機(jī)策略(不推薦,測(cè)試使用,開發(fā)使用) | RandomRule | 從application service列表中隨機(jī)選擇一個(gè) |
| 4 | 最少并發(fā)數(shù)策略(應(yīng)用在硬件軟件環(huán)境一致的情況下,中小型項(xiàng)目使用) | BestAvailableRule | 選擇正在請(qǐng)求中的并發(fā)數(shù)最小的application service,除非這個(gè)application service在熔斷中。 |
| 5 | 重試策略。在“選定的負(fù)載均衡策略”基礎(chǔ)上進(jìn)行重試機(jī)制 | RetryRule | 1.“選定的負(fù)載均衡策略”這個(gè)策略是輪詢策略RoundRobinRule 2.該重試策略先設(shè)定一個(gè)閾值時(shí)間段,如果在這個(gè)閾值時(shí)間段內(nèi)當(dāng)選擇application service不成功,則一直嘗試采用“選定的負(fù)載均衡策略:輪詢策略”最后選擇一個(gè)可用的application service |
| 6 | 可用性敏感策略(一般在同區(qū)域內(nèi)服務(wù)集群環(huán)境中使用) | AvailabilityFilteringRule | 過(guò)濾性能差的application service,有2種: 第一種:過(guò)濾掉在eureka中處于一直連接失敗application service 第二種:過(guò)濾掉高并發(fā)的application service |
| 7 | 區(qū)域敏感性策略(應(yīng)用在大型的,物理隔離分布式環(huán)境中) | ZoneAvoidanceRule | 1.以一個(gè)區(qū)域?yàn)閱挝豢疾炜捎眯?,?duì)于不可用的區(qū)域整個(gè)丟棄,從剩下區(qū)域中選可用的application service 2.如果這個(gè)ip區(qū)域內(nèi)有一個(gè)或多個(gè)實(shí)例不可達(dá)或響應(yīng)變慢,都會(huì)降低該ip區(qū)域內(nèi)其他ip被選中的權(quán)重。 |
指定負(fù)載均衡策略,新增Bean對(duì)象管理方法
@Bean
public IRule iRule(){
return new RandomRule();
}到此這篇關(guān)于SpringCloud Netfilx Ribbon負(fù)載均衡工具使用方法介紹的文章就介紹到這了,更多相關(guān)SpringCloud Netfilx Ribbon內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
spring boot項(xiàng)目打包成war在tomcat運(yùn)行的全步驟
這篇文章主要給大家介紹了關(guān)于spring boot項(xiàng)目打包成war在tomcat運(yùn)行的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用spring boot具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
一個(gè)簡(jiǎn)陋的java圖書管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了一個(gè)簡(jiǎn)陋的java圖書管理系統(tǒng),簡(jiǎn)單的實(shí)現(xiàn)功能測(cè)試,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-07-07
JAVA中JSONObject對(duì)象和Map對(duì)象之間的相互轉(zhuǎn)換
這篇文章主要介紹了JAVA中JSONObject對(duì)象和Map對(duì)象之間的相互轉(zhuǎn)換,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01
Java解析zip文件,并識(shí)別壓縮包里面的文件轉(zhuǎn)換成可操作的IO流方式
這篇文章主要介紹了Java解析zip文件,并識(shí)別壓縮包里面的文件轉(zhuǎn)換成可操作的IO流方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08

