這是一個坑系列,會說一些在系統(tǒng)設(shè)計、系統(tǒng)架構(gòu)上的坑,這些都是我想到哪說到哪,有像這篇一樣比較宏觀的坑,后面的文章也會有到具體技術(shù)細節(jié)的(比如某個函數(shù),某個系統(tǒng)調(diào)用)坑??傊?,到處都是坑,這些坑有些是我經(jīng)歷過的,有些是聽說的,你也可以留言說說你遇到的坑。 第一部分,我們從重構(gòu)這個場景來看看系統(tǒng)架構(gòu)的設(shè)計中過度設(shè)計這個坑。首先,我們這里說的重構(gòu),和《重構(gòu):改善既有代碼的設(shè)計》這本書中的重構(gòu)不太一樣,這是本好書,他主要說的是代碼級別的重構(gòu),這種重構(gòu)是需要在編碼的時候時時刻刻進行的,更多的是一種編程思想的訓(xùn)練,而下面我們要講的重構(gòu)主要是說系統(tǒng)設(shè)計的重構(gòu)。 關(guān)于架構(gòu)師 在說之前先聊聊架構(gòu)師這個職位吧,這個職位最近兩年特別特別火,哪個公司沒個架構(gòu)師好像都不好意思跟人打招呼,各位架構(gòu)師打上這個標簽后頭上就頂了一個光環(huán)了,本人也認識各個公司的一些架構(gòu)師,我認識的架構(gòu)師分成幾種:
最近還有一種說法就是架構(gòu)師到底要不要會寫代碼?我的理解是沒什么可說的,必須要會寫啊,你一個架構(gòu)師,代碼都不會寫還架構(gòu)個毛線,就算你是PPT架構(gòu)師,沒時間寫代碼,但出問題了掄起袖子來解BUG的能力得有吧,而且很關(guān)鍵的一點,架構(gòu)師面對的都是一群技術(shù)宅,你連個代碼都不會寫,你覺得下面的技術(shù)宅會看得起你么?至少你得顯得很會寫吧。為什么說架構(gòu)師呢?因為大部分架構(gòu)上的坑都是從一個不好的設(shè)計開始的,而現(xiàn)在的各個系統(tǒng)的設(shè)計都是由架構(gòu)師來操刀的。 重構(gòu)過程中的過度設(shè)計 1、重構(gòu)過程中的過度設(shè)計 技術(shù)人員最喜歡做的一件事就是重構(gòu),因為技術(shù)宅們都看不上別人的代碼,特別是需要在別人代碼上加新功能的工作更是看不上,架構(gòu)師們是技術(shù)宅的升級版,所以更加看不上別人的架構(gòu)設(shè)計,所以重構(gòu)是經(jīng)常做的事情,小的是功能模塊的重構(gòu),大的是整個系統(tǒng)的重構(gòu),總之,都是不重構(gòu)不舒服斯基。重構(gòu)本身并沒有問題,但是需要看的是重構(gòu)的時機,是不是應(yīng)該重構(gòu)了?我們以一個例子來詳細說說重構(gòu)中的過度設(shè)計吧,你也可以想想要是遇到這樣的系統(tǒng),你是架構(gòu)師,你怎么做?歡迎留言討論。 假如有個初創(chuàng)公司,是幫企業(yè)做OA系統(tǒng)的,最開始的時候是由三個程序員小哥開發(fā)出來的,系統(tǒng)架構(gòu)很簡單,是個AllInOne的設(shè)計,開發(fā)語言PHP,就像下圖一樣。 每當(dāng)客戶有新需求,基本的操作就是加個表---加個邏輯模塊---修改一下界面---上線,而且一般OA系統(tǒng)是部署在客戶內(nèi)部的,所以每次修改都是針對單獨客戶進行開發(fā)。公司發(fā)展的越來越好,有了一些大公司買了他們的OA,有錢了,請了個架構(gòu)師過來優(yōu)化優(yōu)化技術(shù)架構(gòu),架構(gòu)師叫小明(每次都黑小明),小明來了一看現(xiàn)有設(shè)計,我去,這怎么行?三天后,給出了他的建議:
于是,整個系統(tǒng)變成下面這個樣子了。 臥槽,好高大上,乍一看,這就是一個目前比較流行的架構(gòu)圖了,有數(shù)據(jù)層,有中間件層,有業(yè)務(wù)層,有前端展示層,數(shù)據(jù)層支持分庫分表,可以無限擴展,業(yè)務(wù)層微服務(wù)化,也可以無限擴展,docker部署,一個image搞定部署,簡直了! 除了消息隊列沒有用上以外,其他的流行東西用了個遍啊。那么,開始干吧,把人員分成兩撥,一撥繼續(xù)維護現(xiàn)有系統(tǒng),接客戶新需求,一撥開始重構(gòu),一個月時間,把數(shù)據(jù)和功能模塊梳理了一遍,然后開始定各個服務(wù)中的接口,又用了小一個月,然后全部人員投入進去開始編碼,又忙活了三個月,終于重構(gòu)完成了,再花一個月時間追上這4個月新來的需求,牛逼的架構(gòu)上線了!如果小明夠厲害并且開發(fā)人員也給力的話,最好的情況就是上線后一切正常,你不是重構(gòu)么?客戶完全感覺不到有變化,繼續(xù)使用得很high。但這種概率幾乎為零,最有可能的情況是什么呢?
這就是一個典型的過度設(shè)計,過度設(shè)計特點: 完全脫離了業(yè)務(wù)場景來進行技術(shù)架構(gòu)的設(shè)計就是過度設(shè)計。 這個例子中的業(yè)務(wù)場景是一個OA系統(tǒng),OA系統(tǒng)的主要數(shù)據(jù)是企業(yè)的人和人的數(shù)據(jù),一個企業(yè)能有多少人?一個初創(chuàng)公司,能接入10萬人的大公司做OA已經(jīng)非常不錯了吧?即便是10萬人的大公司,人的數(shù)據(jù)也就10萬條,我們在成100,算單個表1000萬條數(shù)據(jù)吧,單臺MySql完全可以hold得住,所以第一條分庫分表的設(shè)計在這個場景下就完全沒有必要,企業(yè)主最關(guān)心的是什么? 是數(shù)據(jù)的安全可靠,所以你在這里把MySql換成Orecle,企業(yè)主覺得安全也會買單,并且你收入還能更多,而且換成Orecle的話,單個表上億問題也不大,何必分庫分表?好,就算你數(shù)據(jù)量巨大,需要分庫分表,那你整個中間件干嘛?中間件的作用是屏蔽底層數(shù)據(jù)沒錯,但還有個場景是數(shù)據(jù)讀寫分離,一般是在大數(shù)據(jù)量并且有高并發(fā)需求的系統(tǒng)使用,你一OA系統(tǒng),能有多高的并發(fā)?需要用中間件這么高端的東西么? 再看看微服務(wù),為了降低系統(tǒng)的耦合度使用了微服務(wù),同樣場景也不對,什么時候需要把服務(wù)拆分出來呢?只有在當(dāng)前服務(wù)已經(jīng)耗費了單臺機器太多的資源了,單機扛不住了,才會把功能比較獨立的模塊拆分成微服務(wù)出去,因為微服務(wù)雖然降低了系統(tǒng)的耦合度,但是需要更多的考慮到系統(tǒng)的可用性和網(wǎng)絡(luò)因素造成的問題,對開發(fā)人員的要求更高,一個OA系統(tǒng),能有多大的計算量?后面的前后端分離啊,docker部署啊,你想想各自的必要程度如何?一個OA是否需要絢麗的界面?一個OA系統(tǒng)的更新頻率有多高?是否需要docker這樣的東西來幫助部署?成本如何?再看看是否是過度設(shè)計?最后,我覺得上面這個系統(tǒng),比較合理的修改設(shè)計是:
這樣下來,系統(tǒng)的性能應(yīng)該有提升,數(shù)據(jù)可靠性也增強了,并且也耗費不了多少資源,通過Log分析,一個局部一個局部的優(yōu)化,直到發(fā)現(xiàn)了一個大坑需要拆分服務(wù)了,再進行服務(wù)的拆分,如果你有更好的建議,歡迎留言討論啊。 2、重構(gòu)的理由 我覺得重構(gòu)得滿足以下幾個條件的大部分,才有重構(gòu)的必要,第一個條件是必須滿足的。
總而言之,重構(gòu)一個系統(tǒng)最需要考慮的就一個詞:成本,需要衡量各方面的成本后,再考慮是否需要重構(gòu),這樣的重構(gòu)才是有意義的重構(gòu)。 高可用架構(gòu)的銀彈 上面我舉例子舉的OA系統(tǒng),并不是說OA一定要這么設(shè)計,只是一種夸張的手法,為了說明后面的完全脫離了業(yè)務(wù)場景來進行技術(shù)架構(gòu)的設(shè)計就是過度設(shè)計,并不是說OA系統(tǒng)太簡單所以不能這么設(shè)計,另外,寫PHP效率低也只是打個比方,并非貶低全世界最好的語言,很多人拿這兩個來噴實在沒必要。好了,下面來說說高可用吧。 1、迷信好架構(gòu)有高可用的銀彈 高可用,我知道一旦帶上這個詞,不管寫什么都會有人有不同意見,我說說我認為的高可用下的坑吧。我想很多人理解的高可用就是單臺機器掛掉了整個服務(wù)不會掛掉,所以寫代碼的時候使用集群的思想去寫代碼,比如做成無狀態(tài)的服務(wù),保證在集群使用的時候無狀態(tài),單機故障不影響服務(wù),從而達到高可用的效果。由這種思想搭建起來的系統(tǒng)很可能長成下面這個樣子,我想很多人都看到過這種架構(gòu)模式吧。 首先,這種架構(gòu)模式本身并沒什么問題,而且也確實很好,有服務(wù)發(fā)現(xiàn),有集群,單臺機器掛掉了還有其他機器可使用,在搜索系統(tǒng),推薦系統(tǒng),廣告系統(tǒng),網(wǎng)站后臺系統(tǒng)中都在大量使用。很多人接收到的信息是有了上圖的那種架構(gòu),那么這個系統(tǒng)就變成了一個高可用的系統(tǒng)了,覺得這種架構(gòu)模式就是高可用的一顆銀彈了。但實際上,上圖的系統(tǒng)解決的主要是下面的兩個問題。
除了上面兩個問題以外,最后才是解決所謂的高可用的問題,這里用了所謂兩個字,因為我覺得高可用這種東西不是一個架構(gòu)的模式能解決的,一個高可用的系統(tǒng)是代碼級別解決的,不是靠幾個開源模塊能解決的。 有些人總認為高可用系統(tǒng)有銀彈,在各種論壇,會議上看到各種架構(gòu),而且基本上都用到了一些成熟的開源軟件,所以覺得有了這些以后就可以是一個高可用的系統(tǒng)了,我有zookeeper,那么服務(wù)單機掛了,服務(wù)照常跑,但實際上然并卵,zookeeper解決的是外部不可控因素導(dǎo)致的機器掛了,比如機器硬盤壞了,網(wǎng)絡(luò)斷了,這種因素導(dǎo)致的服務(wù)掛了,zookeeper能解決,你代碼出問題導(dǎo)致機器掛了,zookeeper下掛1000臺機器也解決不了啊,一般情況下還是一掛全掛。比如一個分布式的搜索系統(tǒng),索引分片了,所以有個集群,有50臺機器,每個分片大概10臺機器,并且機器可以動態(tài)增加減少,集群用zookeeper管理,這算高可用系統(tǒng)嗎? 這可是一個標準的搜索系統(tǒng)的高可用架構(gòu),也只能說,在代碼優(yōu)秀的前提下,這個系統(tǒng)高可用了,網(wǎng)絡(luò)問題和機器硬件問題已經(jīng)比較難搞掛整個集群了。但一旦代碼有個小bug,或者索引數(shù)據(jù)生成的時候出現(xiàn)了點問題,一般情況下,集群就全掛了,談何高可用。 高可用沒有銀彈,你在各處看到的,聽到的,學(xué)習(xí)到的各種高可用架構(gòu),他們只會告訴你這個系統(tǒng)架構(gòu)多么牛逼,用幾個框框框住某幾個模塊,然后告訴你,這個框框里的服務(wù)各種突發(fā)情況都能自適應(yīng),流量洪峰來了線性加機器就能解決,對你來說卻是然并卵,他們沒有告訴你他們的代碼有多牛逼,并且只有在這個前提下才高可用的,想純粹靠幾個框框來架構(gòu)出一個高可用的系統(tǒng),那是PPT架構(gòu)師。 真正的高可用不用糾結(jié)架構(gòu)設(shè)計,只需要代碼的健壯,健壯的代碼加上主備系統(tǒng)設(shè)計,不需要其他的,基本上就是一個高可用的系統(tǒng)了,銀行的核心數(shù)據(jù)處理中心加上異地災(zāi)備就是這樣子的,你敢說他不是高可用的?所以,寫好代碼吧,才能高可用,學(xué)習(xí)架構(gòu),更多的只是對提高系統(tǒng)全局性認識的一種補充,高可用的架構(gòu)不存在,存在的只有高可用的代碼。 2、一個栗子 我前段時間看到過這樣一個系統(tǒng),這是一個O2O的創(chuàng)業(yè)公司的后臺的一個模塊,主要功能是給剛打開APP的用戶提供一個個性化的推薦頁面,外部接入了一些其他系統(tǒng)產(chǎn)生的一些數(shù)據(jù)。數(shù)據(jù)從其他系統(tǒng)推過來以后,先是接入到一個kafka的消息隊列,數(shù)據(jù)進來了以后有一個服務(wù)的集群獲取這個數(shù)據(jù),不同的服務(wù)通過kafka不同的topic獲取,然后二次加工這些數(shù)據(jù),生成一個結(jié)構(gòu)化的個性化數(shù)據(jù),把生成的數(shù)據(jù)存到redis集群中,每個APP用戶對應(yīng)redis中一個key,前面的APP調(diào)用API以后,直接從redis集群中獲取數(shù)據(jù)返回,這些個集群都用zookeeper管理的。這么架構(gòu)出來,消息隊列是為了解決第三方數(shù)據(jù)推送太猛,做緩存用的,而redis集群其實是為了解決前端APP的高并發(fā)訪問的。 我先問了一下,消息隊列這個集群在其他系統(tǒng)模塊也在用,這沒問題,大家都要用嘛,部署一個集群也很應(yīng)該哈。但是這個redis集群只有這里在用,這里我覺得有點問題了,有必要做個帶zookeeper的集群嗎?只是為了打開APP的個性化頁面,用個redis集群?不是大家共用資源的話,我覺得完全沒必要redis集群,一主一備足矣,還容易維護。如果你覺得單機內(nèi)存不夠大,可以用redis2.0,開啟VM功能,突破物理內(nèi)存的限制,redis還能自己在內(nèi)存保持熱點數(shù)據(jù)。 你說這樣是為了解決高并發(fā)下的高可用,如果redis掛了,還能自動切換,這么說吧,我覺得一個系統(tǒng)中,排除硬件故障的問題,一般情況下,等你的服務(wù)全掛光了,redis也還堅挺著。并且redis的并發(fā)能力簡直只能用恐怖來形容,單機2,3萬的QPS(數(shù)據(jù)大小2,3kb左右)完全沒什么問題,一個創(chuàng)業(yè)公司的日活用戶量一般情況下也沒必要用集群去抗并發(fā)吧? 后來,我建議他們把redis集群干掉,換成單機主備的,而且我發(fā)現(xiàn)所謂的個性化推薦其實大部分人看到的頁面是一樣的,這也很好理解,初期沒數(shù)據(jù)的情況下,個性化推薦出來的東西也不夠豐富,redis集群的內(nèi)存使用率其實很低,于是我進一步建議他們用nginx lua的本地字典來緩存最熱的數(shù)據(jù),后面掛個redis,變成一個三級緩存(redis本地磁盤,redis內(nèi)存,nginx本地字典)。如果真的業(yè)務(wù)量上來了,換成redis集群也很容易,現(xiàn)在就沒必要浪費機器資源了,畢竟創(chuàng)業(yè)公司嘛。 嗯,最后他們沖我投來鄙夷的目光,這架構(gòu),人家看不上,萬一突然一天用戶量暴增怎么辦,而且最關(guān)鍵的是人家不差錢,好吧,呵呵。 3、高可用的銀彈在哪? 瞎扯了這么多,有沒有高可用的銀彈呢?恩,優(yōu)秀的代碼就是一切高可用架構(gòu)的基石和銀彈,優(yōu)秀的代碼加上合理的架構(gòu)就是高可用的架構(gòu),一個高可用的架構(gòu)不是靠開源軟件搭積木來得到的,成熟的開源軟件解決的是把一部分本應(yīng)該你寫的代碼變得更優(yōu)秀。 經(jīng)平臺及作者同意授權(quán)轉(zhuǎn)載 |
|