導(dǎo)言:
耦合性,是對(duì)模塊間關(guān)聯(lián)程度的度量。耦合的強(qiáng)弱取決于模塊間接口的復(fù)雜性、調(diào)用模塊的方式以及通過(guò)界面?zhèn)魉蛿?shù)據(jù)的多少。模塊間的耦合度是指模塊之間的依賴(lài)關(guān)系,包括控制關(guān)系、調(diào)用關(guān)系、數(shù)據(jù)傳遞關(guān)系。
模塊間聯(lián)系越多,其耦合性越強(qiáng),同時(shí)表明其獨(dú)立性越差。軟件設(shè)計(jì)中通常用耦合度和內(nèi)聚度作為衡量模塊獨(dú)立程度的標(biāo)準(zhǔn)。高內(nèi)聚低耦合,是軟件工程中的概念,是判斷設(shè)計(jì)好壞的標(biāo)準(zhǔn),主要是面向?qū)ο蟮脑O(shè)計(jì),主要是看類(lèi)的內(nèi)聚性是否高,耦合度是否低。
SpringCloud和Dubbo都是現(xiàn)在比較成熟的微服務(wù)框架,如何使用兩者構(gòu)建搭建你的微服務(wù)系統(tǒng)呢?他們是如何將你的系統(tǒng)解耦的?又是怎么解耦的呢?請(qǐng)聽(tīng)我慢慢道來(lái):
第一步,解耦現(xiàn)有模塊
將現(xiàn)有耦合在一起的模塊進(jìn)行重新的設(shè)計(jì),設(shè)計(jì)成可以獨(dú)立部署的多個(gè)模塊,使用微服務(wù)框架很容易做到,成熟的示例代碼都特別多,這里不再多講。下面是我的微服務(wù)實(shí)現(xiàn)的一個(gè)架構(gòu)設(shè)計(jì)圖。
第二步,抽取公共模塊
架構(gòu)設(shè)計(jì)原則之一就是反向依賴(lài),只從上往下依賴(lài),所以,我們將公共的重復(fù)功能的模塊抽取出來(lái)。必須強(qiáng)調(diào)一點(diǎn)的是,公共模塊必須足夠的功能單一,不能有其他業(yè)務(wù)的邏輯判斷在里面。在整個(gè)模塊依賴(lài)關(guān)系里,應(yīng)該是一棵樹(shù)狀結(jié)構(gòu)的關(guān)系圖,而不是一個(gè)網(wǎng)狀的關(guān)系圖。
1)做好代碼控制
筆者之前就碰到過(guò)這種問(wèn)題,模塊劃分完了,當(dāng)需求變更的時(shí)候,研發(fā)人員根本不管是不是公共模塊,只要能快速完成任務(wù),哪里改的快就在哪里改。因此,這個(gè)需要內(nèi)部要做好代碼的權(quán)限管理,不應(yīng)該開(kāi)放所有的提交代碼的權(quán)限給所有的人。后來(lái)我就將公共模塊的合并代碼的權(quán)限收回了,合并代碼需要先提交申請(qǐng),代碼review過(guò)才能合并代碼。這就保證了公共模塊代碼的功能單一。
2)做好版本管理
公共模塊被多個(gè)模塊模塊使用,任何代碼的修改都可能會(huì)導(dǎo)致到正在使用的模塊無(wú)法使用。這個(gè)就需要做好各個(gè)模塊的版本管理,我是使用maven進(jìn)行版本管理的,定義一個(gè)總的父pom項(xiàng)目來(lái)進(jìn)行各個(gè)模塊的版本管理,任何被其他模塊使用的開(kāi)發(fā)包都要在父pom里進(jìn)行版本管理。當(dāng)新的需求來(lái)了以后,需要對(duì)公共模塊進(jìn)行修改時(shí),要更新模塊的版本號(hào),同時(shí)更新父pom的版本號(hào),需要使用公共模塊新功能的模塊就修改父pom的版本號(hào),不需要使用公共模塊新功能的模塊就不用修改父pom的版本號(hào),這樣公共模塊的新老版本都能使用,即使出現(xiàn)問(wèn)題,也只會(huì)影響到使用新版本的模塊。
第三步,解耦迭代需求
現(xiàn)在的代碼迭代速度快,同時(shí)會(huì)面對(duì)多個(gè)需求,有的需求緊急,有的需求不緊急,而且緊急程度可能隨時(shí)會(huì)調(diào)整,如果將所有的需求都放在一個(gè)分支,當(dāng)只想上線其中幾個(gè)需求的時(shí)候發(fā)現(xiàn)無(wú)法將不上線需求的代碼拆分出來(lái),是不是很尷尬,即使能拆分出來(lái),代碼修改過(guò)以后又要重新進(jìn)行部署測(cè)試,很費(fèi)時(shí)費(fèi)力,所以要針對(duì)不同的需求重新建立研發(fā)分支,這樣就將不同需求的分支解耦,保證想上哪個(gè)就上哪個(gè),需要上多個(gè)需求的就將分支合并上線。
第四步,配置解耦
為每個(gè)模塊每個(gè)環(huán)境配置一個(gè)配置文件,這樣就可以把不同的環(huán)境的配置解耦,不用每次上線都更新一次。但是如果需要修改數(shù)據(jù)庫(kù)配置,還是需要重新部署重啟應(yīng)用才能解決。使用微服務(wù)的配置中心就能解決這個(gè)問(wèn)題了,比如使用ZooKeeper作為SpringCloud的配置中心,修改ZooKeeper中的節(jié)點(diǎn)數(shù)據(jù)就可以實(shí)時(shí)更新配置并生效。
第五步,權(quán)限解耦
當(dāng)采用微服務(wù)架構(gòu)把原來(lái)的系統(tǒng)拆分成多個(gè)系統(tǒng)以后,你會(huì)發(fā)現(xiàn)原來(lái)簡(jiǎn)單的問(wèn)題,現(xiàn)在變的復(fù)雜了,比如功能的權(quán)限控制,原來(lái)是跟業(yè)務(wù)代碼放到一起,現(xiàn)在如果每個(gè)業(yè)務(wù)模塊都有功能權(quán)限的代碼,將是一件非常麻煩的事情。那么解決辦法就是將權(quán)限功能遷移出來(lái),恰巧使用SpringCloudGateway就能完成這件事情,SpringCloudGateway能夠進(jìn)行負(fù)載均衡,各種路由攔截,只要將原來(lái)的權(quán)限控制代碼遷移到Gateway里實(shí)現(xiàn)以下就可以了,權(quán)限配置管理界面和代碼邏輯都不用變。如果是API接口呢,就需要將安全驗(yàn)證等功能放在Gateway里實(shí)現(xiàn)就好了。
第六步,流量解耦
當(dāng)你的系統(tǒng)訪問(wèn)量越來(lái)越大的時(shí)候,你會(huì)發(fā)現(xiàn)每次升級(jí)都是一件非常麻煩的事情,領(lǐng)導(dǎo)會(huì)跟你說(shuō)這個(gè)功能忙時(shí)不能停機(jī)影響用戶使用呀,只能半夜升級(jí)呀,多么痛快的事情啊。有的時(shí)候運(yùn)營(yíng)人員也會(huì)發(fā)現(xiàn),怎么我的后臺(tái)訪問(wèn)怎么這么慢?問(wèn)題出在哪里呢?問(wèn)題就出在,所有的模塊都用了一個(gè)Gateway,多端同時(shí)使用了相同的流量入口,當(dāng)在舉行大促時(shí),并發(fā)量非常高,帶寬占用非常大,那么其他的功能也會(huì)跟著慢下來(lái)。
不能在舉行大促時(shí)發(fā)券時(shí),我線下支付一直支付不了,這是非常嚴(yán)重的事故了,客服電話會(huì)被打爆了。所以,必須要對(duì)流量進(jìn)行拆分,各個(gè)端的流量不能相互影響,比如App端、微信端、運(yùn)營(yíng)后臺(tái)和商戶后臺(tái)等都要分配獨(dú)立的Gateway,并接入獨(dú)立的帶寬,對(duì)于流量大的端可以使用彈性帶寬,對(duì)于運(yùn)營(yíng)后臺(tái)和商戶后臺(tái)就比較小的固定的帶寬即可。這樣就大大降低了升級(jí)時(shí)的難度,是不是再上線時(shí)就沒(méi)那么緊張了?
第七步,數(shù)據(jù)解耦
系統(tǒng)剛上線的時(shí)候,數(shù)據(jù)量不大,所有的模塊感覺(jué)都挺好的,當(dāng)時(shí)間一長(zhǎng),系統(tǒng)訪問(wèn)量非常大的時(shí)候會(huì)發(fā)現(xiàn)功能怎么都變慢了,怎么MySQL的cpu經(jīng)常100%。那么恭喜你,你中招了,你的數(shù)據(jù)需要解耦了。
首先要模塊間數(shù)據(jù)解耦,將不同模塊使用獨(dú)立的數(shù)據(jù)庫(kù),保證各模塊之間的數(shù)據(jù)不相互影響。
其次就是冷熱數(shù)據(jù)解耦,同一個(gè)模塊運(yùn)行時(shí)間長(zhǎng)了以后也會(huì)積累大量的數(shù)據(jù),為了保證系統(tǒng)的性能的穩(wěn)定,要減少因?yàn)閿?shù)據(jù)量太大造成的性能降低,需要對(duì)歷史數(shù)據(jù)進(jìn)行定期的遷移,對(duì)于完整數(shù)據(jù)分析匯總就在其他的庫(kù)中實(shí)現(xiàn)。
第八步,擴(kuò)容解耦
一個(gè)好的架構(gòu)設(shè)計(jì)是要有好的橫向擴(kuò)展的能力,在不需要修改代碼只通過(guò)增加硬件的方式就能提高系統(tǒng)的性能。SpringCloud和Dubbo的注冊(cè)中心天生就能夠?qū)崿F(xiàn)動(dòng)態(tài)添加模塊的節(jié)點(diǎn),其他模塊調(diào)用能夠?qū)崟r(shí)發(fā)現(xiàn)并請(qǐng)求到新的模塊節(jié)點(diǎn)上。
第九步,部署解耦
互聯(lián)網(wǎng)開(kāi)發(fā)在于能夠快速的試錯(cuò),當(dāng)一個(gè)新的版本上線時(shí),經(jīng)常是需要先讓一部分用戶進(jìn)行測(cè)試一下,這就是傳說(shuō)中的灰度發(fā)布,同一個(gè)模塊先部署升級(jí)幾臺(tái)服務(wù)器到新版本,重啟完成后流量進(jìn)來(lái)以后,就可以驗(yàn)證當(dāng)前部署的這幾臺(tái)服務(wù)器有沒(méi)有問(wèn)題,就繼續(xù)部署其他的節(jié)點(diǎn),如果有問(wèn)題馬上回滾到上一個(gè)版本。使用SpringCloudGateway的WeighRouterFilter就能實(shí)現(xiàn)這個(gè)功能。
第十步,動(dòng)靜解耦
當(dāng)同一個(gè)模塊的瞬間有非常高并發(fā)的時(shí)候,對(duì),就是說(shuō)的秒殺,純粹的流量解耦還是不夠,因?yàn)椴荒茏屒懊娴牧髁繘_擊后面真正的下單的功能,這個(gè)時(shí)候就需要更細(xì)的流量解耦,要將靜態(tài)文件的訪問(wèn)通通拋給CDN來(lái)解決,動(dòng)態(tài)和靜態(tài)之間是通過(guò)定時(shí)器來(lái)出發(fā)的,時(shí)間未到之前一直刷新的是靜態(tài)的文件,當(dāng)時(shí)間到了之后,生成新的js文件,告訴靜態(tài)頁(yè)面可以訪問(wèn)下單功能了。
總結(jié)
在模塊劃分時(shí),要遵循“一個(gè)模塊,一個(gè)功能”的原則,盡可能使模塊達(dá)到功能內(nèi)聚。
事實(shí)上,微服務(wù)架構(gòu)短期來(lái)看,并沒(méi)有很明顯的好處,甚至短期內(nèi)會(huì)影響系統(tǒng)的開(kāi)發(fā)進(jìn)度,因?yàn)楦邇?nèi)聚,低耦合的系統(tǒng)對(duì)開(kāi)發(fā)設(shè)計(jì)人員提出了更高的要求。高內(nèi)聚,低耦合的好處體現(xiàn)在系統(tǒng)持續(xù)發(fā)展的過(guò)程中,高內(nèi)聚,低耦合的系統(tǒng)具有更好的重用性,維護(hù)性,擴(kuò)展性,可以更高效的完成系統(tǒng)的維護(hù)開(kāi)發(fā),持續(xù)的支持業(yè)務(wù)的發(fā)展,而不會(huì)成為業(yè)務(wù)發(fā)展的障礙。