(研發(fā)中心?? 尹書偉)
剛接到要我到北京參加MPD大會通知的時(shí)候,我并不感到有多少激動(dòng)與期盼,畢竟參加這樣的會議多了,沒有多少新鮮感。不過,每次去北京都有不同的體驗(yàn)。早先去的時(shí)候全國都沒有高鐵,大約得十六七個(gè)小時(shí)的樣子。那時(shí)候經(jīng)驗(yàn)也少,自然覺得學(xué)到的東西很多,什么都新鮮。那時(shí)候沒有智能手機(jī),一切要提前規(guī)劃好,尤其是下了飛機(jī)或火車之后怎么走要提前弄明白,甚至需要在紙上記下來。后來有了高鐵,有了智能手機(jī),有了專車,隨著互聯(lián)網(wǎng)的快速發(fā)展,一切方便了很多。由于這些便利,我也不再需要提前記下怎么走了,到了以后再用地圖導(dǎo)航就好了。然而這次去北京,剛到出站口,發(fā)現(xiàn)手機(jī)已被停機(jī),這意味著我自己徹底與信息時(shí)代隔離,甚至想給自己的手機(jī)充值都不可能,因?yàn)闆]有信號沒流量。晚上十點(diǎn),身處一個(gè)完全陌生的地方,我不得不借助于原始的信息獲取手段——問路。經(jīng)歷過這樣的窘境,才更加意識到互聯(lián)網(wǎng)飛速發(fā)展所帶來的便利。上次去北京,體驗(yàn)到的是專車的便利,而這次讓我體驗(yàn)了一下遍地五顏六色的共享單車的便利。
在到達(dá)會場之前,我已經(jīng)在課程選擇上拿好了主意,最感興趣的課程有兩個(gè):大型系統(tǒng)、遺留系統(tǒng)的微服務(wù)改造實(shí)踐和DevOps企業(yè)化部署之路。然而到達(dá)會場之后卻發(fā)現(xiàn)課程有所調(diào)整,這兩個(gè)課程都被換掉了。不能不說這是非常令人遺憾的。從講師陣容上來看,應(yīng)該還算說得過去,大多為互聯(lián)網(wǎng)應(yīng)用的一些牛人。我基本就在架構(gòu)設(shè)計(jì)專屬教室留了下來。
首先上場的是餓了么研發(fā)中心總經(jīng)理史海峰,題目看上去很潮的樣子:《當(dāng)我們談?wù)摷軜?gòu)的時(shí)候我們在談?wù)撌裁??》。一共分兩個(gè)半場,都是套路,上半場上來提出什么是架構(gòu)之類的問題,學(xué)員先發(fā)表自己的見解,然后史海峰給出他自己的看法,下半場也大致如此,只不過話題在什么是架構(gòu)師這樣的話題上。這樣的課程,我自認(rèn)為沒啥干貨,只不過是概念性的普及而已。消息隊(duì)列、讀寫分離、最終一致、冪等、避免分布式事務(wù)、數(shù)據(jù)庫主鍵與業(yè)務(wù)無關(guān)等,都只是泛泛而談,我想大家也都對這樣的理念不陌生。在談到技術(shù)債的時(shí)候,很多人現(xiàn)身說法,每個(gè)人都認(rèn)為自己在為前人還債,有個(gè)人甚至一還就是八年,最后不堪重負(fù)走人了。談到技術(shù)債,深有感觸,誰會愿意替人還債呢?然而不是每個(gè)項(xiàng)目都能夠從頭開始設(shè)計(jì),即便是從頭開始設(shè)計(jì)開發(fā),只要在工期壓力,領(lǐng)導(dǎo)壓力下,欠債和偷懶是難免的。作為架構(gòu)師應(yīng)該施加影響,確保自己的設(shè)計(jì)得到廣泛支持并得到不折不扣的執(zhí)行,避免欠債。有時(shí)候故意欠債也是一種無奈的權(quán)衡,但必須記得及時(shí)還債,免得債務(wù)越積越多,最后只能全部賴賬了。技術(shù)債要管理,首要一條自然是立即停止欠債。技術(shù)債只是個(gè)形象的比方,真實(shí)項(xiàng)目中什么是技術(shù)債受限于人們對軟件開發(fā)認(rèn)識的深度。很多人認(rèn)識不到自己所欠的技術(shù)債,也許還自我感覺很美。有的時(shí)候意識到了,但是總是想著一下子把債務(wù)還清,一下子變成無債一身輕。必須意識到,債務(wù)是長年累月累積的,要想償還必須付出巨大的利息,需擺正心態(tài),做好長期還債計(jì)劃,剝絲抽繭慢慢來。
微服務(wù)相關(guān)的內(nèi)容換成了京東開放平臺架構(gòu)師王棟的《從京東微服務(wù)架構(gòu)演進(jìn)看互聯(lián)網(wǎng)高可用架構(gòu)設(shè)計(jì)》,臨時(shí)救場臨時(shí)準(zhǔn)備,卻比上一場的哲學(xué)式的空談更能提起我的興趣。當(dāng)然,架構(gòu)的演進(jìn)最終到了微服務(wù)架構(gòu)這一模式,自然少不了要強(qiáng)調(diào)一下適合的架構(gòu)才是好架構(gòu)。適合有多重含義,不僅僅指適合項(xiàng)目,更要適合團(tuán)隊(duì),只要團(tuán)隊(duì)能駕馭得了,那就OK。
單體架構(gòu)的最大劣勢就是隨著規(guī)模的增大,維護(hù)成本呈指數(shù)級增長。這一點(diǎn)我公司是有很多教訓(xùn)的。團(tuán)隊(duì)的持續(xù)交付周期變長,正確添加新功能變得非常困難,溝通、管理和協(xié)調(diào)成本必然顯著增加。新人培養(yǎng)周期長,通常也是線上事故的制造者。可伸縮性差,難以擴(kuò)展。

微服務(wù)架構(gòu)使得巨大的單體應(yīng)用被分解為很多服務(wù),并且技術(shù)開發(fā)團(tuán)隊(duì)也得以完全分離,獨(dú)立部署使得團(tuán)隊(duì)之間不再需要協(xié)調(diào)部署,這使得持續(xù)部署成為可能。而且每個(gè)服務(wù)根據(jù)自身特點(diǎn)得以獨(dú)立擴(kuò)展,獨(dú)立決定運(yùn)行所需軟硬件資源。由于每個(gè)服務(wù)所支持的業(yè)務(wù)變得很小,新人所需了解的業(yè)務(wù)知識也就很少,培養(yǎng)周期大大縮短。由于隔離去耦,新人造成的麻煩被限定在可控范圍。
關(guān)于如何拆分服務(wù),王棟給出了一些原則,當(dāng)然這些原則都是耳熟能詳?shù)?,并不是他自己的發(fā)明?;驹瓌t是低耦合,高內(nèi)聚。一個(gè)服務(wù)只負(fù)責(zé)一件事情,可以獨(dú)立部署。這個(gè)原則,其實(shí)在其它層面也成立,比如一個(gè)類應(yīng)該只負(fù)責(zé)一個(gè)主要職責(zé),一個(gè)方法應(yīng)該只做一件事。橫向按照系統(tǒng)功能劃分,比如:接入子系統(tǒng)、消息推送子系統(tǒng)、客戶端監(jiān)控管理子系統(tǒng)等等。 縱向根據(jù)業(yè)務(wù)模塊劃分,清楚各個(gè)服務(wù)的職責(zé)邊界是什么,比如:購物車、訂單管理、單品頁、實(shí)時(shí)庫存等等。要堅(jiān)持最小知識原則,一個(gè)組件或者是對象不應(yīng)該知道其他組件或者對象的內(nèi)部實(shí)現(xiàn)細(xì)節(jié)。不允許循環(huán)依賴,不允許從低層向高層依賴。根據(jù)業(yè)務(wù)發(fā)展情況不同,公司所處階段不同,服務(wù)拆分的粒度也不相同。粒度越小,維護(hù)成本越大。服務(wù)多了以后又會產(chǎn)生新的混亂,因此服務(wù)一定要治理。
像很多人一樣,我也曾經(jīng)苦苦的尋找軟件領(lǐng)域的救世良方,以為那些大牛手里有不為人知的秘笈,有了它一切都被完美解決。但我早已領(lǐng)悟到,很多大師也告訴過我,沒有銀彈。如果說有的話,還是那些人人皆知的原則,哪怕做到一條,比如說真正做到高內(nèi)聚低耦合,那就不用擔(dān)心自己的修改會對外部其他人造成影響。但是就這一條,如果你是以數(shù)據(jù)為中心編程,那就不可能低耦合,因?yàn)閹毂砗蚐QL訪問沒有訪問級別保護(hù),所有的應(yīng)用深度耦合到了一起。王棟也多次強(qiáng)調(diào)了這一點(diǎn),一定不能直接訪問其它團(tuán)隊(duì)的庫表,一定是通過服務(wù)來進(jìn)行訪問,定義好接口。當(dāng)然也不能有所謂的公共庫表,比如檔案,你會說大家都要用,所以都可以隨便訪問。但這會破壞高內(nèi)聚低耦合原則,所以這也必須進(jìn)行服務(wù)封裝,通過服務(wù)來訪問。其實(shí)封裝和隱藏就能夠極大地提升系統(tǒng)的穩(wěn)定性和可維護(hù)性,怎么強(qiáng)調(diào)都不過分。實(shí)施起來的關(guān)鍵點(diǎn)是,讓成員形成意識,像防賊一樣防止他人侵入我的實(shí)現(xiàn)細(xì)節(jié)當(dāng)中。如果人人都防范起來,那么每個(gè)人都安全了,耦合就會降低,系統(tǒng)就會穩(wěn)定。
現(xiàn)在我們已經(jīng)知道了足夠多的知識,只是沒有人會靜下心去真正實(shí)踐這些真知灼見。我們要知道的是一切并沒有那么神秘,并沒有那么難,你只要去做,不過如此而已。這也是《從bag of words 到alpha GO》題目的作者李睿的忠告。李?;旧辖o出的都是干貨了,從簡單的算法到神經(jīng)網(wǎng)絡(luò)娓娓道來。最后,告誡我們,不知道感覺很神秘,知道了也就那么回事,然而,復(fù)雜的算法是要付出代價(jià)的,應(yīng)該選擇能夠解決問題的最簡單辦法來解決問題。
最后,分享一下王棟提供的兩個(gè)案例,這兩個(gè)案例各有特點(diǎn)。反復(fù)強(qiáng)調(diào)的是數(shù)據(jù)庫不是用來走量的,僅在必要時(shí)走數(shù)據(jù)庫。另外,也可以看到,一提到數(shù)據(jù)量大,我們往往就想到大數(shù)據(jù)技術(shù),這也是一種誤區(qū),關(guān)鍵還是要看場景,對王棟看來不到10億那都是很少的數(shù)據(jù)。還有一點(diǎn)要注意,在第二個(gè)案例用了三層緩存,這也是有代價(jià)的,不能盲目模仿,如果我們沒有那么苛刻的響應(yīng)時(shí)間要求,一層緩存效果就很好了。

?在第一個(gè)案例中主要設(shè)計(jì)是加入了緩存,數(shù)據(jù)庫分表和讀寫分離。緩存是第一位的,替數(shù)據(jù)庫抗住了量。在有數(shù)據(jù)更新時(shí),不得不直通數(shù)據(jù)庫,更新完數(shù)據(jù)庫后,則同步更新緩存,異步更新Solr(目前京東開始準(zhǔn)備用ES替換Solr)。出現(xiàn)問題降級,直走緩存。




在第二個(gè)案例中,由于數(shù)據(jù)量不大,因此就不用關(guān)系型數(shù)據(jù)庫了,直接持久化到Redis中。雖然數(shù)據(jù)少,但是為了滿足高性能高可用特性,不惜金錢,采用雙機(jī)房雙活方式,而且還要分片。加了三級緩存,第一級就是普通的分布式緩存,第二級在應(yīng)用服務(wù)器所在節(jié)點(diǎn)增加緩存,避免掉了網(wǎng)絡(luò)開銷,第三級增加JVM緩存,避免了跨進(jìn)程開銷。這樣的架構(gòu)部署完了以后,任何請求的相應(yīng)時(shí)間都在1-3毫秒之間,絕不會超過5毫秒。