?作者簡介:大家好,我是Leo,熱愛Java后端開發(fā)者,一個想要與大家共同進(jìn)步的男人????
??個人主頁:Leo的博客
??當(dāng)前專欄: 微服務(wù)探索之旅
?特色專欄: MySQL學(xué)習(xí)
??本文內(nèi)容:Ribbon和 Nacos服務(wù)注冊中心
???個人小站 :個人博客,歡迎大家訪問
??個人知識庫: 知識庫,歡迎大家訪問
上一節(jié)我們學(xué)習(xí)了SpringCloud的核心組件Eureka ,但是它逐漸被Nacos替代了,在此之前我們先了解一下Ribbon負(fù)載均衡。
1. Ribbon負(fù)載均衡
1.1 關(guān)于負(fù)載均衡
? 負(fù)載均衡一般分為服務(wù)器端負(fù)載均衡和客戶端負(fù)載均衡
? 所謂服務(wù)器端負(fù)載均衡,比如Nginx
、F5
這些,請求到達(dá)服務(wù)器之后由這些負(fù)載均衡器根據(jù)一定的算法將請求路由到目標(biāo)服務(wù)器處理。
? 所謂客戶端負(fù)載均衡,比如我們要說的Ribbon
,服務(wù)消費者客戶端會有一個服務(wù)器地址列表,調(diào)用方在請求前通過一定的負(fù)載均衡算法選擇一個服務(wù)器進(jìn)行訪問,負(fù)載均衡算法的執(zhí)行是在請求客戶端進(jìn)行。
? Ribbon是Netflix
發(fā)布的負(fù)載均衡器。Eureka一般配合Ribbon進(jìn)行使用,Ribbon
利用從Eureka
中讀取到服務(wù)信息,在調(diào)用服務(wù)提供者提供的服務(wù)時,會根據(jù)一定的算法進(jìn)行負(fù)載。
覺得還是有點沒理解,話不多說,直接上實戰(zhàn)
我們?nèi)绻麤]有任何配置的情況下,只需要加上
@LoadBalanced
這個注解 ,他的默認(rèn)策略就是輪詢策略,簡單來說 ,就我們哥倆(這里指的是用戶微服務(wù)集群)輪著來,一人一次
我們這里做一個測試,我們同時發(fā)起四次不同請求,使用訂單微服務(wù),遠(yuǎn)程調(diào)用用戶微服務(wù)
我們會發(fā)現(xiàn)這四次請求,分別調(diào)用了四次用戶微服務(wù),而這四次是分發(fā)在用戶微服務(wù)1和用戶微服務(wù)2
,而且正好是我們剛所說的輪詢策略,一人兩次。
下面我們更改Ribbon的策略,再來看看
首先在OrderApplication中加入我們更改的策略,這里我們更改的是隨機(jī)策略
@Bean
public IRule randomRule() {
return new RandomRule();
}
重新啟動訂單微服務(wù)進(jìn)行測試
此時我們發(fā)現(xiàn),用戶微服務(wù)1沒有一個命中,而用戶微服務(wù)2全部命中,這就是隨機(jī)策略,能不能命中全靠隨機(jī)
。
1.2 負(fù)載均衡原理
我們添加了@LoadBalanced
注解,即可實現(xiàn)負(fù)載均衡功能,這是什么原理呢
SpringCloud底層其實是利用了一個名為Ribbon
的組件,來實現(xiàn)負(fù)載均衡功能的。
那么我們發(fā)出的請求明明是http://userservice/user/1,怎么變成了http://localhost:9001的呢?
1.3 源碼跟蹤
為什么我們只輸入了service名稱就可以訪問了呢?之前還要獲取ip和端口。
顯然有人幫我們根據(jù)service名稱,獲取到了服務(wù)實例的ip和端口。它就是LoadBalancerInterceptor
,這個類會在對RestTemplate
的請求進(jìn)行攔截,然后從Eureka根據(jù)服務(wù)id獲取服務(wù)列表,隨后利用負(fù)載均衡算法得到真實的服務(wù)地址信息,替換服務(wù)id。
我們進(jìn)行源碼跟蹤:
1)LoadBalancerIntercepor
可以看到這里的intercept方法,攔截了用戶的HttpRequest請求,然后做了幾件事:
-
request.getURI()
:獲取請求uri,本例中就是 http://user-service/user/8 -
originalUri.getHost()
:獲取uri路徑的主機(jī)名,其實就是服務(wù)id,user-service
-
this.loadBalancer.execute()
:處理服務(wù)id,和用戶請求。
這里的this.loadBalancer
是LoadBalancerClient
類型,我們繼續(xù)跟入。
2)LoadBalancerClient
繼續(xù)跟入execute方法:
代碼是這樣的:
-
getLoadBalancer(serviceId):根據(jù)服務(wù)id獲取ILoadBalancer,而
LoadBalancer
會拿著服務(wù)id
去eureka中獲取服務(wù)列表并保存起來。 - getServer(loadBalancer):利用內(nèi)置的負(fù)載均衡算法,從服務(wù)列表中選擇一個。本例中,可以看到獲取了9003端口的服務(wù)
放行后,再次訪問并跟蹤,發(fā)現(xiàn)獲取的是9001:
果然實現(xiàn)了負(fù)載均衡。
1.4 負(fù)載均衡策略IRule
在剛才的代碼中,可以看到獲取服務(wù)使通過一個getServer
方法來做負(fù)載均衡:
我們繼續(xù)跟入:
繼續(xù)跟蹤源碼chooseServer方法,發(fā)現(xiàn)這么一段代碼:
我們看看這個rule是誰:
這里的rule默認(rèn)值是一個RoundRobinRule
,看類的介紹:
這不就是輪詢的意思嘛。
到這里,整個負(fù)載均衡的流程我們就清楚了。
總結(jié)
SpringCloudRibbon
的底層采用了一個攔截器,攔截了RestTemplate發(fā)出的請求,對地址做了修改。用一幅圖來總結(jié)一下:
基本流程如下:
- 攔截我們的RestTemplate請求http://userservice/user/1
- RibbonLoadBalancerClient會從請求url中獲取服務(wù)名稱,也就是user-service
- DynamicServerListLoadBalancer根據(jù)user-service到eureka拉取服務(wù)列表
- eureka返回列表,localhost:9001、localhost:9002
- IRule利用內(nèi)置負(fù)載均衡規(guī)則,從列表中選擇一個,例如localhost:9001
- RibbonLoadBalancerClient修改請求地址,用localhost:8081替代userservice,得到http://localhost:9001/user/1,發(fā)起真實請求
1.5 負(fù)載均衡策略
負(fù)載均衡的規(guī)則都定義在IRule接口中,而IRule有很多不同的實現(xiàn)類:
不同規(guī)則的含義如下:
負(fù)載均衡策略 | 描述 |
---|---|
RoundRobinRule:輪詢策略 | 簡單輪詢服務(wù)列表來選擇服務(wù)器。它是Ribbon默認(rèn)的負(fù)載均衡規(guī)則。默認(rèn)超過10次獲取到的server都不可用,會返回一個空的server |
RandomRule:隨機(jī)策略 | 如果隨機(jī)到的server為null或者不可用的話,會while不停的循環(huán)選取 |
AvailabilityFilteringRule:最小連接數(shù)策略 | 對以下兩種服務(wù)器進(jìn)行忽略: (1)在默認(rèn)情況下,這臺服務(wù)器如果3次連接失敗,這臺服務(wù)器就會被設(shè)置為“短路”狀態(tài)。短路狀態(tài)將持續(xù)30秒,如果再次連接失敗,短路的持續(xù)時間就會幾何級地增加。 (2)并發(fā)數(shù)過高的服務(wù)器。如果一個服務(wù)器的并發(fā)連接數(shù)過高,配置了AvailabilityFilteringRule規(guī)則的客戶端也會將其忽略。并發(fā)連接數(shù)的上限,可以由客戶端的..ActiveConnectionsLimit屬性進(jìn)行配置。 |
WeightedResponseTimeRule:加權(quán)響應(yīng)時間規(guī)則 | 為每一個服務(wù)器賦予一個權(quán)重值。服務(wù)器響應(yīng)時間越長,這個服務(wù)器的權(quán)重就越小。這個規(guī)則會隨機(jī)選擇服務(wù)器,這個權(quán)重值會影響服務(wù)器的選擇。 |
ZoneAvoidanceRule:區(qū)域權(quán)衡策略(默認(rèn)策略) | 擴(kuò)展了輪詢策略,繼承了2個過濾器:ZoneAvoidancePredicate和AvailabilityPredicate,除了過濾超時和鏈接數(shù)過多的server,還會過濾掉不符合要求的zone區(qū)域里面的所有節(jié)點, 在一個區(qū)域/機(jī)房內(nèi)的服務(wù)實例中輪詢。先過濾再輪詢 |
BestAvailableRule:最佳可用規(guī)則 | 忽略那些短路的服務(wù)器,并選擇并發(fā)數(shù)較低的服務(wù)器。 |
RandomRule:隨機(jī)策略 | 隨機(jī)選擇一個可用的服務(wù)器。如果隨機(jī)到的server為null或者不可用的話,會while不停的循環(huán)選取 |
RetryRule:重試策略 | 一定時限內(nèi)循環(huán)重試。默認(rèn)繼承RoundRobinRule,也支持自定義注入,RetryRule會在每次選取之后,對選舉的server進(jìn)行判斷,是否為null,是否alive,并且在500ms內(nèi)會不停的選取判斷。而RoundRobinRule失效的策略是超過10次,RandomRule是沒有失效時間的概念,只要serverList沒都掛。 |
默認(rèn)的實現(xiàn)就是ZoneAvoidanceRule
,是一種輪詢方案
1.5.1 自定義負(fù)載均衡策略
通過定義IRule實現(xiàn)可以修改負(fù)載均衡規(guī)則,有兩種方式:
- 代碼方式:在order-service中的OrderApplication類中,定義一個新的IRule:
@Bean
public IRule randomRule(){
return new RandomRule();
}
- 配置文件方式:在order-service的application.yml文件中,添加新的配置也可以修改規(guī)則:
userservice: # 給某個微服務(wù)配置負(fù)載均衡規(guī)則,這里是userservice服務(wù)
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 負(fù)載均衡規(guī)則
注意,一般用默認(rèn)的負(fù)載均衡規(guī)則,不做修改。
1.6 饑餓加載
Ribbon默認(rèn)是采用懶加載,即第一次訪問時才會去創(chuàng)建LoadBalanceClient,請求時間會很長。
而饑餓加載則會在項目啟動時創(chuàng)建,降低第一次訪問的耗時,通過下面配置開啟饑餓加載:
ribbon:
eager-load:
enabled: true
clients: userservice
2.Nacos注冊中心
國內(nèi)公司一般都推崇阿里巴巴的技術(shù),比如注冊中心,SpringCloudAlibaba也推出了一個名為Nacos的注冊中心。
2.1認(rèn)識和安裝Nacos
Nacos是阿里巴巴的產(chǎn)品,現(xiàn)在是SpringCloud中的一個組件。相比Eureka功能更加豐富,在國內(nèi)受歡迎程度較高。
2.1.1 下載安裝包
在Nacos的GitHub頁面,提供有下載鏈接,可以下載編譯好的Nacos服務(wù)端或者源代碼:
GitHub主頁:https://github.com/alibaba/nacos
GitHub的Release下載頁:https://github.com/alibaba/nacos/releases
如圖:
windows版本使用nacos-server-1.4.1.zip
包即可。
2.1.2 解壓
將這個包解壓到任意非中文目錄下,如圖:
目錄說明:
- bin:啟動腳本
- conf:配置文件
2.1.3 端口配置
Nacos的默認(rèn)端口是8848,如果你電腦上的其它進(jìn)程占用了8848端口,請先嘗試關(guān)閉該進(jìn)程。
如果無法關(guān)閉占用8848端口的進(jìn)程,也可以進(jìn)入nacos的conf目錄,修改配置文件中的端口:
修改其中的內(nèi)容:
2.1.4 啟動
啟動非常簡單,進(jìn)入bin目錄,結(jié)構(gòu)如下:
然后執(zhí)行命令即可:
-
windows命令:
startup.cmd -m standalone
執(zhí)行后的效果如圖:
2.1.5 訪問
在瀏覽器輸入地址:http://127.0.0.1:8848/nacos即可:
默認(rèn)的賬號和密碼都是nacos,進(jìn)入后:
2.2 微服務(wù)注冊到Nacos
2.2.1 引入依賴
- 在cloud-demo父工程的pom文件中的
<dependencyManagement>
中引入SpringCloudAlibaba的依賴:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.6.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
- 然后在user-service和order-service中的pom文件中引入nacos-discovery依賴:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
注意:不要忘了注釋掉eureka的依賴。
2.2.2 配置nacos地址
在user-service和order-service的application.yml中添加nacos地址:
spring:
cloud:
nacos:
server-addr: localhost:8848
注意:不要忘了注釋掉eureka的依賴。
2.2.3 重啟
保護(hù)閾值:可以設(shè)置為0-1之間的浮點數(shù)
,它其實是一個比例值(當(dāng)前服務(wù)健康實例數(shù)/當(dāng)前服務(wù)總實例數(shù))
? 場景:
? 一般流程下,nacos是服務(wù)注冊中心,服務(wù)消費者要從nacos獲取某一個服務(wù)的可用實例信息,對于服務(wù)實例有健康/不健康狀態(tài)之分,nacos在返回給消費者實例信息的時候,會返回健康實例。這個時候在一些高并發(fā)、大流量場景下會存在一定的問題
? 如果服務(wù)A有100個實例,98個實例都不健康了,只有2個實例是健康的,如果nacos只返回這兩個健康實例的信息的話,那么后續(xù)消費者的請求將全部被分配到這兩個實例,流量洪峰到來,2個健康的實例也扛不住了,整個服務(wù)A 就扛不住,上游的微服務(wù)也會導(dǎo)致崩潰,產(chǎn)生雪崩效應(yīng)。
? 保護(hù)閾值的意義在于
? 當(dāng)服務(wù)A健康實例數(shù)/總實例數(shù) < 保護(hù)閾值 的時候,說明健康實例真的不多了,這個時候保護(hù)閾值會被觸發(fā)(狀態(tài)true)
? nacos將會把該服務(wù)所有的實例信息(健康的+不健康的)全部提供給消費者,消費者可能訪問到不健康的實例,請求失敗,但這樣也比造成雪崩要好,犧牲了一些請求,保證了整個系統(tǒng)的一個可用。
? 注意:阿里內(nèi)部在使用nacos的時候,也經(jīng)常調(diào)整這個保護(hù)閾值參數(shù)。
2.3 服務(wù)分級存儲模型
一個服務(wù)可以有多個實例,例如我們的user-service,可以有:
- 127.0.0.1:9001
- 127.0.0.1:9002
- 127.0.0.1:9003
- 127.0.0.1:9004
假如這些實例分布于全國各地的不同機(jī)房,例如:
- 127.0.0.1:9001,在上海機(jī)房
- 127.0.0.1:9002,在上海機(jī)房
- 127.0.0.1:9003,在杭州機(jī)房
- 127.0.0.1:9004,在杭州機(jī)房
Nacos就將同一機(jī)房內(nèi)的實例 劃分為一個集群。
也就是說,user-service是服務(wù),一個服務(wù)可以包含多個集群,如杭州、上海,每個集群下可以有多個實例,形成分級模型,如圖:
微服務(wù)互相訪問時,應(yīng)該盡可能訪問同集群實例,因為本地訪問速度更快。當(dāng)本集群內(nèi)不可用時,才訪問其它集群。例如:
杭州機(jī)房內(nèi)的order-service應(yīng)該優(yōu)先訪問同機(jī)房的user-service。
2.4 給user-service配置集群
修改user-service的application.yml文件,添加集群配置:
spring:
cloud:
nacos:
server-addr: localhost:8848
discovery:
cluster-name: HZ # 集群名稱
重啟兩個user-service實例后,我們可以在nacos控制臺看到下面結(jié)果:
我們再次復(fù)制一個user-service啟動配置,添加屬性:
-Dserver.port=8083 -Dspring.cloud.nacos.discovery.cluster-name=SH
配置如圖所示:
啟動UserApplication3后再次查看nacos控制臺:
2.5 同集群優(yōu)先的負(fù)載均衡
默認(rèn)的ZoneAvoidanceRule
并不能實現(xiàn)根據(jù)同集群優(yōu)先來實現(xiàn)負(fù)載均衡。
因此Nacos中提供了一個NacosRule
的實現(xiàn),可以優(yōu)先從同集群中挑選實例。
1)給order-service配置集群信息
修改order-service的application.yml文件,添加集群配置:
spring:
cloud:
nacos:
server-addr: localhost:8848
discovery:
cluster-name: HZ # 集群名稱
2)修改負(fù)載均衡規(guī)則
修改order-service的application.yml文件,修改負(fù)載均衡規(guī)則:
userservice:
ribbon:
NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule # 負(fù)載均衡規(guī)則
2.6 權(quán)重配置
實際部署中會出現(xiàn)這樣的場景:
服務(wù)器設(shè)備性能有差異,部分實例所在機(jī)器性能較好,另一些較差,我們希望性能好的機(jī)器承擔(dān)更多的用戶請求。
但默認(rèn)情況下NacosRule
是同集群內(nèi)隨機(jī)挑選,不會考慮機(jī)器的性能問題。
因此,Nacos
提供了權(quán)重配置來控制訪問頻率,權(quán)重越大則訪問頻率越高。
在nacos
控制臺,找到user-service
的實例列表,點擊編輯,即可修改權(quán)重:
在彈出的編輯窗口,修改權(quán)重:
注意:如果權(quán)重修改為0,則該實例永遠(yuǎn)不會被訪問
2.7 環(huán)境隔離
Nacos提供了namespace來實現(xiàn)環(huán)境隔離功能。
- nacos中可以有多個namespace
- namespace下可以有g(shù)roup、service等
- 不同namespace之間相互隔離,例如不同namespace的服務(wù)互相不可見
Namespace:命名空間,對不同的環(huán)境進(jìn)行隔離,比如隔離開發(fā)環(huán)境、測試環(huán)境和生產(chǎn)環(huán)境
Group:分組,將若干個服務(wù)或者若干個配置集歸為一組,通常習(xí)慣一個系統(tǒng)歸為一個組(拉勾招聘、拉勾獵頭、拉勾教育)
Service:某一個服務(wù),比如商品微服務(wù)
DataId:配置集或者可以認(rèn)為是一個配置文件
Namespace + Group + Service 如同 Maven 中的GAV坐標(biāo),GAV坐標(biāo)是為了鎖定Jar,而這里是為了鎖定服務(wù)
Namespace + Group + DataId 如同 Maven 中的GAV坐標(biāo),GAV坐標(biāo)是為了鎖定Jar,而這里是為了鎖定配置文件
最佳實踐
? Nacos抽象出了Namespace
、Group
、Service
、DataId
等概念,具體代表什么取決于怎么用(非常靈活),推薦用法如下
概念 | 描述 |
---|---|
Namespace | 代表不同的環(huán)境,如開發(fā)dev、測試test、生產(chǎn)環(huán)境prod |
Group | 代表某項目,比如拉勾云項目 |
Service | 某個項目中具體xxx服務(wù) |
DataId | 某個項目中具體的xxx配置文件 |
2.7.1 創(chuàng)建namespace
默認(rèn)情況下,所有service、data、group都在同一個namespace,名為public:
我們可以點擊頁面新增按鈕,添加一個namespace:
然后,填寫表單:
就能在頁面看到一個新的namespace:
2.7.2 給微服務(wù)配置namespace
給微服務(wù)配置namespace只能通過修改配置來實現(xiàn)。
例如,修改order-service的application.yml
文件:
spring:
cloud:
nacos:
server-addr: localhost:8848
discovery:
cluster-name: HZ
namespace: 6f91682a-dae8-4236-8974-48595037e16c # 命名空間,填I(lǐng)D
重啟order-service后,訪問控制臺,可以看到下面的結(jié)果:
此時訪問order-service,因為namespace不同,會導(dǎo)致找不到userservice,控制臺會報錯:
2.8 Nacos與Eureka的區(qū)別
Nacos的服務(wù)實例分為兩種l類型:
-
臨時實例:如果實例宕機(jī)超過一定時間,會從服務(wù)列表剔除,默認(rèn)的類型。
-
非臨時實例:如果實例宕機(jī),不會從服務(wù)列表剔除,也可以叫永久實例。
配置一個服務(wù)實例為永久實例:
spring:
cloud:
nacos:
discovery:
ephemeral: false # 設(shè)置為非臨時實例
Nacos和Eureka整體結(jié)構(gòu)類似,服務(wù)注冊、服務(wù)拉取、心跳等待,但是也存在一些差異:
-
Nacos與eureka的共同點文章來源:http://www.zghlxwxcb.cn/news/detail-485661.html
- 都支持服務(wù)注冊和服務(wù)拉取
- 都支持服務(wù)提供者心跳方式做健康檢測
-
Nacos與Eureka的區(qū)別文章來源地址http://www.zghlxwxcb.cn/news/detail-485661.html
- Nacos支持服務(wù)端主動檢測提供者狀態(tài):臨時實例采用心跳模式,非臨時實例采用主動檢測模式
- 臨時實例心跳不正常會被剔除,非臨時實例則不會被剔除
- Nacos支持服務(wù)列表變更的消息推送模式,服務(wù)列表更新更及時
- Nacos集群默認(rèn)采用AP方式,當(dāng)集群中存在非臨時實例時,采用CP模式;Eureka采用AP方式
到了這里,關(guān)于Ribbon和 Nacos服務(wù)注冊中心的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!