服務端的負載均衡(Nginx)
我們用戶服務發送請求首先打到Ng上,然後Ng根據負載均衡算法進行選擇一個服務調用,而我們的Ng部署在服務器上的,所以Ng又稱爲服務端的負載均衡
客戶端負載均衡(ribbon)
spring cloud ribbon是 基於NetFilix ribbon 實現的一套客戶端的負載均衡工具,Ribbon客戶端組件提供一系列的完善的配置,如超時,重試等。通過Load Balancer(LB)獲取到服務提供的所有機器實例,Ribbon 會自動基於某種規則(輪詢,隨機)去調用這些服務。
Ribbon也可以實現我們自己的負載均衡算法。
內置的負載均衡算法
ZoneAvoidanceRule 默認規則,複合判斷server所在區域的性能和server的可用性選擇服務器
RandomRule(隨機選擇一個Server)
RoundRobinRule 輪詢選擇, 輪詢index,選擇index對應位置的Server
RetryRule 先按照RoundRobinRule的策略獲取服務,如果獲取服務失敗則在指定時間內會進行重試,獲取可用的服務
AvailabilityFilteringRule 會先過濾掉由於多次訪問故障而處於斷路器跳閘狀態的服務, 還有併發的連接數量超過閾值的服務,然後對剩餘的服務列表按照輪詢策略進行訪問
BestAvailableRule 會先過濾掉由於多次訪問故障而處於斷路器跳閘狀態的服務,然後選擇一個併發量最小的服務
WeightedResponseTimeRule 根據平均響應時間計算所有服務的權重,響應時間越快服務權重越大被選中的概率越高。
剛啓動時如果統計信息不足,則使用RoundRobinRule策略,等統計信息足夠, 會切換到WeightedResponseTimeRule
自定義的負載均衡算法(隨機)
寫一個TestRestTemplate類繼承RestTemplate,從寫doExucute()方法。
/**
* 根據RestTemplate特性自己改造
*/
@Slf4j
public class TestRestTemplate extends RestTemplate {
private DiscoveryClient discoveryClient;
public TestRestTemplate (DiscoveryClient discoveryClient) {
this.discoveryClient = discoveryClient;
}
protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
@Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
Assert.notNull(url, "URI is required");
Assert.notNull(method, "HttpMethod is required");
ClientHttpResponse response = null;
try {
log.info("請求的url路徑爲:{}",url);
//把服務名 替換成我們的IP
url = replaceUrl(url);
log.info("替換後的路徑:{}",url);
ClientHttpRequest request = createRequest(url, method);
if (requestCallback != null) {
requestCallback.doWithRequest(request);
}
response = request.execute();
handleResponse(url, method, response);
return (responseExtractor != null ? responseExtractor.extractData(response) : null);
}
catch (IOException ex) {
String resource = url.toString();
String query = url.getRawQuery();
resource = (query != null ? resource.substring(0, resource.indexOf('?')) : resource);
throw new ResourceAccessException("I/O error on " + method.name() +
" request for \"" + resource + "\": " + ex.getMessage(), ex);
} finally {
if (response != null) {
response.close();
}
}
}
/**
* 方法實現說明:把微服務名稱 去註冊中心拉取對應IP進行調用
* http://product-center/selectProductInfoById/1
*/
private URI replaceUrl(URI url){
//1:從URI中解析調用的調用的serviceName=product-center
String serviceName = url.getHost();
log.info("調用微服務的名稱:{}",serviceName);
//2:解析我們的請求路徑 reqPath= /selectProductInfoById/1
String reqPath = url.getPath();
log.info("請求path:{}",reqPath);
//通過微服務的名稱去nacos服務端獲取 對應的實例列表
List<ServiceInstance> serviceInstanceList = discoveryClient.getInstances(serviceName);
if(serviceInstanceList.isEmpty()) {
throw new RuntimeException("沒有可用的微服務實例列表:"+serviceName);
}
String serviceIp = chooseTargetIp(serviceInstanceList);
String source = serviceIp+reqPath;
try {
return new URI(source);
} catch (URISyntaxException e) {
log.error("根據source:{}構建URI異常",source);
}
return url;
}
/**
* 方法實現說明:從服務列表中 隨機選舉一個ip
*/
private String chooseTargetIp(List<ServiceInstance> serviceInstanceList) {
//採取隨機的獲取一個
Random random = new Random();
Integer randomIndex = random.nextInt(serviceInstanceList.size());
String serviceIp = serviceInstanceList.get(randomIndex).getUri().toString();
log.info("隨機選舉的服務IP:{}",serviceIp);
return serviceIp;
}
}
個人公衆號,日常分享一個知識點,每天進步一點點,面試不慌: