全解析Spring Cloud之負(fù)載均衡之LoadBalance
負(fù)載均衡
問(wèn)題
上面是我們之前的代碼,是根據(jù)應(yīng)用名稱獲取了服務(wù)實(shí)例列表,并從列表中選擇了一個(gè)服務(wù)實(shí)例。
那如果一個(gè)服務(wù)對(duì)應(yīng)多個(gè)實(shí)例呢?流量是否可以合理的分配到多個(gè)實(shí)例呢?
我們?cè)賳?dòng)兩個(gè)product-service示例。
步驟
打開(kāi)View->Tool Windows->Services
選中ProductServiceApplication,然后右鍵,選擇Copy Configuration
然后改名,并點(diǎn)擊Modify options
然后點(diǎn)擊Add VM options
然后添加-Dserver.port=9091,然后Apply,OK
然后再重復(fù)上述步驟,再添加一個(gè)服務(wù)實(shí)例。
現(xiàn)象
啟動(dòng)上述所有實(shí)例后,可以在Eureka網(wǎng)站頁(yè)面看到:
此時(shí)多次訪問(wèn)"http://127.0.0.1:8080/order/1",然后查看IDEA上的日志,可以看到,我們剛剛的多次訪問(wèn),都訪問(wèn)到了同一臺(tái)機(jī)器上,即第一個(gè)注冊(cè)到Eureka的服務(wù)實(shí)例端口號(hào)為9092的機(jī)器。
這肯定不是我們想要的結(jié)果,我們啟動(dòng)多個(gè)服務(wù)實(shí)例,是希望可以分擔(dān)其它機(jī)器的負(fù)荷,那么如何實(shí)現(xiàn)呢?
我們可以修改一下之前的order-service中的OrderService代碼,把只請(qǐng)求服務(wù)列表第一臺(tái)機(jī)器修改為輪詢請(qǐng)求服務(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; //計(jì)數(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); //計(jì)算輪流的實(shí)例idnex int index= count.getAndIncrement() % instances.size(); //獲取實(shí)例 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,再次多次訪問(wèn)"127.0.0.1:8080/order/1",可以看到每個(gè)服務(wù)實(shí)例都有被請(qǐng)求到:
通過(guò)?志可以看到, 請(qǐng)求被均衡的分配在了不同的實(shí)例上, 這就是負(fù)載均衡.
什么是負(fù)載均衡?
負(fù)載均衡(Load Balance,簡(jiǎn)稱 LB) , 是?并發(fā), ?可?系統(tǒng)必不可少的關(guān)鍵組件.
當(dāng)服務(wù)流量增?時(shí), 通常會(huì)采?增加機(jī)器的?式進(jìn)?擴(kuò)容, 負(fù)載均衡就是?來(lái)在多個(gè)機(jī)器或者其他資源中, 按照?定的規(guī)則合理分配負(fù)載.
負(fù)載均衡的一些實(shí)現(xiàn)
上?的例?中, 我們只是簡(jiǎn)單的對(duì)實(shí)例進(jìn)?了輪詢, 但真實(shí)的業(yè)務(wù)場(chǎng)景會(huì)更加復(fù)雜. ?如根據(jù)機(jī)器的配置進(jìn)?負(fù)載分配, 配置?的分配的流量?, 配置低的分配流量低等.
服務(wù)多機(jī)部署時(shí), 開(kāi)發(fā)?員都需要考慮負(fù)載均衡的實(shí)現(xiàn), 所以也出現(xiàn)了?些負(fù)載均衡器, 來(lái)幫助我們實(shí)現(xiàn)負(fù)載均衡.
負(fù)載均衡分為服務(wù)端負(fù)載均衡和客?端負(fù)載均衡.
服務(wù)端負(fù)載均衡
在服務(wù)端進(jìn)?負(fù)載均衡的算法分配.
?較有名的服務(wù)端負(fù)載均衡器是Nginx. 請(qǐng)求先到達(dá)Nginx負(fù)載均衡器, 然后通過(guò)負(fù)載均衡算法, 在多個(gè)服務(wù)器之間選擇?個(gè)進(jìn)?訪問(wèn).
客戶端負(fù)載均衡
在客?端進(jìn)?負(fù)載均衡的算法分配.
把負(fù)載均衡的功能以庫(kù)的?式集成到客?端, ?不再是由?臺(tái)指定的負(fù)載均衡設(shè)備集中提供.
?如Spring Cloud的Ribbon, 請(qǐng)求發(fā)送到客?端, 客?端從注冊(cè)中?(?如Eureka)獲取服務(wù)列表, 在發(fā)送請(qǐng)求前通過(guò)負(fù)載均衡算法選擇?個(gè)服務(wù)器,然后進(jìn)?訪問(wèn).
Ribbon是Spring Cloud早期的默認(rèn)實(shí)現(xiàn), 由于不維護(hù)了, 所以最新版本的Spring Cloud負(fù)載均衡集成的是Spring Cloud LoadBalancer(Spring Cloud官?維護(hù))。
客?端負(fù)載均衡和服務(wù)端負(fù)載均衡最?的區(qū)別在于服務(wù)清單所存儲(chǔ)的位置。
Spring Cloud LoadBalance
SpringCloud 從 2020.0.1 版本開(kāi)始, 移除了Ribbon 組件,使?Spring Cloud LoadBalancer 組件來(lái)代替 Ribbon 實(shí)現(xiàn)客?端負(fù)載均衡。
使用Spring Cloud LoadBalance實(shí)現(xiàn)負(fù)載均衡
1. 給 RestTemplate 這個(gè)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; } }
此時(shí)再次多次訪問(wèn)"127.0.0.1:8080/order/1",可以看到每個(gè)服務(wù)實(shí)例都有被請(qǐng)求到,且比例差不多:
負(fù)載均衡策略
負(fù)載均衡策略是?種思想, ?論是哪種負(fù)載均衡器, 它們的負(fù)載均衡策略都是相似的. Spring Cloud
LoadBalancer 僅?持兩種負(fù)載均衡策略: 輪詢策略 和 隨機(jī)策略。
1. 輪詢(Round Robin): 輪詢策略是指服務(wù)器輪流處理??的請(qǐng)求. 這是?種實(shí)現(xiàn)最簡(jiǎn)單, 也最常?的策略. ?活中也有類似的場(chǎng)景, ?如學(xué)校輪流值?, 或者輪流打掃衛(wèi)?.
2. 隨機(jī)選擇(Random): 隨機(jī)選擇策略是指隨機(jī)選擇?個(gè)后端服務(wù)器來(lái)處理新的請(qǐng)求.
官方介紹
翻譯:
Spring Cloud提供了自己的客戶端負(fù)載均衡器抽象和實(shí)現(xiàn)。對(duì)于負(fù)載平衡機(jī)制,添加了ReactiveLoadBalancer接口,并為其提供了基于輪轉(zhuǎn)和隨機(jī)的實(shí)現(xiàn)。為了讓實(shí)例從反應(yīng)式ServiceInstanceListSupplier中進(jìn)行選擇,使用了該接口。目前,我們支持ServiceInstanceListSupplier的基于服務(wù)發(fā)現(xiàn)的實(shí)現(xiàn),該實(shí)現(xiàn)使用類路徑中可用的發(fā)現(xiàn)客戶端從服務(wù)發(fā)現(xiàn)中檢索可用實(shí)例。通過(guò)將Spring.Cloud.LoadBalancer.enabled的值設(shè)置為false,可以禁用Spring Cloud LoadBalancer。
1. 定義隨機(jī)算法對(duì)象, 通過(guò) @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 注解, 可以對(duì)不同的服務(wù)提供?配置不同的客?端負(fù)載均衡算法策略.
由于我們只有?個(gè)客戶端服務(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 注解說(shuō)明
1. name: 該負(fù)載均衡策略對(duì)哪個(gè)服務(wù)?效(服務(wù)提供?)
2. configuration : 該負(fù)載均衡策略 ?哪個(gè)負(fù)載均衡策略實(shí)現(xiàn).
此時(shí)再次多次訪問(wèn)"127.0.0.1:8080/order/1",可以看到每個(gè)服務(wù)實(shí)例都有被請(qǐng)求到,且比例隨機(jī):
LoadBalancer原理
LoadBalancer 的實(shí)現(xiàn), 主要是 LoadBalancerInterceptor , 這個(gè)類會(huì)對(duì) RestTemplate 的請(qǐng)
求進(jìn)?攔截, 然后從Eureka根據(jù)服務(wù)id獲取服務(wù)列表,隨后利?負(fù)載均衡算法得到真實(shí)的服務(wù)地址信息,替換服務(wù)id。
我們來(lái)看看源碼實(shí)現(xiàn):
可以看到這?的intercept?法, 攔截了??的HttpRequest請(qǐng)求,然后做了?件事:
1. request.getURI() 從請(qǐng)求中獲取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ù)載均衡, 并處理請(qǐng)求
根據(jù)serviceId,和負(fù)載均衡策略, 選擇處理的服務(wù):
根據(jù)serviceId,和負(fù)載均衡策略, 選擇處理的服務(wù):
服務(wù)部署
準(zhǔn)備環(huán)境和數(shù)據(jù)
安裝好JDK17和MySQL,并在MySQL中建表且存放好數(shù)據(jù)信息。
修改配置文件中的數(shù)據(jù)庫(kù)密碼。
服務(wù)構(gòu)建打包
采?Maven打包, 需要對(duì)3個(gè)服務(wù)分別打包:
eureka-server, order-service, product-service
啟動(dòng)服務(wù)
上傳Jar包到云服務(wù)器
第一次上傳需要安裝 lrzsz
Centos:
yum install lrzsz
Ubantu:
apt install lrzsz
直接拖動(dòng)文件到xshell窗口,上傳成功。
啟動(dòng)服務(wù)
#后臺(tái)啟動(dòng)eureka-server, 并設(shè)置輸出?志到logs/eureka.log
nohup java -jar eureka-server.jar >logs/eureka.log &#后臺(tái)啟動(dòng)order-service, 并設(shè)置輸出?志到logs/order.log
nohup java -jar order-service.jar >logs/order.log &#后臺(tái)啟動(dòng)product-service, 并設(shè)置輸出?志到logs/order.log
nohup java -jar product-service.jar >logs/product-9090.log &
再多啟動(dòng)兩臺(tái)product-service實(shí)例
#啟動(dòng)實(shí)例, 指定端?號(hào)為9091
nohup java -jar product-service.jar --server.port=9091 >logs/product-9091.log &#啟動(dòng)實(shí)例, 指定端?號(hào)為9092
nohup java -jar product-service.jar --server.port=9092 >logs/product-9092.log &
遠(yuǎn)程調(diào)用訪問(wèn)
可以看到,能夠正常訪問(wèn)并響應(yīng)。
到此這篇關(guān)于Spring Cloud之負(fù)載均衡之LoadBalance的文章就介紹到這了,更多相關(guān)Spring Cloud負(fù)載均衡內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringCloud負(fù)載均衡spring-cloud-starter-loadbalancer解讀
- Spring Cloud LoadBalancer 負(fù)載均衡詳解
- Spring?cloud負(fù)載均衡@LoadBalanced?&?LoadBalancerClient
- 詳解openfeign集成spring?cloud?loadbalancer實(shí)現(xiàn)負(fù)載均衡流程
- SpringCloud LoadBalancer自定義負(fù)載均衡器使用解析
- 詳解SpringCloud LoadBalancer 新一代負(fù)載均衡器
- SpringCloud?LoadBalancerClient?負(fù)載均衡原理解析
相關(guān)文章
spring boot @ResponseBody轉(zhuǎn)換JSON 時(shí) Date 類型處理方法【兩種方法】
這篇文章主要介紹了spring boot @ResponseBody轉(zhuǎn)換JSON 時(shí) Date 類型處理方法,主要給大家介紹Jackson和FastJson兩種方式,每一種方法給大家介紹的都非常詳細(xì),需要的朋友可以參考下2018-08-08使用Spring來(lái)創(chuàng)建一個(gè)簡(jiǎn)單的工作流引擎
這篇文章主要給大家介紹了關(guān)于使用Spring來(lái)創(chuàng)建一個(gè)簡(jiǎn)單的工作流引擎的相關(guān)資料,需要的朋友可以參考下2006-12-12解決mybatis 執(zhí)行mapper的方法時(shí)報(bào)空指針問(wèn)題
這篇文章主要介紹了解決mybatis 執(zhí)行mapper的方法時(shí)報(bào)空指針問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07SpringBoot中l(wèi)ogback日志保存到mongoDB的方法
這篇文章主要介紹了SpringBoot中l(wèi)ogback日志保存到mongoDB的方法,2017-11-11Java日常練習(xí)題,每天進(jìn)步一點(diǎn)點(diǎn)(63)
下面小編就為大家?guī)?lái)一篇Java基礎(chǔ)的幾道練習(xí)題(分享)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧,希望可以幫到你2021-08-08