目錄
1. 什么是 LoadBalancer ?
2. 負(fù)載均衡策略的分類
2.1 常見的負(fù)載均衡策略
3. 為什么要學(xué)習(xí) Spring Cloud Balancer ?
4. Spring Cloud LoadBalancer 內(nèi)置的兩種負(fù)載均衡策略
4.1 輪詢負(fù)載均衡策略(默認(rèn)的)
4.2 隨機(jī)負(fù)載均衡策略
4.2.1 創(chuàng)建隨機(jī)負(fù)載均衡策略
?4.2.2?設(shè)置隨機(jī)負(fù)載均衡策略
5. Nacos 權(quán)重負(fù)載均衡器
5.1 創(chuàng)建 Nacos 負(fù)載均衡器
5.2?設(shè)置 Nacos 負(fù)載均衡器
6. 自定義負(fù)載均衡器
6.1?創(chuàng)建自定義負(fù)載均衡器
6.2?封裝自定義負(fù)載均衡器
6.3?為服務(wù)設(shè)置自定義負(fù)載均衡策器
7.? Spring Cloud LoadBalancer 中的緩存機(jī)制
7.1 Spring Cloud LoadBalancer 中緩存機(jī)制的一些特性
7.2 關(guān)閉緩存
1. 什么是 LoadBalancer ?
LoadBalancer(負(fù)載均衡器)是一種用來分發(fā)網(wǎng)絡(luò)或應(yīng)用程序流量到多個服務(wù)器的技術(shù)。它可以防止任何單一服務(wù)的過載,通過分散負(fù)載來保持整個系統(tǒng)的平穩(wěn)運(yùn)行,保證系統(tǒng)的高可用性和可靠性。
2. 負(fù)載均衡策略的分類
負(fù)載均衡策略大體上分為兩類:服務(wù)端的負(fù)載均衡和客戶端的負(fù)載均衡
① 服務(wù)端負(fù)載均衡 (如 Nginx、F5)
請求先到達(dá)一個中介(如負(fù)載均衡器設(shè)備或者服務(wù),例如Nginx),由這個中介根據(jù)配置的策略將請求分發(fā)到后端的多個服務(wù)器中。它對客戶端是透明的,即客戶端不需要知道有多少服務(wù)器以及它們的存在。
② 客戶端負(fù)載均衡 (如 Ribbon、Spring Cloud LoadBalancer)
請求的分配邏輯由客戶端持有,客戶端直接決定將請求發(fā)送到哪一個服務(wù)器。也就是說在客戶端負(fù)載均衡中,客戶端通常具備一份服務(wù)列表,它知道每個服務(wù)的健康狀況,基于這些信息和負(fù)載均衡策略,客戶端會選擇一個最適合的服務(wù)去發(fā)送請求。
服務(wù)端負(fù)載均衡和客戶端負(fù)載均衡策略有什么區(qū)別 ?
它倆的區(qū)別主要在靈活性和性能兩方面(結(jié)合上面兩幅圖來理解):
1. 靈活性
- 客戶端負(fù)載均衡更加靈活,它可以針對每一個請求,每一個 service 做單獨的負(fù)載均衡配置。
2. 性能
- 客戶端負(fù)載均衡性能相對來說更好一點,因為服務(wù)端負(fù)載均衡中,當(dāng)請求來了之后,它得先去到服務(wù)端的負(fù)載均衡,然后服務(wù)端的負(fù)載均衡再將請求發(fā)送給對應(yīng)的服務(wù)器,整個過程發(fā)送了兩次請求,而客戶端負(fù)載均衡只需要發(fā)送一次請求。
- 其次,服務(wù)端負(fù)載均衡中,客戶端的請求都先打到了中心節(jié)點上,這個流量是很大的,所以服務(wù)端的負(fù)載均衡器,它的壓力相對來說就比較大,那么性能就不可能比客戶端負(fù)載均衡高。
- 反觀客戶端負(fù)載均衡,它就沒有所謂的中心節(jié)點,它將集中的壓力給釋放了,因為客戶端有成千上萬個,它可以讓每個客戶端去調(diào)用自己的負(fù)載均衡器,而不是讓成千上萬個客戶端去調(diào)用一個負(fù)載均衡器。
【擴(kuò)充知識】?
如果將負(fù)載均衡器視為代理,那么服務(wù)端負(fù)載均衡可以視作是反向代理的一種形式,因為它接收客戶端請求后再決定將其分配給哪一個服務(wù)器;而客戶端負(fù)載均衡則可以看作具有正向代理的性質(zhì),因為客戶端知道要聯(lián)系的服務(wù)列表,并直接向選定的服務(wù)器發(fā)送請求。
正向代理:正向代理類似于一個中間人,代表客戶端去請求服務(wù)??蛻舳吮仨氁渲么?,因此客戶端是知道代理的存在的。正向代理隱藏了客戶端的信息,服務(wù)器不知道真正的請求者是誰。
反向代理:反向代理則是代表服務(wù)器接收客戶端的請求??蛻舳送ǔ2恢篮竺嬗卸嗌俜?wù)器,也不需要知道。反向代理隱藏了服務(wù)端的信息,客戶端只與反向代理交互,像Nginx這樣的服務(wù)器就是一個反向代理的例子。
2.1 常見的負(fù)載均衡策略
常見的負(fù)載均衡策略有以下幾種:
- 輪詢:按順序分配,每個服務(wù)器輪流接收一個連接。
- 隨機(jī)選擇:隨機(jī)挑選服務(wù)器,分散負(fù)載。
- 最少連接:選擇當(dāng)前連接數(shù)最少的服務(wù)器。
- IP哈希:根據(jù)用戶IP分配,相同IP的請求總是發(fā)給同一服務(wù)器。
- 加權(quán)輪詢:類似輪詢,但服務(wù)器根據(jù)權(quán)重獲取更多或更少請求。
- 加權(quán)隨機(jī)選擇:權(quán)重高的服務(wù)器有更高幾率被選中。
- 最短響應(yīng)時間:響應(yīng)時間短的服務(wù)器優(yōu)先接收新請求。
3. 為什么要學(xué)習(xí) Spring Cloud Balancer ?
因為 Ribbon 作為早期的客戶端負(fù)載均衡工具,在 Spring Cloud 2020.0.0 版本之后已經(jīng)被移除了,取而代之的是 Spring Cloud LoadBalancer,而且 Ribbon 也已經(jīng)不再維護(hù),所以它也是 Spring 官方推薦的負(fù)載均衡解決方案。
其他一些原因:
更好的兼容性:LoadBalancer就像一個全新的配件,它與Spring Cloud的其他組件搭配得更好。
支持響應(yīng)式編程:現(xiàn)在編程界有一種新的編程方式叫做“響應(yīng)式編程”,LoadBalancer能很好地支持這種現(xiàn)代編程風(fēng)格。
易于使用和維護(hù):LoadBalancer的設(shè)計易于拼裝和修改,這對于開發(fā)者來說,維護(hù)和定制起來更加方便。
多功能:LoadBalancer有很多內(nèi)置功能,比如自動幫你挑選服務(wù)器,就像購物網(wǎng)站幫你推薦商品一樣聰明。
4. Spring Cloud LoadBalancer 內(nèi)置的兩種負(fù)載均衡策略
4.1 輪詢負(fù)載均衡策略(默認(rèn)的)
從它的源碼實現(xiàn)可以看出來默認(rèn)的負(fù)載均衡策略是輪詢的策略。
IDEA 搜索它的配置類 LoadBalancerClientConfiguration:
進(jìn)入到 RoundRobinLoadBalancer 這個類里邊,定位到 getInstanceResponse 方法,就能看到輪詢策略的關(guān)鍵代碼:
private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {
if (instances.isEmpty()) {
if (log.isWarnEnabled()) {
log.warn("No servers available for service: " + this.serviceId);
}
return new EmptyResponse();
} else if (instances.size() == 1) {
return new DefaultResponse((ServiceInstance)instances.get(0));
} else {
// 輪詢策略的關(guān)鍵代碼
int pos = this.position.incrementAndGet() & Integer.MAX_VALUE;
ServiceInstance instance = (ServiceInstance)instances.get(pos % instances.size());
return new DefaultResponse(instance);
}
}
理解關(guān)鍵代碼:
int pos = this.position.incrementAndGet() & Integer.MAX_VALUE;
-
this.position.
incrementAndGet()
方法等價于 "++隨機(jī)數(shù) "。這是一個原子操作,保證了每次調(diào)用都會得到一個唯一的遞增數(shù)值。 -
& Integer.MAX_VALUE
這部分是一個位運(yùn)算,它確保了如果position
的值增加到超過Integer.MAX_VALUE
時,不會產(chǎn)生負(fù)數(shù)。其一,在輪詢算法中,如果計數(shù)器變成負(fù)數(shù),那么取余操作可能會產(chǎn)生負(fù)的索引值,這是無效的; 其二,也可也保證在相同規(guī)則底下的公平性。
ServiceInstance instance = (ServiceInstance)
instances.get(pos % instances.size()); // 進(jìn)行輪詢選擇
-
instances
是一個包含所有服務(wù)實例的列表。 -
pos % instances.size()
計算的是pos
除以instances
列表大小的余數(shù),這保證了不論pos
增長到多大,這個表達(dá)式的結(jié)果都是在0
到instances.size() - 1
的范圍內(nèi),這樣就可以循環(huán)地從服務(wù)實例列表中選擇服務(wù)實例。
4.2 隨機(jī)負(fù)載均衡策略
實現(xiàn)隨機(jī)負(fù)載均衡策略的步驟:
① 創(chuàng)建隨機(jī)負(fù)載均衡策略
② 設(shè)置隨機(jī)負(fù)載均衡策略
接下來的操作都是基于這篇博客基礎(chǔ)上去操作的,有需要的可以先去看看這篇博客,先把前置的代碼準(zhǔn)備好:https://blog.csdn.net/xaiobit_hl/article/details/134142521
4.2.1 創(chuàng)建隨機(jī)負(fù)載均衡策略
這些寫法都是相通的,可以仿照源碼中的輪詢策略的關(guān)鍵代碼:
可以去源碼中的LoadBalancerClientConfiguration中去定位到 reactorServiceInstanceLoadBalancer 方法,然后復(fù)制下來,修改幾個關(guān)鍵地方即可。
public class RandomLoadBalancerConfig {
// 隨機(jī)的負(fù)載均衡策略
@Bean
public ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty("loadbalancer.client.name");
return new RandomLoadBalancer(
loadBalancerClientFactory.getLazyProvider(name,
ServiceInstanceListSupplier.class), name);
}
}
?4.2.2?設(shè)置隨機(jī)負(fù)載均衡策略
在 consumer 模塊中的 service 接口上設(shè)置負(fù)載均衡策略:
@Service
@FeignClient("loadbalancer-service")
// 設(shè)置局部的負(fù)載均衡策略
@LoadBalancerClient(name = "loadbalancer-service",
configuration = RandomLoadBalancerConfig.class)
public interface UserService {
@RequestMapping("/user/getname")
public String getName(@RequestParam("id") Integer id);
}
PS:有時候局部的負(fù)載均衡策略不會生效(版本問題),可以將其設(shè)為全局的負(fù)載均衡策略。
如何設(shè)置全局的負(fù)載均衡策略:(在啟動類上加 @LoadBalancerClients 注解)
@SpringBootApplication
@EnableFeignClients // 開啟 OpenFeign
// 設(shè)置全局的負(fù)載均衡策略
@LoadBalancerClients(defaultConfiguration =
RandomLoadBalancerConfig.class)
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
這個時候,就是隨機(jī)的負(fù)載均衡策略了,可以啟動兩個生產(chǎn)者和消費者,然后拿著消費者這邊的端口去獲取服務(wù)感受。
5. Nacos 權(quán)重負(fù)載均衡器
Nacos 中有兩種負(fù)載均衡策略:權(quán)重負(fù)載均衡策略和 CMDB(地域就近訪問)標(biāo)簽負(fù)載均衡策略
它默認(rèn)的策略是權(quán)重。
在 Spring Cloud Balancer 配置為 Nacos 負(fù)載均衡器的步驟:
① 創(chuàng)建 Nacos 負(fù)載均衡器
② 設(shè)置 Nacos 負(fù)載均衡器
5.1 創(chuàng)建 Nacos 負(fù)載均衡器
配置 Nacos 負(fù)載均衡需要注入?NacosDiscoveryProperties 這個類,因為它需要使用到配置文件中的一些關(guān)鍵信息。
@LoadBalancerClients(defaultConfiguration = NacosLoadBalancerConfig.class)
public class NacosLoadBalancerConfig {
@Resource
NacosDiscoveryProperties nacosDiscoveryProperties;
@Bean
public ReactorLoadBalancer<ServiceInstance> nacosLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty("loadbalancer.client.name");
return new NacosLoadBalancer(
loadBalancerClientFactory.getLazyProvider(name,
ServiceInstanceListSupplier.class), name, nacosDiscoveryProperties);
}
}
5.2?設(shè)置 Nacos 負(fù)載均衡器
@Service
@FeignClient("loadbalancer-service")
// 設(shè)置局部的負(fù)載均衡策略
@LoadBalancerClient(name = "loadbalancer-service",
configuration = NacosLoadBalancerConfig.class)
public interface UserService {
@RequestMapping("/user/getname")
public String getName(@RequestParam("id") Integer id);
}
再測試之前,可以先將 Nacos 中一個生產(chǎn)者的權(quán)重給設(shè)置為 10,一個設(shè)置為 1,這樣就能明顯感受到 Nacos 權(quán)重的負(fù)載均衡策略了。
6. 自定義負(fù)載均衡器
自定義負(fù)載均衡策略需要 3 個步驟:
① 創(chuàng)建自定義負(fù)載均衡器
② 封裝自定義負(fù)載均衡器
③ 為服務(wù)設(shè)置自定義負(fù)載均衡策器
6.1?創(chuàng)建自定義負(fù)載均衡器
這里也是可以參考源碼的實現(xiàn)的,搜索?RandomLoadBalancer 這個類,模仿它的實現(xiàn)去創(chuàng)建自定義負(fù)載均衡器。
Ⅰ. 創(chuàng)建一個負(fù)載均衡類,?并讓其實現(xiàn)?ReactorServiceInstanceLoadBalancer 接口;
Ⅱ. 復(fù)制?RandomLoadBalancer 的整個方法體,粘貼到自定義負(fù)載均衡類中,并修改構(gòu)造方法名稱
Ⅲ. 在關(guān)鍵方法 getInstanceResponse 中實現(xiàn)自定義負(fù)載均衡策略(以IP哈希負(fù)載均衡為例)
public class CustomLoadBalancer implements ReactorServiceInstanceLoadBalancer {
private static final Log log = LogFactory.getLog(CustomLoadBalancer.class);
private final String serviceId;
private ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;
public CustomLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId) {
this.serviceId = serviceId;
this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
}
public Mono<Response<ServiceInstance>> choose(Request request) {
ServiceInstanceListSupplier supplier = (ServiceInstanceListSupplier)this.serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);
return supplier.get(request).next().map((serviceInstances) -> {
return this.processInstanceResponse(supplier, serviceInstances);
});
}
private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier, List<ServiceInstance> serviceInstances) {
Response<ServiceInstance> serviceInstanceResponse = this.getInstanceResponse(serviceInstances);
if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {
((SelectedInstanceCallback)supplier).selectedServiceInstance((ServiceInstance)serviceInstanceResponse.getServer());
}
return serviceInstanceResponse;
}
private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {
if (instances.isEmpty()) {
if (log.isWarnEnabled()) {
log.warn("No servers available for service: " + this.serviceId);
}
return new EmptyResponse();
} else {
// 自定義負(fù)載均衡策略
// 獲取 Request 對象
ServletRequestAttributes attributes = (ServletRequestAttributes)
RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
String ipAddress = request.getRemoteAddr();
System.out.println("用戶 IP:" + ipAddress);
int hash = ipAddress.hashCode();
// IP 哈希負(fù)載均衡【關(guān)鍵代碼】
int index = hash % instances.size();
// 得到服務(wù)實例的方法
ServiceInstance instance = (ServiceInstance) instances.get(index);
return new DefaultResponse(instance);
}
}
}
6.2?封裝自定義負(fù)載均衡器
public class CustomLoadBalancerConfig {
// IP 哈希負(fù)載均衡
@Bean
public ReactorLoadBalancer<ServiceInstance> customLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty("loadbalancer.client.name");
return new CustomLoadBalancer(
loadBalancerClientFactory.getLazyProvider(name,
ServiceInstanceListSupplier.class), name);
}
}
6.3?為服務(wù)設(shè)置自定義負(fù)載均衡策器
@Service
@FeignClient("loadbalancer-service")
// 設(shè)置局部的負(fù)載均衡策略
@LoadBalancerClient(name = "loadbalancer-service",
configuration = CustomLoadBalancerConfig.class)
public interface UserService {
@RequestMapping("/user/getname")
public String getName(@RequestParam("id") Integer id);
}
?PS:測試的時候發(fā)現(xiàn)自定義的負(fù)載均衡策略不生效怎么辦 ?
① 把前邊的 Nacos 的負(fù)載均衡器一整個注釋掉(包括 @LoadBalancerClients注解),只提供一個類。
② 如果設(shè)置局部的負(fù)載均衡不生效,就去啟動類上設(shè)置全局的負(fù)載均衡策略。
@SpringBootApplication
@EnableFeignClients // 開啟 OpenFeign
// 設(shè)置全局的負(fù)載均衡策略
@LoadBalancerClients(defaultConfiguration =
CustomLoadBalancerConfig.class)
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
7.? Spring Cloud LoadBalancer 中的緩存機(jī)制
Spring Cloud LoadBalancer 中獲取服務(wù)實例有兩種方式:
1. 實時獲?。?/strong>每次都從注冊中心得到最新的健康實例(效果好,開銷大)
2. 緩存服務(wù)列表:每次得到服務(wù)列表之后,緩存一段時間(既保證性能,也能保證一定的及時性)
Spring Cloud LoadBalancer 默認(rèn)開啟了緩存服務(wù)列表的功能。
測試 Spring Cloud LoadBalancer 的緩存機(jī)制:
1. 將前面設(shè)置負(fù)載均衡策略全部注釋掉,使用默認(rèn)的輪詢測試(便于觀察)
2. 準(zhǔn)備兩個服務(wù)
3. 將其中一個服務(wù)下線,下線的同時立馬去獲取服務(wù),然后等大約 35s ,再去獲取服務(wù)
【測試結(jié)果】 當(dāng)我下線第一個服務(wù)的時候,立馬去獲取服務(wù),這個時候還是兩個服務(wù)輪詢的獲取,等過了 35s 左右,就只能獲取到 64067 這個服務(wù)了。
7.1 Spring Cloud LoadBalancer 中緩存機(jī)制的一些特性
默認(rèn)特性如下:
① 緩存的過期時間為 35s;
② 緩存保存?zhèn)€數(shù)為 256 個。
我們可以通過在配置文件中去設(shè)置這些特性:
spring:
cloud:
loadbalancer:
cache:
ttl: 35s # 過期時間
capacity: 1024 # 設(shè)置緩存?zhèn)€數(shù)
7.2 關(guān)閉緩存
關(guān)閉 Spring Cloud LoadBalancer 中的緩存可以通過以下配置文件來設(shè)置:文章來源:http://www.zghlxwxcb.cn/news/detail-812863.html
spring:
cloud:
loadbalancer:
cache:
enabled: false # 關(guān)閉緩存
PS:盡管關(guān)閉緩存對于開發(fā)和測試很有用,但是在生產(chǎn)環(huán)境上,它的效率是要遠(yuǎn)低于開啟緩存,所以在生產(chǎn)環(huán)境上始終都要開啟緩存。文章來源地址http://www.zghlxwxcb.cn/news/detail-812863.html
到了這里,關(guān)于Spring Cloud LoadBalancer 負(fù)載均衡策略與緩存機(jī)制的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!