1否灾、前言
公司內(nèi)考慮到服務(wù)器資源成本的問(wèn)題卖擅,目前業(yè)務(wù)上還在進(jìn)行服務(wù)的容器化改造和遷移,計(jì)劃將容器化后的服務(wù)墨技,以及一些中間件(MQ惩阶、DB、ES扣汪、Redis等)盡量都遷移到其他機(jī)房断楷。
那你們?yōu)槭裁床挥冒⒗镌瓢。v訊云啊崭别,還用自己的機(jī)房冬筒?
的確是這樣,公司內(nèi)部目前還是有專門的運(yùn)維團(tuán)隊(duì)茅主。也是因?yàn)闅v史原因舞痰,當(dāng)時(shí)業(yè)務(wù)發(fā)展比較迅猛,考慮到數(shù)據(jù)的安全性也是自建機(jī)房的诀姚。對(duì)于中小型公司這樣做响牛,顯然成本太高了,所以一般都用阿里云。對(duì)于中大型企業(yè)或者對(duì)數(shù)據(jù)安全性要求高的公司呀打,自建機(jī)房維護(hù)的也不再少數(shù)矢赁。
對(duì)于中間件來(lái)說(shuō),比如 Redis 緩存贬丛,有的業(yè)務(wù)也是因?yàn)闅v史原因撩银,當(dāng)時(shí)上線后都是單獨(dú)申請(qǐng),并部署的一套集群瘫寝,但是量并不是很大蜒蕾,所以類似這種情況的,可以考慮跟其他項(xiàng)目使用的集群合并為一個(gè)焕阿,這樣就可能節(jié)省了一部分服務(wù)器資源咪啡。
現(xiàn)在大多數(shù)企業(yè)都已經(jīng)微服務(wù)化,容器化了暮屡。
所以撤摸,將非容器化的業(yè)務(wù)要求都遷移到容器中,這里的容器基本都是指 Kubernetes
平臺(tái)了褒纲,通過(guò)容器發(fā)布調(diào)度服務(wù)准夷,對(duì)于運(yùn)維來(lái)說(shuō),維護(hù)變得更加便捷莺掠,高效衫嵌。
對(duì)于研發(fā)來(lái)說(shuō),業(yè)務(wù)需要部署服務(wù)彻秆,不再需要重新提 JIRA
工單楔绞,走一系列審核流程,最后給到你的可能還是一臺(tái)虛擬機(jī)唇兑,依賴的軟件單獨(dú)安裝部署酒朵。用了容器,只要在 集裝箱
中提前安裝好所需軟件環(huán)境扎附,按照發(fā)布規(guī)范打好鏡像蔫耽,發(fā)布服務(wù)的過(guò)程一路就是 點(diǎn)點(diǎn)點(diǎn)...
。
2留夜、線上業(yè)務(wù)場(chǎng)景介紹
繼續(xù)來(lái)說(shuō)今天的主題匙铡。
有一個(gè)項(xiàng)目是 SpringCloud
架構(gòu)的,其中使用到了 網(wǎng)關(guān) Zuul
碍粥,并且也使用了到了 Eureka
作為注冊(cè)中心慰枕。
因?yàn)樵擁?xiàng)目提前已經(jīng)遷移到北京機(jī)房節(jié)點(diǎn)部署的容器環(huán)境,我們最終目標(biāo)是遷移到其他機(jī)房(如:天津機(jī)房)即纲。
北京有兩個(gè)機(jī)房:A機(jī)房、B機(jī)房博肋,因?yàn)槎荚诒本┑驼詢蓚€(gè)機(jī)房之間的 網(wǎng)絡(luò)延時(shí)
是可以接受的蜂厅。
微服務(wù)也同樣在這兩個(gè)機(jī)房之間都有部署。
此時(shí)膊畴,如果只是將微服務(wù)部署到 天津機(jī)房
掘猿,會(huì)變成如下圖所示的關(guān)系:
問(wèn)題很明顯,就是網(wǎng)關(guān)服務(wù)只有北京的唇跨,而微服務(wù)新增了天津機(jī)房的稠通,此時(shí)會(huì)導(dǎo)致 跨機(jī)房調(diào)用
,即北京網(wǎng)關(guān)調(diào)用到了天津微服務(wù)买猖。
盡管北京到天津 ping
的網(wǎng)絡(luò)延時(shí)僅有 3 毫秒
之差改橘,但是服務(wù)與服務(wù)之間的調(diào)用,可就不止這 3 毫秒了玉控。
其中包括服務(wù)器與服務(wù)器之間 TCP連接的建立飞主、數(shù)據(jù)傳輸?shù)木W(wǎng)絡(luò)開銷,如果數(shù)據(jù)包過(guò)大高诺,跨機(jī)房訪問(wèn)耗時(shí)就會(huì)很明顯了碌识。
所以呢,盡量避免跨機(jī)房訪問(wèn)虱而,當(dāng)然要將網(wǎng)關(guān)也要遷移到天津機(jī)房筏餐。
但是,大家看 粉紅色粗體
的線條牡拇,仍然存在跨機(jī)房調(diào)用魁瞪,天津網(wǎng)關(guān)調(diào)用到北京微服務(wù)。
對(duì)于線上并發(fā)訪問(wèn)量稍微大點(diǎn)诅迷,或者有些接口響應(yīng)體大的佩番,又或者網(wǎng)絡(luò)抖動(dòng)等場(chǎng)景下,可能就會(huì)導(dǎo)致接口響應(yīng)時(shí)間變長(zhǎng)了罢杉。
如何解決呢趟畏?
因大部分業(yè)務(wù)都部署到天津,可以將天津機(jī)房的服務(wù)權(quán)重調(diào)高
SLB配置 (類Nginx):
upstream {
server 北京機(jī)房網(wǎng)關(guān)IP 20滩租;
server 天津機(jī)房網(wǎng)關(guān)IP 80赋秀;
}
網(wǎng)關(guān)與微服務(wù)之間,都是通過(guò) Eureka
注冊(cè)中心媒介來(lái)溝通律想,即 注冊(cè)服務(wù)
拉取服務(wù)
猎莲。
僅僅在網(wǎng)關(guān)層配置好權(quán)重還不夠,此時(shí)還會(huì)存在天津網(wǎng)關(guān)路由到北京微服務(wù)上技即。
Eureka 內(nèi)部是基于 Ribbon
實(shí)現(xiàn)負(fù)載均衡的著洼,自行實(shí)現(xiàn)按權(quán)重的負(fù)載均衡策略,Eureka做一點(diǎn)改造,界面上支持權(quán)重的修改身笤。
下圖截圖了部分示例:
IP后面的就是權(quán)重值豹悬,可以在界面上輸入權(quán)重值進(jìn)行調(diào)整。
我們可以將北京微服務(wù)權(quán)重調(diào)低液荸,天津微服務(wù)權(quán)重調(diào)高瞻佛。
相當(dāng)于網(wǎng)關(guān)以及微服務(wù)兩側(cè)都是通過(guò)基于 權(quán)重
的負(fù)載均衡算法來(lái)盡量減少
跨機(jī)房調(diào)用的,但是無(wú)法避免
跨機(jī)房調(diào)用娇钱。
使用 Eureka 的分區(qū)改進(jìn)
上面描述的方案對(duì)于 20%
的流量仍然存在跨機(jī)房訪問(wèn)伤柄,我們能不能做到先訪問(wèn)同一機(jī)房的服務(wù),如果同一機(jī)房的服務(wù)都不可用了文搂,再訪問(wèn)其他機(jī)房的呢适刀?
答案是 可以的
。
我們可以借助于 Eureka
注冊(cè)中心里提供了 region
和 zone
的概念來(lái)實(shí)現(xiàn)细疚。
region
和 zone
兩個(gè)概念均來(lái)自亞馬遜的 AWS:
region
:簡(jiǎn)單理解為地理上的分區(qū)蔗彤,比如亞洲地區(qū),或者華北地區(qū)等等疯兼,沒有具體大小的限制然遏。根據(jù)項(xiàng)目情況,自行合理劃分 region吧彪。
zone
:簡(jiǎn)單理解為 region
內(nèi)的具體機(jī)房待侵,比如說(shuō) zone
劃分為北京、天津姨裸,且北京有兩個(gè)機(jī)房秧倾,就可以在 region 內(nèi)劃分為三個(gè)zone,北京劃分為zone1傀缩、zone2那先,天津?yàn)閦one3。
結(jié)合上面的示例赡艰,假設(shè)僅設(shè)置一個(gè) region
為京津地區(qū)售淡。
然后我們給這個(gè)區(qū)域下的網(wǎng)關(guān)服務(wù)、微服務(wù)打上 zone
機(jī)房標(biāo)簽慷垮,在系統(tǒng)運(yùn)維上將機(jī)房也稱作 IDC
數(shù)據(jù)中心揖闸。
網(wǎng)關(guān)服務(wù)打上zone標(biāo)簽:
微服務(wù)打上zone標(biāo)簽:
這個(gè)功能都是在 Eureka注冊(cè)中心
上實(shí)現(xiàn)的,在給服務(wù)配置 zone
前料身,調(diào)用路徑如下所示:
給服務(wù)配置 zone
之后汤纸,框架內(nèi)部的路由機(jī)制
的實(shí)現(xiàn)下,調(diào)用路徑如下所示:
當(dāng)前使用的 Eureka
是部署在北京芹血,如果想讓服務(wù)在注冊(cè)
贮泞、續(xù)約
楞慈、拉取
動(dòng)作時(shí)也能實(shí)現(xiàn) 就近機(jī)房訪問(wèn)
,部署架構(gòu)就變成如下這個(gè)樣子:
北京區(qū)域不同機(jī)房假設(shè)認(rèn)為網(wǎng)絡(luò)延時(shí)小隙畜,所以北京兩個(gè)機(jī)房可以使用同一個(gè) Eureka
集群抖部;天津可以單獨(dú)再部署一套 Eureka
集群,這樣就可以實(shí)現(xiàn)優(yōu)先路由到同機(jī)房訪問(wèn)议惰。
服務(wù)注冊(cè)的關(guān)鍵配置
基本原理就是這樣,貼上一段 Eureka
使用 region
和 zone
的配置供大家參考:
spring:
application:
name: mananger
server:
port: ${EUREKA_SERVER_PORT:8011}
eureka:
instance:
# 全網(wǎng)服務(wù)實(shí)例唯一標(biāo)識(shí)
instance-id: ${EUREKA_SERVER_IP:127.0.0.1}:${server.port}
# 服務(wù)實(shí)例的meta數(shù)據(jù)鍵值對(duì)集合乡恕,可由注冊(cè)中心進(jìn)行服務(wù)實(shí)例間傳遞
metadata-map:
# [HA-P配置]-當(dāng)前服務(wù)實(shí)例的zone
zone: ${EUREKA_SERVER_ZONE:tz-1}
profiles: ${spring.profiles.active}
# 開啟ip言询,默認(rèn)為false=》hostname
prefer-ip-address: true
ip-address: ${EUREKA_SERVER_IP:127.0.0.1}
# [HA-P配置]-當(dāng)前服務(wù)實(shí)例的region
client:
region: ${EUREKA_SERVER_REGION:cn-bj}
# [HA-P配置]-開啟當(dāng)前服務(wù)實(shí)例優(yōu)先發(fā)現(xiàn)同zone的注冊(cè)中心,默認(rèn)為true
prefer-same-zone-eureka: true
# [服務(wù)注冊(cè)]-允許當(dāng)前服務(wù)實(shí)例注冊(cè)傲宜,默認(rèn)為true
register-with-eureka: true
# [服務(wù)續(xù)約]-允許當(dāng)前服務(wù)實(shí)例獲取注冊(cè)信息运杭,默認(rèn)為true
fetch-registry: true
# [HA-P配置]-可用region下zone集合
availability-zones:
cn-bj: ${eureka.instance.metadata-map.zone},zone-bj,zone-tj
service-url:
# [HA-P配置]-各zone下注冊(cè)中心地址列表
zone-bj: http://BJIP1:8011/eureka,http://BJIP2:8012/eureka
zone-tj: http://TJIP1:8013/eureka,http://TJIP2:8014/eureka
prefer-same-zone-eureka
:
默認(rèn)就為true,首先會(huì)通過(guò) region
找到 availability-zones
內(nèi)的第一個(gè) zone
函卒,然后通過(guò)這個(gè) zone
找到 service-url
對(duì)應(yīng)該機(jī)房的注冊(cè)中心地址列表辆憔,并向該列表內(nèi)的 第一個(gè)URL
地址發(fā)起注冊(cè)和心跳,不會(huì)再向其它的URL地址發(fā)起操作报嵌。只有當(dāng)?shù)谝粋€(gè)URL地址注冊(cè)失敗的情況下虱咧,才會(huì)依次向其它的URL發(fā)起操作,重試一定次數(shù)仍然失敗锚国,會(huì)間隔一段心跳時(shí)間繼續(xù)重試腕巡。
eureka.instance.metadata-map.zone
:
服務(wù)提供者和消費(fèi)者都要配置該參數(shù),表示自己屬于哪一個(gè)機(jī)房的血筑。網(wǎng)關(guān)服務(wù)也屬于消費(fèi)者绘沉,從注冊(cè)中心拉取到注冊(cè)表之后會(huì)根據(jù)這個(gè)參數(shù)中指定的 zone
進(jìn)行過(guò)濾,過(guò)濾后向同 zone
內(nèi)的服務(wù)會(huì)有多個(gè)實(shí)例 豺总,通過(guò) Ribbon
來(lái)實(shí)現(xiàn)負(fù)載均衡調(diào)用车伞。如果同一 zone
內(nèi)的所有服務(wù)都不可用時(shí),會(huì)其他 zone
的服務(wù)發(fā)起調(diào)用喻喳。
另外注意一點(diǎn) availability-zones
下 region 的配置是 ${eureka.instance.metadata-map.zone},...
這樣配置的好處是另玖,你只要指定好了 eureka.instance.metadata-map.zone
,優(yōu)先會(huì)將這個(gè)參數(shù)放到可用分區(qū)下作為第一個(gè) zone
來(lái)訪問(wèn)沸枯。
Zuul 網(wǎng)關(guān)路由分區(qū)源碼分析
網(wǎng)關(guān)使用的 zuul
日矫,其內(nèi)部也是通過(guò) ribbon
和 eureka
的結(jié)合來(lái)實(shí)現(xiàn)服務(wù)之間的調(diào)用,因?yàn)榫W(wǎng)關(guān)實(shí)際也是個(gè)服務(wù)消費(fèi)者绑榴,同樣會(huì)注冊(cè)到 eureka
上哪轿,被網(wǎng)關(guān)拉取過(guò)來(lái)的注冊(cè)表里的服務(wù),作為服務(wù)提供者翔怎,同樣會(huì)注冊(cè)到eureka
上窃诉。
通過(guò)一張圖把控整個(gè)請(qǐng)求的大致脈絡(luò):
上述圖示中部分核心源碼如下所示:
PollServerListUpdater#start(final UpdateAction action)
啟動(dòng)后會(huì)每隔30秒(默認(rèn))去Eureka注冊(cè)中心拉取一次注冊(cè)表信息杨耙,更新本地緩存的數(shù)據(jù)結(jié)構(gòu)。
調(diào)用到了DyamicServerListLoadBalancer匿名實(shí)現(xiàn)類中飘痛。
通過(guò)DyamicServerListLoadBalancer類調(diào)用了 updateListOfServer()
方法更新服務(wù)列表珊膜,serverListImpl的實(shí)現(xiàn)是DiscoveryEnabledNIWSServerList類
在DiscoveryEnabledNIWSServerList類內(nèi)部會(huì)調(diào)用 obtainServersViaDiscovery()
方法,其內(nèi)部通過(guò) EurekaClient
來(lái)實(shí)現(xiàn)從 Eureka 注冊(cè)中心拉取服務(wù)列表宣脉。
過(guò)濾器內(nèi)部獲取同一機(jī)房(zone)的服務(wù)列表车柠,先后會(huì)調(diào)用 ZonePreferenceServerListFilter
和 ZoneAffinityServerListFilter
兩個(gè)過(guò)濾器實(shí)現(xiàn) zone
的過(guò)濾。
最開始獲取的Servers一共是有4條記錄塑猖,根據(jù)調(diào)試的代碼看竹祷,我們是為了獲取 zone
為2的服務(wù),所以得到的結(jié)果是一條羊苟,即 zone = "2"
塑陵,說(shuō)明找到了同 zone 服務(wù)。
請(qǐng)求接口后會(huì)調(diào)用到 LoadBalancerContext#getServerFromLoadBalancer(...)
蜡励,內(nèi)部會(huì)調(diào)用到ILoadBalancer
具體實(shí)現(xiàn)的 chooseServer()
方法令花,最終會(huì)獲取到 zone="2"
里的一個(gè)Server。
那么這里是如何選擇的Server呢凉倚?
本地調(diào)試時(shí)兼都,只配置了已給可用的zone,所以這里條件滿足會(huì)直接調(diào)用 super.chooseServer(key)
父類的方法:
BaseLoadBalancer#chooseServer(...) 父類的選擇Server的方法占遥,其內(nèi)部通過(guò) IRule#choose(key)
會(huì)調(diào)用到具體的負(fù)載均衡器的實(shí)現(xiàn):
上述截圖中俯抖,能看到 MetadataWeightedRule
,這個(gè)類是我們自行基于權(quán)重
負(fù)載均衡實(shí)現(xiàn)瓦胎。
該實(shí)現(xiàn)類是繼承了 ZoneAviodanceRule
芬萍,目的就是利用了 zone
的概念,所重寫的 choose(Object key)
方法搔啊,調(diào)用了 this.getPredicate().getEligibleServers(...)
會(huì)走同樣的過(guò)濾規(guī)則獲取到同一機(jī)房(zone)下的所有服務(wù)列表柬祠,然后在基于每個(gè)服務(wù)配置的權(quán)重篩選一個(gè)Server。
獲取到 Server
后负芋,拼接接口的URI請(qǐng)求地址 http://IP:PORT/api/.../xxx.json
漫蛔,通過(guò)底層的 OkHttp
實(shí)現(xiàn)完成 Http 接口的調(diào)用過(guò)程。
好了旧蛾,到此基本就分析完了莽龟,從網(wǎng)關(guān)請(qǐng)求,通過(guò) ribbon
組件從 eureka
注冊(cè)中心拉取服務(wù)列表锨天,如何基于 zone
分區(qū)來(lái)實(shí)現(xiàn)多數(shù)據(jù)中心的訪問(wèn)毯盈。
對(duì)于 服務(wù)注冊(cè)
,要保證服務(wù)能注冊(cè)到同一個(gè) zone
內(nèi)的注冊(cè)中心病袄,如果跨 zone
注冊(cè)搂赋,會(huì)導(dǎo)致網(wǎng)絡(luò)延時(shí)較大赘阀,出現(xiàn)拉取注冊(cè)表,心跳超時(shí)等問(wèn)題脑奠。
對(duì)于 服務(wù)調(diào)用
基公,要保證優(yōu)先調(diào)用同一個(gè) zone
內(nèi)的服務(wù),當(dāng)無(wú)法找到同 zone
或者 同 zone
內(nèi)的服務(wù)不可用時(shí)宋欺,才會(huì)轉(zhuǎn)向調(diào)用其他 zone
里的服務(wù)轰豆。
本文提到的只是網(wǎng)關(guān)到微服務(wù)之間的調(diào)用,實(shí)際項(xiàng)目中齿诞,微服務(wù)還會(huì)調(diào)用其他第三方的服務(wù)秒咨,也要同時(shí)考慮到跨機(jī)房調(diào)用的問(wèn)題,盡量都讓各服務(wù)之間在同機(jī)房調(diào)用掌挚,減少網(wǎng)絡(luò)延時(shí),提高服務(wù)的穩(wěn)定性菩咨。