全解析Spring Cloud之負(fù)載均衡之LoadBalance
負(fù)載均衡
問題

上面是我們之前的代碼,是根據(jù)應(yīng)用名稱獲取了服務(wù)實例列表,并從列表中選擇了一個服務(wù)實例。
那如果一個服務(wù)對應(yīng)多個實例呢?流量是否可以合理的分配到多個實例呢?
我們再啟動兩個product-service示例。
步驟
打開View->Tool Windows->Services

選中ProductServiceApplication,然后右鍵,選擇Copy Configuration

然后改名,并點(diǎn)擊Modify options

然后點(diǎn)擊Add VM options

然后添加-Dserver.port=9091,然后Apply,OK

然后再重復(fù)上述步驟,再添加一個服務(wù)實例。

現(xiàn)象
啟動上述所有實例后,可以在Eureka網(wǎng)站頁面看到:

此時多次訪問"http://127.0.0.1:8080/order/1",然后查看IDEA上的日志,可以看到,我們剛剛的多次訪問,都訪問到了同一臺機(jī)器上,即第一個注冊到Eureka的服務(wù)實例端口號為9092的機(jī)器。



這肯定不是我們想要的結(jié)果,我們啟動多個服務(wù)實例,是希望可以分擔(dān)其它機(jī)器的負(fù)荷,那么如何實現(xiàn)呢?
我們可以修改一下之前的order-service中的OrderService代碼,把只請求服務(wù)列表第一臺機(jī)器修改為輪詢請求服務(wù)列表中的機(jī)器。
修改后的order-service中的OrderService代碼如下:
package order.service;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import order.mapper.OrderMapper;
import order.model.OrderInfo;
import order.model.ProductInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@Slf4j
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
//計數(shù)器
private AtomicInteger count = new AtomicInteger(1);
private List<ServiceInstance> instances;
@PostConstruct
public void init(){
//從Eureka中獲取服務(wù)列表
instances = discoveryClient.getInstances("product-service");
}
public OrderInfo selectOrderById(Integer orderId){
OrderInfo orderInfo = orderMapper.selectOrderById(orderId);
//計算輪流的實例idnex
int index= count.getAndIncrement() % instances.size();
//獲取實例
String uri = instances.get(index).getUri().toString();
//拼接url
String url = uri+"/product/"+orderInfo.getProductId();
log.info("遠(yuǎn)程調(diào)用url:{}", url);
ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);
orderInfo.setProductInfo(productInfo);
return orderInfo;
}
}重啟order-service,再次多次訪問"127.0.0.1:8080/order/1",可以看到每個服務(wù)實例都有被請求到:



通過?志可以看到, 請求被均衡的分配在了不同的實例上, 這就是負(fù)載均衡.
什么是負(fù)載均衡?
負(fù)載均衡(Load Balance,簡稱 LB) , 是?并發(fā), ?可?系統(tǒng)必不可少的關(guān)鍵組件.
當(dāng)服務(wù)流量增?時, 通常會采?增加機(jī)器的?式進(jìn)?擴(kuò)容, 負(fù)載均衡就是?來在多個機(jī)器或者其他資源中, 按照?定的規(guī)則合理分配負(fù)載.
負(fù)載均衡的一些實現(xiàn)
上?的例?中, 我們只是簡單的對實例進(jìn)?了輪詢, 但真實的業(yè)務(wù)場景會更加復(fù)雜. ?如根據(jù)機(jī)器的配置進(jìn)?負(fù)載分配, 配置?的分配的流量?, 配置低的分配流量低等.
服務(wù)多機(jī)部署時, 開發(fā)?員都需要考慮負(fù)載均衡的實現(xiàn), 所以也出現(xiàn)了?些負(fù)載均衡器, 來幫助我們實現(xiàn)負(fù)載均衡.
負(fù)載均衡分為服務(wù)端負(fù)載均衡和客?端負(fù)載均衡.
服務(wù)端負(fù)載均衡
在服務(wù)端進(jìn)?負(fù)載均衡的算法分配.
?較有名的服務(wù)端負(fù)載均衡器是Nginx. 請求先到達(dá)Nginx負(fù)載均衡器, 然后通過負(fù)載均衡算法, 在多個服務(wù)器之間選擇?個進(jìn)?訪問.

客戶端負(fù)載均衡
在客?端進(jìn)?負(fù)載均衡的算法分配.
把負(fù)載均衡的功能以庫的?式集成到客?端, ?不再是由?臺指定的負(fù)載均衡設(shè)備集中提供.
?如Spring Cloud的Ribbon, 請求發(fā)送到客?端, 客?端從注冊中?(?如Eureka)獲取服務(wù)列表, 在發(fā)送請求前通過負(fù)載均衡算法選擇?個服務(wù)器,然后進(jìn)?訪問.
Ribbon是Spring Cloud早期的默認(rèn)實現(xiàn), 由于不維護(hù)了, 所以最新版本的Spring Cloud負(fù)載均衡集成的是Spring Cloud LoadBalancer(Spring Cloud官?維護(hù))。

客?端負(fù)載均衡和服務(wù)端負(fù)載均衡最?的區(qū)別在于服務(wù)清單所存儲的位置。
Spring Cloud LoadBalance
SpringCloud 從 2020.0.1 版本開始, 移除了Ribbon 組件,使?Spring Cloud LoadBalancer 組件來代替 Ribbon 實現(xiàn)客?端負(fù)載均衡。
使用Spring Cloud LoadBalance實現(xiàn)負(fù)載均衡
1. 給 RestTemplate 這個Bean添加 @LoadBalanced 注解就可以
package order.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class BeanConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}2.修改后的order-service中的OrderService代碼如下:
修改IP為服務(wù)端名稱。
package order.service;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import order.mapper.OrderMapper;
import order.model.OrderInfo;
import order.model.ProductInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Slf4j
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private RestTemplate restTemplate;
public OrderInfo selectOrderById(Integer orderId){
OrderInfo orderInfo = orderMapper.selectOrderById(orderId);
String url = "http://product-service/product/"+orderInfo.getProductId();
log.info("遠(yuǎn)程調(diào)用url:{}", url);
ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);
orderInfo.setProductInfo(productInfo);
return orderInfo;
}
}此時再次多次訪問"127.0.0.1:8080/order/1",可以看到每個服務(wù)實例都有被請求到,且比例差不多:



負(fù)載均衡策略
負(fù)載均衡策略是?種思想, ?論是哪種負(fù)載均衡器, 它們的負(fù)載均衡策略都是相似的. Spring Cloud
LoadBalancer 僅?持兩種負(fù)載均衡策略: 輪詢策略 和 隨機(jī)策略。
1. 輪詢(Round Robin): 輪詢策略是指服務(wù)器輪流處理??的請求. 這是?種實現(xiàn)最簡單, 也最常?的策略. ?活中也有類似的場景, ?如學(xué)校輪流值?, 或者輪流打掃衛(wèi)?.
2. 隨機(jī)選擇(Random): 隨機(jī)選擇策略是指隨機(jī)選擇?個后端服務(wù)器來處理新的請求.
官方介紹

翻譯:
Spring Cloud提供了自己的客戶端負(fù)載均衡器抽象和實現(xiàn)。對于負(fù)載平衡機(jī)制,添加了ReactiveLoadBalancer接口,并為其提供了基于輪轉(zhuǎn)和隨機(jī)的實現(xiàn)。為了讓實例從反應(yīng)式ServiceInstanceListSupplier中進(jìn)行選擇,使用了該接口。目前,我們支持ServiceInstanceListSupplier的基于服務(wù)發(fā)現(xiàn)的實現(xiàn),該實現(xiàn)使用類路徑中可用的發(fā)現(xiàn)客戶端從服務(wù)發(fā)現(xiàn)中檢索可用實例。通過將Spring.Cloud.LoadBalancer.enabled的值設(shè)置為false,可以禁用Spring Cloud LoadBalancer。
1. 定義隨機(jī)算法對象, 通過 @Bean 將其加載到 Spring 容器中
此處使?Spring Cloud LoadBalancer提供的 RandomLoadBalancer
package order.config;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.RandomLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
public class CustomLoadBalancerConfiguration {
@Bean
ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new RandomLoadBalancer(loadBalancerClientFactory
.getLazyProvider(name, ServiceInstanceListSupplier.class),
name);
}
}注意: 該類需要滿?:
1. 不? @Configuration 注釋
2. 在組件掃描范圍內(nèi)
2. 使? @LoadBalancerClient 或者 @LoadBalancerClients 注解
在 RestTemplate 配置類上?, 使? @LoadBalancerClient 或 @LoadBalancerClients 注解, 可以對不同的服務(wù)提供?配置不同的客?端負(fù)載均衡算法策略.
由于我們只有?個客戶端服務(wù)提供者, 所以使?@LoadBalancerClient。
package order.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@LoadBalancerClient(name = "product-service", configuration = CustomLoadBalancerConfiguration.class)
@Configuration
public class BeanConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}@LoadBalancerClient 注解說明
1. name: 該負(fù)載均衡策略對哪個服務(wù)?效(服務(wù)提供?)
2. configuration : 該負(fù)載均衡策略 ?哪個負(fù)載均衡策略實現(xiàn).
此時再次多次訪問"127.0.0.1:8080/order/1",可以看到每個服務(wù)實例都有被請求到,且比例隨機(jī):



LoadBalancer原理
LoadBalancer 的實現(xiàn), 主要是 LoadBalancerInterceptor , 這個類會對 RestTemplate 的請
求進(jìn)?攔截, 然后從Eureka根據(jù)服務(wù)id獲取服務(wù)列表,隨后利?負(fù)載均衡算法得到真實的服務(wù)地址信息,替換服務(wù)id。
我們來看看源碼實現(xiàn):

可以看到這?的intercept?法, 攔截了??的HttpRequest請求,然后做了?件事:
1. request.getURI() 從請求中獲取uri, 也就是 http://product-service
2. service/product/1001 originalUri.getHost() 從uri中獲取路徑的主機(jī)名, 也就是服務(wù)id, product-service
3. loadBalancer.execute 根據(jù)服務(wù)id, 進(jìn)?負(fù)載均衡, 并處理請求
根據(jù)serviceId,和負(fù)載均衡策略, 選擇處理的服務(wù):

根據(jù)serviceId,和負(fù)載均衡策略, 選擇處理的服務(wù):

服務(wù)部署
準(zhǔn)備環(huán)境和數(shù)據(jù)
安裝好JDK17和MySQL,并在MySQL中建表且存放好數(shù)據(jù)信息。
修改配置文件中的數(shù)據(jù)庫密碼。
服務(wù)構(gòu)建打包
采?Maven打包, 需要對3個服務(wù)分別打包:
eureka-server, order-service, product-service

啟動服務(wù)
上傳Jar包到云服務(wù)器
第一次上傳需要安裝 lrzsz
Centos:
yum install lrzsz
Ubantu:
apt install lrzsz
直接拖動文件到xshell窗口,上傳成功。
啟動服務(wù)
#后臺啟動eureka-server, 并設(shè)置輸出?志到logs/eureka.log
nohup java -jar eureka-server.jar >logs/eureka.log &#后臺啟動order-service, 并設(shè)置輸出?志到logs/order.log
nohup java -jar order-service.jar >logs/order.log &#后臺啟動product-service, 并設(shè)置輸出?志到logs/order.log
nohup java -jar product-service.jar >logs/product-9090.log &
再多啟動兩臺product-service實例
#啟動實例, 指定端?號為9091
nohup java -jar product-service.jar --server.port=9091 >logs/product-9091.log &#啟動實例, 指定端?號為9092
nohup java -jar product-service.jar --server.port=9092 >logs/product-9092.log &

遠(yuǎn)程調(diào)用訪問



可以看到,能夠正常訪問并響應(yīng)。
到此這篇關(guān)于Spring Cloud之負(fù)載均衡之LoadBalance的文章就介紹到這了,更多相關(guān)Spring Cloud負(fù)載均衡內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringCloud負(fù)載均衡spring-cloud-starter-loadbalancer解讀
- Spring Cloud LoadBalancer 負(fù)載均衡詳解
- Spring?cloud負(fù)載均衡@LoadBalanced?&?LoadBalancerClient
- 詳解openfeign集成spring?cloud?loadbalancer實現(xiàn)負(fù)載均衡流程
- SpringCloud LoadBalancer自定義負(fù)載均衡器使用解析
- 詳解SpringCloud LoadBalancer 新一代負(fù)載均衡器
- SpringCloud?LoadBalancerClient?負(fù)載均衡原理解析
相關(guān)文章
spring boot @ResponseBody轉(zhuǎn)換JSON 時 Date 類型處理方法【兩種方法】
這篇文章主要介紹了spring boot @ResponseBody轉(zhuǎn)換JSON 時 Date 類型處理方法,主要給大家介紹Jackson和FastJson兩種方式,每一種方法給大家介紹的都非常詳細(xì),需要的朋友可以參考下2018-08-08
解決mybatis 執(zhí)行mapper的方法時報空指針問題
這篇文章主要介紹了解決mybatis 執(zhí)行mapper的方法時報空指針問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07
SpringBoot中l(wèi)ogback日志保存到mongoDB的方法
這篇文章主要介紹了SpringBoot中l(wèi)ogback日志保存到mongoDB的方法,2017-11-11
Java日常練習(xí)題,每天進(jìn)步一點(diǎn)點(diǎn)(63)
下面小編就為大家?guī)硪黄狫ava基礎(chǔ)的幾道練習(xí)題(分享)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧,希望可以幫到你2021-08-08

