摘要:一持續(xù)集成對(duì)于微服務(wù)的意義拆之前要先解決合的問(wèn)題在很多微服務(wù)化的文章中,很少會(huì)把持續(xù)集成放在第一篇,因?yàn)榇蠖鄶?shù)的文章都會(huì)將如何拆的問(wèn)題,例如拆的粒度,拆的時(shí)機(jī),拆的方式。二持續(xù)集成就是不斷的嘗試在一起集成就是在一起。
此文已由作者劉超授權(quán)網(wǎng)易云社區(qū)發(fā)布。
歡迎訪問(wèn)網(wǎng)易云社區(qū),了解更多網(wǎng)易技術(shù)產(chǎn)品運(yùn)營(yíng)經(jīng)驗(yàn)。
一、持續(xù)集成對(duì)于微服務(wù)的意義:拆之前要先解決合的問(wèn)題
在很多微服務(wù)化的文章中,很少會(huì)把持續(xù)集成放在第一篇,因?yàn)榇蠖鄶?shù)的文章都會(huì)將如何拆的問(wèn)題,例如拆的粒度,拆的時(shí)機(jī),拆的方式。
為什么需要拆呢?因?yàn)檫@是人類處理問(wèn)題的本質(zhì)方式:將一個(gè)大的復(fù)雜問(wèn)題,變成很多個(gè)小問(wèn)題解決。
所以當(dāng)一個(gè)系統(tǒng)復(fù)雜到一定程度,當(dāng)維護(hù)一個(gè)系統(tǒng)的人數(shù)多到一定程度,解決問(wèn)題的難度和溝通成本大大提高,因而需要拆成很多個(gè)工程,拆成很多個(gè)團(tuán)隊(duì),分而治之。
然而當(dāng)每個(gè)子團(tuán)隊(duì)將子問(wèn)題解決了,整個(gè)系統(tǒng)的問(wèn)題就解決了么?你可以想象你將一輛整車拆成零件,然后再組裝起來(lái)的過(guò)程,你就可以想象拆雖然不容易,合則更難,需要各種標(biāo)準(zhǔn),各種流水線,才能將零件組裝稱為車。
我們先來(lái)回顧一下拆的過(guò)程。
最初的應(yīng)用大多數(shù)是一個(gè)單體應(yīng)用
一個(gè)Java后端,后面跟一個(gè)數(shù)據(jù)庫(kù),基本上就搞定了。
隨著系統(tǒng)復(fù)雜度的增加,首先Java程序需要做的是縱向的拆分。
首先最外面是一個(gè)負(fù)載均衡,接著是接入的nginx,做不同服務(wù)的路由。
不同的服務(wù)拆成獨(dú)立的進(jìn)程,獨(dú)立部署,每個(gè)服務(wù)使用自己的數(shù)據(jù)庫(kù)和緩存,解決數(shù)據(jù)庫(kù)和緩存的單點(diǎn)瓶頸。
數(shù)據(jù)庫(kù)使用一主多從的模式,進(jìn)行讀寫(xiě)分離,主要針對(duì)讀多寫(xiě)少的場(chǎng)景。
為了承載更多的請(qǐng)求,設(shè)置緩存層,將數(shù)據(jù)緩存到Memcached或者Redis中,增加命中率。
當(dāng)然還有些跨服務(wù)的查詢,或者非結(jié)構(gòu)化數(shù)據(jù)的查詢,引入搜索引擎,比關(guān)系型數(shù)據(jù)庫(kù)的查詢速度快很多。
在高并發(fā)情況下,僅僅縱向拆分還不夠,因而需要做真正的服務(wù)化。
一個(gè)服務(wù)化的架構(gòu)如圖所示。
首先是接入層,這一層主要實(shí)現(xiàn)API網(wǎng)關(guān)和動(dòng)態(tài)資源和靜態(tài)資源的分離及緩存,并且可以在這一層做整個(gè)系統(tǒng)的限流。
接下來(lái)是Web層,也就是controller,提供最外層的API,是對(duì)外提供服務(wù)的一層。
下面是組合服務(wù)層,有時(shí)候被稱為編排層,compose層,是實(shí)現(xiàn)復(fù)雜邏輯的一層。
下面是基礎(chǔ)服務(wù)層,是提供原子性的基本的邏輯的一層,他下面是緩存,數(shù)據(jù)庫(kù)。
服務(wù)之間需要治理,需要相互發(fā)現(xiàn),所以一般會(huì)有dubbo或者springcloud一樣的框架。
對(duì)所有的服務(wù),都應(yīng)該有監(jiān)控告警,及時(shí)發(fā)現(xiàn)異常,并自動(dòng)修復(fù)或者告警運(yùn)維手動(dòng)修復(fù)。
對(duì)于所有的服務(wù)的日志,應(yīng)該有相同的格式,收集到一起,稱為日志中心,方便發(fā)現(xiàn)錯(cuò)誤的時(shí)候,在統(tǒng)一的一個(gè)地方可以debug。
對(duì)于所有的服務(wù)的配置,有統(tǒng)一的管理的地方,稱為配置中心,可以通過(guò)修改配置中心,下發(fā)配置,對(duì)于整個(gè)集群進(jìn)行配置的修改,例如打開(kāi)熔斷或者降級(jí)開(kāi)關(guān)等。
通過(guò)簡(jiǎn)單的描述,大家可以發(fā)現(xiàn),從一個(gè)簡(jiǎn)單的單體應(yīng)用,變成如此復(fù)雜的微服務(wù)架構(gòu),除了關(guān)心怎么拆的問(wèn)題,還必須關(guān)注:
如何控制拆的風(fēng)險(xiǎn)
如何保證代碼質(zhì)量
如何保證功能不變,不引入新的Bug
答案當(dāng)然就是集成,從一開(kāi)始就集成,并且不斷的集成,反復(fù)的將拆分的模塊重新組合,看看是否能夠順利組合起來(lái),并且保證功能的不變。
要是不沒(méi)事兒就組合一下,天知道幾個(gè)月以后還能不能合的起來(lái)。
別忘了程序是人寫(xiě)的,你和你媳婦長(zhǎng)時(shí)間不溝通都對(duì)不上默契,別說(shuō)兩個(gè)程序員了。
二、持續(xù)集成就是不斷的嘗試在一起
集成就是在一起。
為什么需要一個(gè)統(tǒng)一的代碼倉(cāng)庫(kù)Git來(lái)做代碼管理呢?是為了代碼集成在一起。
為什么需要進(jìn)行構(gòu)建build呢?就是代碼邏輯需要集成在一起,編譯不出錯(cuò)
為什么要單元測(cè)試呢?一個(gè)模塊的功能集成在一起能夠正確工作。
為什么需要聯(lián)調(diào)測(cè)試Staging環(huán)境呢?需要將不同模塊之間集成在一起,在一個(gè)類生產(chǎn)的環(huán)境中進(jìn)行測(cè)試。
最終才是部署到生產(chǎn)環(huán)境中,將所有人分開(kāi)做的工作才算真正的合在了一起。
持續(xù)集成就是制定一系列流程,或者一個(gè)系列規(guī)則,將需要在一起的各個(gè)層次規(guī)范起來(lái),方便大家在一起,強(qiáng)迫大家在一起。
三、持續(xù)集成,持續(xù)交付,持續(xù)部署,敏捷開(kāi)發(fā),DevOps都啥關(guān)系?
這些概念都容易混淆,他們之間是什么關(guān)系呢?
敏捷開(kāi)發(fā)Agile是一種開(kāi)發(fā)流程,是一種快速迭代的開(kāi)發(fā)流程,每個(gè)開(kāi)發(fā)流程非常短,長(zhǎng)到一個(gè)月,短到兩個(gè)星期,就會(huì)是一個(gè)周期,在這個(gè)周期中,每天都要開(kāi)會(huì)同步,每天都要集成。正是因?yàn)橹芷诙蹋判枰掷m(xù)的做這件事情,如果一個(gè)開(kāi)發(fā)周期長(zhǎng)達(dá)幾個(gè)月,則不需要持續(xù)的集成,最后留幾個(gè)星期的集成時(shí)間一起做也是可以的,但是這樣就不能達(dá)到互聯(lián)網(wǎng)公司的快速迭代,也是我們常??吹絺鹘y(tǒng)公司的做法。
持續(xù)集成往往指對(duì)代碼的提交,構(gòu)建,測(cè)試的過(guò)程,也就是上述的在一起的過(guò)程。
持續(xù)交付是指將集成好的交付物,例如war,jar,或者容器鏡像,部署在聯(lián)調(diào)環(huán)境,或者預(yù)發(fā)環(huán)境的過(guò)程。
持續(xù)部署是指將交付物持續(xù)部署在生產(chǎn)環(huán)境的過(guò)程。
我們常說(shuō)CICD,CD有時(shí)候指的是Delivery交付,有的是指Deployment部署,對(duì)于非生產(chǎn)環(huán)境,自動(dòng)部署是沒(méi)有問(wèn)題的,對(duì)于生產(chǎn)環(huán)境,往往還是需要有專人來(lái)進(jìn)行更為嚴(yán)肅的部署過(guò)程,不會(huì)完全的自動(dòng)化。
接下來(lái)就是DevOps,DevOps不只是CICD,除了技術(shù)和流程,還包含文化。例如容器化帶來(lái)的一個(gè)巨大的轉(zhuǎn)變是,原來(lái)只有運(yùn)維關(guān)心環(huán)境的部署,無(wú)論是測(cè)試環(huán)境,還是生產(chǎn)環(huán)境,都是運(yùn)維搞定的,而容器化之后,需要開(kāi)發(fā)自己寫(xiě)Dockerfile,自己關(guān)心環(huán)境的部署。因?yàn)槲⒎?wù)之后,模塊太多了,讓少數(shù)的運(yùn)維能夠很好的管理所有的服務(wù),壓力大,易出錯(cuò),然而開(kāi)發(fā)往往分成很多的團(tuán)隊(duì),每個(gè)模塊自己關(guān)心自己的部署,則不易出錯(cuò),這就需要運(yùn)維一部分的工作讓研發(fā)來(lái)做,需要研發(fā)和運(yùn)維的打通,如果公司沒(méi)有這個(gè)文化,研發(fā)的老大說(shuō)我們不寫(xiě)Dockerfile,則DevOps是搞不定的。
四、從一個(gè)持續(xù)集成的日常,看上述的幾個(gè)概念如何實(shí)踐
這是一個(gè)持續(xù)集成的流程,但是運(yùn)行起來(lái)更加的復(fù)雜。
首先,項(xiàng)目開(kāi)發(fā)的流程使用的是Agile,用常見(jiàn)的scrum為例子。
每天早上第一件事情,就是開(kāi)站會(huì)standup meeting,為什么要站著呢?因?yàn)闀r(shí)間不能太長(zhǎng),微服務(wù)的一個(gè)模塊,大概需要5-9人的團(tuán)隊(duì)規(guī)模,如果團(tuán)隊(duì)規(guī)模太大了,說(shuō)明服務(wù)應(yīng)該進(jìn)行拆分了,這個(gè)團(tuán)隊(duì)規(guī)模,是能夠保證比較短的時(shí)間之內(nèi)過(guò)完昨天的狀態(tài)的。
一定要大家一起開(kāi),而不要線下去更新Jira,雖然看起來(lái)一樣,但是執(zhí)行起來(lái)完全不一樣。只有大家一起開(kāi),一起看燃盡圖,一起說(shuō)我昨天做了什么,今天打算做什么,有什么阻礙,才能夠讓大家都了解情況,不要期望大家會(huì)去看別人的Jira,經(jīng)驗(yàn)告訴你,不會(huì)的。
而且這個(gè)站會(huì)對(duì)于開(kāi)發(fā)是比較大的壓力,例如你的一個(gè)功能block了依賴方的開(kāi)發(fā),在會(huì)議上會(huì)暴露出來(lái),大家都知道這件事情了,一天block,兩天block,第三天你都不好意思去說(shuō)了,這會(huì)強(qiáng)迫你將大任務(wù),比如原來(lái)寫(xiě)1周干一件什么事情,寫(xiě)成小時(shí)級(jí)別,這樣每天你都有的說(shuō),昨天完成了一個(gè)task,而不是周只在那里說(shuō)干同樣一件事情,而且一旦有了block,team lead會(huì)知道這件事情,會(huì)幫你趕緊解決這個(gè)事情,推進(jìn)整個(gè)項(xiàng)目的進(jìn)展。讓一個(gè)技術(shù)人員在團(tuán)隊(duì)面前承認(rèn)這件事情我嘗試了幾天,的確搞不定了,也是一種壓力。
站會(huì)中的內(nèi)容其實(shí)在前一天晚上就要開(kāi)始準(zhǔn)備了。
持續(xù)集成要求每天都提交代碼,這樣才能降低代碼集成的風(fēng)險(xiǎn),不能埋頭寫(xiě)一周一起提交,這樣往往集成不成功。怎么樣才能鼓勵(lì)每天都提交代碼呢?一個(gè)就是第二天的站會(huì),你這個(gè)功能代碼提交了,單元測(cè)試通過(guò)了,第二天才能說(shuō)做完了,否則不算,這就逼得你,將大任務(wù)拆成小任務(wù),每天都多次提交。
而且Git的提交方式,是后提交者有責(zé)任去merge,保證代碼的編譯通過(guò)和測(cè)試通過(guò),你會(huì)發(fā)現(xiàn),如果你不及時(shí)提交,等你改了一大片代碼,別人都提交完了,這一大片的沖突都是你來(lái)merge,測(cè)試用例不通過(guò)的你來(lái)fix,所以逼的你有一個(gè)小的功能的改動(dòng),就盡早提交,pull一下發(fā)現(xiàn)沒(méi)有人提交,趕緊提交。
提交不是馬上進(jìn)入主庫(kù),而是需要代碼審核,這是把控代碼質(zhì)量的重要的環(huán)節(jié)。
代碼質(zhì)量的控制往往每個(gè)公司都有文檔,甚至你可以從網(wǎng)上下載一篇很長(zhǎng)很長(zhǎng)的Java代碼規(guī)范。但是我們常??吹降睦邮?,規(guī)范是有,但是虱子多了不咬人,規(guī)范太多的,誰(shuí)也記不住,等于沒(méi)有規(guī)范。
所以建議將復(fù)雜的規(guī)范通過(guò)項(xiàng)目組內(nèi)部的討論,簡(jiǎn)化為簡(jiǎn)單的10幾條軍規(guī),深入人心,大家都容易記住,并且容器執(zhí)行。
代碼審核往往需要注意下面的幾方面
代碼結(jié)構(gòu):整個(gè)項(xiàng)目組應(yīng)該規(guī)定統(tǒng)一的代碼組織結(jié)構(gòu),使得每個(gè)開(kāi)發(fā)拿到另一個(gè)人的代碼,都能看的熟悉的面孔。這也是scrum中提倡的每個(gè)開(kāi)發(fā)之間是可替代的,當(dāng)一個(gè)模塊有了阻礙,其他人是可以幫上忙的。至于核心的邏輯,估計(jì)審核人員也來(lái)不及細(xì)看,這不要緊,核心邏輯是否通過(guò),不能靠眼睛,要靠測(cè)試。
有沒(méi)有注釋,尤其是對(duì)外的接口,應(yīng)該有完善的注釋,方便自動(dòng)生成接口文檔。
異常的處理,是否拋出太過(guò)寬泛的異常,是否吞掉異常,是否吞掉異常的日志等。
對(duì)于pom是否有修改,引入了新的jar。
對(duì)于配置文件是否有修改,對(duì)外訪問(wèn)是否設(shè)置超時(shí)
對(duì)于數(shù)據(jù)庫(kù)是否有修改,是否經(jīng)過(guò)DBA審核
接口實(shí)現(xiàn)是否冪等,因?yàn)镈ubbo和springcloud都會(huì)重試接口。接口是否會(huì)升級(jí),是否帶版本號(hào)
是否有單元測(cè)試
當(dāng)然還有一些不容易一眼看出來(lái)的,可以通過(guò)一段時(shí)間通過(guò)統(tǒng)一的代碼review,來(lái)修改這些問(wèn)題。
某個(gè)類代碼長(zhǎng)度過(guò)長(zhǎng)
設(shè)計(jì)是否合理,高內(nèi)聚低耦合
數(shù)據(jù)庫(kù)設(shè)計(jì)是否合理
數(shù)據(jù)庫(kù)事務(wù)是否使用合理
代碼是否有明顯的阻塞
代碼審核完畢之后提交上去之后,一個(gè)是要通過(guò)靜態(tài)代碼審查,可以發(fā)現(xiàn)一些可能帶來(lái)代碼風(fēng)險(xiǎn)的問(wèn)題,例如異常過(guò)于寬泛等。
在就是要通過(guò)單元測(cè)試。我們應(yīng)該要求每個(gè)類都要有單元測(cè)試,并且單元測(cè)試覆蓋率要達(dá)到一定的指標(biāo)。單元測(cè)試要有帶Mock的模塊內(nèi)的集成測(cè)試。
在編譯過(guò)程中會(huì)觸發(fā)單元測(cè)試,單元測(cè)試不通過(guò),已經(jīng)代碼覆蓋率,都會(huì)統(tǒng)計(jì)后發(fā)郵件,抄送所有的人,這對(duì)于研發(fā)來(lái)講又是一個(gè)壓力。
當(dāng)有一天你的提交break掉了測(cè)試,或者代碼覆蓋率很低,則就像通報(bào)批評(píng)一樣,你需要趕緊去修改。
單元測(cè)試完畢之后,就會(huì)上傳成果物,或者是war或者是jar,一般會(huì)用nexus,因?yàn)橛邪姹咎?hào),有md5,可以保證安裝在環(huán)境中的就是某個(gè)版本的某個(gè)包,我們還遇到過(guò)有使用FTP的,這樣一個(gè)是很難保證版本號(hào)的維護(hù),升級(jí)和回滾比較難弄,另一個(gè)是沒(méi)有md5,很可能包不完整都有可能的,而且一旦發(fā)生,很難發(fā)現(xiàn)。
如果使用了容器,則還需要編譯Dockerfile,使用Docker鏡像作為交付,能夠?qū)崿F(xiàn)更好的環(huán)境一致性,保證原子的升級(jí)和回滾。
每天下班前,當(dāng)天的代碼需要提交到庫(kù)中去,晚上會(huì)做一次統(tǒng)一的環(huán)境部署和集成測(cè)試。
每天晚上凌晨,會(huì)有自動(dòng)化的腳本將Docker鏡像通過(guò)編排部署一個(gè)完整的環(huán)境,然后跑集成測(cè)試用例,集成測(cè)試用例應(yīng)該是基于API的,很多的公司是基于UI的,這樣由于UI變化太快,還有UI不能覆蓋所有的場(chǎng)景,所以還是建議UI和API分離,通過(guò)API進(jìn)行集成測(cè)試,有了每天的測(cè)試,才能保證每天晚上的版本都是可以交付的版本,也保證我們微服務(wù)拆分的時(shí)候,盡管改了很多,不會(huì)因?yàn)樾碌男薷?,破壞掉原?lái)能夠通過(guò)的測(cè)試用例,保證不會(huì)有了新的,壞了舊的。
這個(gè)集成測(cè)試或者叫回歸測(cè)試每天晚上都做,都是在一個(gè)全新的環(huán)境中,這就是持續(xù)部署和持續(xù)交付。
如果某一天測(cè)試不通過(guò),則會(huì)發(fā)出郵件來(lái),是因?yàn)楫?dāng)天誰(shuí)的哪個(gè)提交,導(dǎo)致測(cè)試不通過(guò),抄送所有人,這是另一個(gè)壓力。
所以第二天的站會(huì)上,昨天你完成了哪些功能,是否提交了,是否完成了單元測(cè)試,是否通過(guò)了集成測(cè)試,就都知道了,你需要給大家一個(gè)解釋,然后進(jìn)入到新一天的開(kāi)發(fā)。
到了兩周,一個(gè)周期完畢,可以上線到生產(chǎn)環(huán)境了,可以通知有權(quán)限的運(yùn)維進(jìn)行操作,但是也是通過(guò)自動(dòng)化的腳本進(jìn)行部署的。
這就是整個(gè)過(guò)程,層層保證質(zhì)量,從中可以看到,敏捷開(kāi)發(fā),持續(xù)集成,持續(xù)交付,持續(xù)部署,DevOps是互相聯(lián)系的,少了哪個(gè),流程都玩不轉(zhuǎn)。
五、有關(guān)代碼結(jié)構(gòu)
代碼結(jié)構(gòu)往往包括:
API接口包
訪問(wèn)外部服務(wù)包
數(shù)據(jù)庫(kù)DTO
訪問(wèn)數(shù)據(jù)庫(kù)包
服務(wù)與商務(wù)邏輯
外部服務(wù)
如果使用Dubbo RPC,則API接口往往在一個(gè)多帶帶的jar里面,被服務(wù)端和客戶端共同依賴,但是使用了springcloud的restful方式就不用了,只要在各自的代碼里面定義就可以了,會(huì)變成json的方式傳遞,這樣的好處是當(dāng)jar有多個(gè)版本依賴,需要升級(jí)的時(shí)候,關(guān)系非常復(fù)雜,難以維護(hù),而json的方式比較好的解決了這個(gè)問(wèn)題。
這個(gè)模塊提供了哪些接口,只要到API接口這個(gè)package下面找就可以了。因?yàn)闊o(wú)論是Dubbo還是springcloud,接口的調(diào)用都會(huì)重試,因而接口需要實(shí)現(xiàn)冪等。
訪問(wèn)外部服務(wù)的包,這將所有對(duì)外的訪問(wèn)獨(dú)立出來(lái),好處一是可以抽象出來(lái),在服務(wù)拆分的時(shí)候,可能會(huì)用到,例如原來(lái)支付的邏輯在下單的模塊中,要講支付獨(dú)立出來(lái),則會(huì)有一個(gè)抽象層,涉及到老的支付方式,還是調(diào)用本模塊中的邏輯,涉及到新接入的支付方式使用遠(yuǎn)程調(diào)用,有了這一層方便的多。好處二是可以實(shí)現(xiàn)熔斷,當(dāng)被調(diào)用的服務(wù)不正常的時(shí)候,在這里可以返回托底數(shù)據(jù)。好處三是可以實(shí)現(xiàn)Mock,這樣對(duì)于單元測(cè)試來(lái)講非常好,不用依賴于其他服務(wù),就可以自己進(jìn)行測(cè)試。
DTO和訪問(wèn)數(shù)據(jù)庫(kù)的包,看到了這些數(shù)據(jù)結(jié)構(gòu),會(huì)幫助程序員快速掌握代碼邏輯,不知道大家有沒(méi)有這個(gè)體驗(yàn),你去看一個(gè)開(kāi)源軟件的代碼,首先要看的是他的數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu)和關(guān)系看懂了,代碼邏輯就比較容易懂了,如果數(shù)據(jù)結(jié)構(gòu)沒(méi)看懂,則光看邏輯,就容易云里霧里的。
還有就是核心的代碼邏輯和對(duì)接口的實(shí)現(xiàn)。在這里面是軟件代碼設(shè)計(jì)的內(nèi)功所在,但是卻不是流程能夠控制的。
六、有關(guān)接口設(shè)計(jì)規(guī)范
上面也說(shuō)過(guò)了,Dubbo和Springcloud會(huì)對(duì)接口進(jìn)行重試,因而接口需要保持冪等。也即多次調(diào)用,應(yīng)該產(chǎn)生一致的結(jié)果,例如轉(zhuǎn)賬1元,因?yàn)檎{(diào)用失敗或者超時(shí)重試的時(shí)候,最終結(jié)果還應(yīng)該是轉(zhuǎn)賬1元,而非調(diào)用兩次變成轉(zhuǎn)賬2元。
冪等判斷盡量提前,可以使用ID作為判斷條件。
接口的實(shí)現(xiàn)應(yīng)該盡量避免阻塞,可以使用異步方式提升性能。
接口應(yīng)該包括能夠區(qū)分不同情況的異常,而非拋出寬泛的Exception,不能吞掉異常。
接口的實(shí)現(xiàn)要有足夠的容錯(cuò)性,以及對(duì)不同版本的兼容性。當(dāng)要引入新接口的時(shí)候,使用先添加,后刪除的方式。
接口應(yīng)該有良好的注釋。
七、有關(guān)代碼設(shè)計(jì)
對(duì)于代碼的設(shè)計(jì),這里常說(shuō)的就是SOLID原則。
S是單一責(zé)任原則,如果你的代碼中有一個(gè)類行數(shù)太長(zhǎng),可能你需要重新審視一下,是不是這個(gè)類承擔(dān)了過(guò)多的責(zé)任。
O是開(kāi)放關(guān)閉原則,比較拗口,對(duì)擴(kuò)展開(kāi)放,對(duì)修改關(guān)閉。思想是對(duì)于代碼的直接修改是非常危險(xiǎn)的事情,因?yàn)槟悴恢肋@段代碼原來(lái)被誰(shuí)用了,而且當(dāng)時(shí)候用的時(shí)候,面臨的情況都是怎樣的。因而不要貿(mào)然修改一段代碼,而是選擇用接口進(jìn)行調(diào)用,用實(shí)現(xiàn)進(jìn)行擴(kuò)展的方式進(jìn)行。當(dāng)你要實(shí)現(xiàn)一段新的功能的時(shí)候,不要改原來(lái)的代碼,也不要if-else,而是應(yīng)該擴(kuò)展一種實(shí)現(xiàn),讓原來(lái)的調(diào)用的代碼邏輯還是原來(lái)的,在新的情況下使用新實(shí)現(xiàn)的代碼邏輯。
L是里氏替換原則,如果基于接口進(jìn)行編程,則子類一定要能夠擴(kuò)展父類的功能,如果不能,說(shuō)明不應(yīng)該繼承與這個(gè)接口。例如你的實(shí)現(xiàn)的時(shí)候,發(fā)現(xiàn)接口中有一個(gè)方法在你這里實(shí)在對(duì)應(yīng)不到實(shí)現(xiàn),不是接口設(shè)計(jì)的問(wèn)題,就是你不應(yīng)該繼承這個(gè)接口,絕不能出現(xiàn)not implemented類似之類的實(shí)現(xiàn)方法。
I是接口隔離原則,接口不應(yīng)該設(shè)計(jì)的大而全,一個(gè)接口暴露出所有的功能,從而使得客戶端依賴了自己不需要的接口或者接口的方法。而是應(yīng)該講接口進(jìn)行細(xì)分和提取,而不應(yīng)該將太過(guò)靈活的參數(shù)和變量混雜在一個(gè)接口中。
D是依賴倒置原則,A模塊依賴于B模塊,B模塊有了修改,反而要改A,就是依賴的過(guò)于緊密的問(wèn)題。這就是常說(shuō)的,你變了,我沒(méi)變,為啥我要改。如果基于抽象的接口編程,將修改隱藏在后面,則能夠?qū)崿F(xiàn)依賴的解耦。
以上是模塊內(nèi)部常見(jiàn)的設(shè)計(jì)原則,對(duì)于模塊之間,則是對(duì)于云原生應(yīng)用常說(shuō)的十二原則。
詳情可云原生時(shí)代下的12-factor應(yīng)用與實(shí)踐
八、有關(guān)配置文件
在代碼倉(cāng)庫(kù)中,還需要管理的是配置文件,往往在src/main/resource下面。
配置的管理原來(lái)多使用profile進(jìn)行管理,對(duì)于dev, test, production使用不同的配置文件。
然而當(dāng)配置非常多的時(shí)候,比較的痛苦,而且配置不斷的修改,每次上線各種配置需要仔細(xì)的核對(duì),眼睛都花了,才敢上線。
我們可以將配置分為下面的三類:
內(nèi)部配置項(xiàng)(啟動(dòng)后不變,改變需要重啟)
集中配置項(xiàng)(配置中心,可動(dòng)態(tài)下發(fā))
外部配置項(xiàng)(外部依賴,和環(huán)境相關(guān))
在梳理配置的時(shí)候,可以按著三類歸類,分門(mén)別類管理。
在使用了容器之后,很多的內(nèi)部配置項(xiàng)可固化在配置文件中,放在容器鏡像中,需要啟動(dòng)的時(shí)候修改的,則通過(guò)環(huán)境變量,在啟動(dòng)容器的時(shí)候,在編排文件中進(jìn)行修改。
依賴的內(nèi)部服務(wù)的地址,在容器平臺(tái)kubernetes里面,可以通過(guò)配置服務(wù)名進(jìn)行服務(wù)發(fā)現(xiàn),僅僅在配置文件中配置名稱就可以了,不用配置真實(shí)的地址,kubernetes可以根據(jù)不同的環(huán)境,不同的namespace自動(dòng)關(guān)聯(lián)好,大大簡(jiǎn)化了配置。當(dāng)然也可以用服務(wù)中心Dubbo和Springcloud做內(nèi)部服務(wù)的相互發(fā)現(xiàn)。
依賴的外部服務(wù)的地址,例如mysql,redis等,往往不同的環(huán)境不同,也可以通過(guò)配置kubernetes外部服務(wù)名的方式進(jìn)行,而不用一一核對(duì),擔(dān)心測(cè)試環(huán)境連上了生產(chǎn)環(huán)境的IP地址。
還有一些集中配置項(xiàng),需要?jiǎng)討B(tài)修改的,例如限流,降級(jí)的開(kāi)關(guān)等,需要通過(guò)統(tǒng)一的配置中心進(jìn)行管理。
九、有關(guān)數(shù)據(jù)庫(kù)版本
代碼可以很好的版本化,應(yīng)用也可以用鏡像進(jìn)行原子化的升級(jí)和回滾。
唯一比較難做到的就是數(shù)據(jù)庫(kù)如何版本化管理。
有一個(gè)工具flyway可以比較好的做這件事情。
在代碼中,flyway需要有以下的結(jié)構(gòu):
在src/db/migration中有sql文件,命名規(guī)則,如:V1__2017_4_13.sql ,V開(kāi)頭+版本號(hào)+雙下劃線+描述,后綴為sql
增加flyway的java類,實(shí)現(xiàn)migration方法
在數(shù)據(jù)庫(kù)中,flyway會(huì)自動(dòng)增加SCHEME_VERSION表。
當(dāng)服務(wù)啟動(dòng)的時(shí)候,java類的migration方法會(huì)被調(diào)用,它會(huì)按照指定路徑中sql語(yǔ)句的版本號(hào)進(jìn)行排序并且按照這個(gè)排序去執(zhí)行,當(dāng)每一個(gè)sql文件被執(zhí)行后,元數(shù)據(jù)的表就會(huì)按照格式進(jìn)行更新。
當(dāng)服務(wù)重啟的時(shí)候,F(xiàn)lyway 再次掃描sql的時(shí)候,它就會(huì)檢查元數(shù)據(jù)表中遷移版本,如果要執(zhí)行的遷移腳本的版本小于或者等于當(dāng)前版本,F(xiàn)lyway將會(huì)忽略,不再重復(fù)執(zhí)行。
但是flyway從來(lái)不解決數(shù)據(jù)庫(kù)升級(jí)和回滾的代碼兼容性問(wèn)題。
太多的人問(wèn)這個(gè)問(wèn)題了,代碼可以灰度發(fā)布,數(shù)據(jù)庫(kù)咋灰度?代碼升級(jí)了,發(fā)現(xiàn)不對(duì)可以回滾,數(shù)據(jù)庫(kù)咋回滾。
如果可以停服的話,自然是使用數(shù)據(jù)庫(kù)快照備份的方式進(jìn)行回滾了。
如果不可以停服,沒(méi)辦法,只有在代碼層面做兼容性。每次涉及數(shù)據(jù)庫(kù)升級(jí)的都是大事情,代碼當(dāng)然應(yīng)該有個(gè)開(kāi)關(guān),保證隨時(shí)可以切回原來(lái)的邏輯。
網(wǎng)易云計(jì)算基礎(chǔ)服務(wù)深度整合了 IaaS、PaaS 及容器技術(shù),提供彈性計(jì)算、DevOps 工具鏈及微服務(wù)基礎(chǔ)設(shè)施等服務(wù),幫助企業(yè)解決 IT、架構(gòu)及運(yùn)維等問(wèn)題,使企業(yè)更聚焦于業(yè)務(wù),是新一代的云計(jì)算平臺(tái),點(diǎn)擊可免費(fèi)試用。
文章來(lái)源: 網(wǎng)易云社區(qū)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/25286.html
摘要:對(duì)于企業(yè)而言,最大的作用就是提升效率,基本上適用于所有做研發(fā)的企業(yè)。滴滴這些年的業(yè)務(wù)飛速增長(zhǎng),成為國(guó)內(nèi)第二個(gè)日訂單量超過(guò)千萬(wàn)的公司,隨之而來(lái)的是系統(tǒng)屢次出現(xiàn)線上故障,穩(wěn)定性建設(shè)成為滴滴支撐業(yè)務(wù)發(fā)展的重要保障。 最近幾年,DevOps 的發(fā)展非常迅速,如今在開(kāi)發(fā)運(yùn)維圈子里如果不懂DevOps 都不敢說(shuō)自己是混這個(gè)圈子的人。國(guó)外有人專門(mén)針對(duì) DevOps 做了一項(xiàng)調(diào)查,結(jié)果顯示在2016 ...
摘要:一些官宣的特性,在公司內(nèi)是嚴(yán)格禁止的。一個(gè)超過(guò)個(gè)表的聯(lián)合查詢業(yè)務(wù),大概率是不合理的。微服務(wù)就是一個(gè)多模塊項(xiàng)目規(guī)范化的過(guò)程。服務(wù)治理微服務(wù)最重要的特色就是其治理功能。微服務(wù)引出的另外一個(gè)問(wèn)題就是調(diào)用鏈,即某個(gè)請(qǐng)求的真實(shí)路徑。 大家都在學(xué)SpringCloud,貌似學(xué)會(huì)了SC就牛逼哄哄,感覺(jué)不得了的樣子。但微服務(wù),在整個(gè)企業(yè)級(jí)應(yīng)用中,只占了一小部分。微服務(wù)引入的問(wèn)題比解決的問(wèn)題還要多,你會(huì)...
摘要:介紹這一系列文章旨在描述面對(duì)一個(gè)從零開(kāi)始的應(yīng)用的時(shí)候如何去搭建一個(gè)業(yè)務(wù)無(wú)關(guān)的平臺(tái)承載上層應(yīng)用流量這里要強(qiáng)調(diào)這個(gè)平臺(tái)的幾個(gè)特點(diǎn)業(yè)務(wù)無(wú)關(guān)性無(wú)論業(yè)務(wù)的形態(tài)如何均可以在此架構(gòu)之上運(yùn)行這里可能會(huì)面臨若干情況如無(wú)狀態(tài)服務(wù)長(zhǎng)連接服務(wù)等后續(xù)會(huì)根據(jù)場(chǎng)景運(yùn)行描 介紹 這一系列文章, 旨在描述面對(duì)一個(gè)從零開(kāi)始的 B(C)/S 應(yīng)用的時(shí)候, 如何去搭建一個(gè)業(yè)務(wù)無(wú)關(guān)的平臺(tái), 承載上層應(yīng)用流量. 這里要強(qiáng)調(diào)這個(gè)平...
摘要:介紹這一系列文章旨在描述面對(duì)一個(gè)從零開(kāi)始的應(yīng)用的時(shí)候如何去搭建一個(gè)業(yè)務(wù)無(wú)關(guān)的平臺(tái)承載上層應(yīng)用流量這里要強(qiáng)調(diào)這個(gè)平臺(tái)的幾個(gè)特點(diǎn)業(yè)務(wù)無(wú)關(guān)性無(wú)論業(yè)務(wù)的形態(tài)如何均可以在此架構(gòu)之上運(yùn)行這里可能會(huì)面臨若干情況如無(wú)狀態(tài)服務(wù)長(zhǎng)連接服務(wù)等后續(xù)會(huì)根據(jù)場(chǎng)景運(yùn)行描 介紹 這一系列文章, 旨在描述面對(duì)一個(gè)從零開(kāi)始的 B(C)/S 應(yīng)用的時(shí)候, 如何去搭建一個(gè)業(yè)務(wù)無(wú)關(guān)的平臺(tái), 承載上層應(yīng)用流量. 這里要強(qiáng)調(diào)這個(gè)平...
摘要:介紹這一系列文章旨在描述面對(duì)一個(gè)從零開(kāi)始的應(yīng)用的時(shí)候如何去搭建一個(gè)業(yè)務(wù)無(wú)關(guān)的平臺(tái)承載上層應(yīng)用流量這里要強(qiáng)調(diào)這個(gè)平臺(tái)的幾個(gè)特點(diǎn)業(yè)務(wù)無(wú)關(guān)性無(wú)論業(yè)務(wù)的形態(tài)如何均可以在此架構(gòu)之上運(yùn)行這里可能會(huì)面臨若干情況如無(wú)狀態(tài)服務(wù)長(zhǎng)連接服務(wù)等后續(xù)會(huì)根據(jù)場(chǎng)景運(yùn)行描 介紹 這一系列文章, 旨在描述面對(duì)一個(gè)從零開(kāi)始的 B(C)/S 應(yīng)用的時(shí)候, 如何去搭建一個(gè)業(yè)務(wù)無(wú)關(guān)的平臺(tái), 承載上層應(yīng)用流量. 這里要強(qiáng)調(diào)這個(gè)平...
閱讀 1816·2021-11-25 09:43
閱讀 15529·2021-09-22 15:11
閱讀 2658·2019-08-30 13:19
閱讀 2039·2019-08-30 12:54
閱讀 1851·2019-08-29 13:06
閱讀 975·2019-08-26 14:07
閱讀 1638·2019-08-26 10:47
閱讀 3063·2019-08-26 10:41