DàYé首席路 | 架構界之六識(中篇)
- 2019 年 11 月 1 日
- 筆記
佛性導讀
上回說到六識中的眼識和耳識,感受開闊視野和認真傾聽的力量。中篇我們集中聊一下鼻識(鼻嗅香)和舌識(舌嘗味),此二識的內容跨度相對更大也更駁雜一些,不求面面俱到,但求點到即說透。
鼻 識
鼻嗅香。
芬香馥郁惹人嗅,形容嗅覺靈敏,我們通常都會想到狗鼻子。狗的嗅覺靈敏度大約是人類的1000倍以上,它不僅僅可以聞到淡淡的氣味,還可以聞出主人的心情,神奇么?甚至有個說法:狗是通過鼻子來感知世界的。
架構師同樣可以靠「嗅覺」感知程式世界。完美的系統架構,食之則甘,嗅之則香。但眾生如我皆難擷完美之花,惟有香花之蜜採之,毒草之液避之。
1. 程式碼異味Code Smell
程式碼中的任何可能導致深層次問題的癥狀都可以叫做程式碼異味,這種味道通常不是錯誤,可能只是某些結構或寫法違反了基本的原則規範,當下不一定會阻止程式運行,但未來出故障的風險係數極高。
Martin Fowler的經典之作《重構》就列舉了22種典型的程式碼壞味道:重複程式碼、過長函數、過大的類…過多的注釋等。其實只要足夠敏感(高要求),很多程式碼異味都可以避免的,但就是有些人,對IDE里波浪線提示的重複程式碼視而不見,對錯誤的注釋視而不見,對相似易混淆的命名視而不見… 這些程式運行起來可能都沒有問題,但是日後來維護這些程式的人,卻可能因為這種「異味」不小心引入致命的問題。踩坑不忘挖坑人,只剩破口大罵一地雞毛。
架構師在自己做編碼或者評審他人程式碼時,對程式碼異味的嗅探能力算是打底的基本素質之一。
2. 架構異味-反模式
有設計模式,就有反設計模式。前者是高人總結抽象出的具有普適意義的通用問題解決方案,是正向的「花香」;後者與程式碼異味類似,並不一定會立即導致錯誤,但它是一種低效、有隱患、有風險的解決方案,未來有極大可能導致錯誤,是慢性的「毒藥」,也被分類整理出來,成為了反模式。
近階段我最深惡痛絕的反模式列幾個:

– 復用方形的輪子:在我看來,編程領域的很多歷史程式碼就像小龍蝦的頭。每次把頭剝下來看到蝦黃,都要吸兩口汁水生怕浪費,即便知道頭部里都是龍蝦的器官,容易積聚一些有害物質。而這些已經染「毒」了的歷史程式碼,復用一次就是引入一次危險。就像方形的輪子,壓根跑不動跑不遠。若問為什麼用這個輪子,經常得到的回復是「這是之前的程式碼,一直這麼跑著的,我也沒管」。瞧,鍋甩走了…架構師若不能對輪子瞭若指掌,想當然的不求甚解,是不負責任也是不合格的;
– 紊亂的結構分層:微服務體系下的業務系統層次相對比較清楚,比如簡單粗暴的Controller-Business-Service,但就是架不住程式設計師那顆躁動的內心,似乎特別喜歡在這種地方做文章彰顯自己的「設計」能力。曾經看到一個並不怎麼複雜的業務系統,除了剛才說的三層,還設計好多層helper/component/handler/adapter…可能設計者覺得層高高一些,視野會更美麗吧。反正我是看懵逼了,無端增加了很多閱讀和理解成本。最讓人啼笑皆非的是,剛到第三個迭代周期,設計者自己已經分不清各層的調用關係,開始亂來了;
– 無視可預見的擴展:「留待日後優化」是程式設計師的一句世紀大謊言。相信我們都有一顆精益求精的心,若已預見到的擴展,比如將一個hardcode變數改成可動態配置,也就分分鐘的事,就動手去做了吧,不要給自己的懶惰找借口,好么?拉鉤上吊。
3. 性能異味-調優
能聞出性能的味道來,是比較高端的玩家了。性能涉及的邊界實在太廣,從前端到後端到資料庫,從協議到執行緒到網路,從記憶體到磁碟到快取…簡直無所不包。
性能沒有盡頭,當還在用鎖來解決資源競爭問題的時候,無鎖設計出現了;當多核並發處理能力到瓶頸的時候,單核單執行緒極限壓榨性能的設計出現了(參考LMAX架構)…性能問題永遠是架構師躲不過去必須面對的一道坎兒,你至少得先知曉它,才能分析它直到解開它。
然後特別重要也是特別容易被忽視的一點,就是性能優化不僅僅技術單一維度,還有業務維度。相信你一定碰到過介面上只是多展示一個參數,後端介面不得不做超級複雜的邏輯控制和計算,由此引入性能問題。比如剩餘庫存量在秒殺頁面的展示,比如不同類型的交易流水(跨庫)合併在一頁上展示,再比如列表頁的留言評論數顯示具體數字還是999+…產品設計稍微做個妥協或者轉換就可以避免好多問題,包括性能。
關於性能優化,部分技術點參見:高性能Web應用的優化技術
4. 安全異味-漏洞
程式設計師三界內還有一些超然的角色,現在可以出場了,比如黑客。他們大多低調內斂,卻時而性格乖張,陰晴不定,擁有翻雲覆雨的能力,這種能力可以讓你的系統、你的生意瞬間灰飛煙滅,黑客眼裡的世界從來都是千瘡百孔的。
資訊安全就是保護資訊資產不受偶然或者惡意的侵犯,而遭受破壞、更改或者泄露。為此我們不得不在資訊流動的所有環節考慮安全性,如數據層、應用層、系統層、網路層和物理層。經常被忽視的還有一類,就是業務安全。如何在複雜的業務流轉和依賴中,識別出漏洞並修復,也是架構師的職責之一。比如最基本的輸入正確的支付密碼才能付款成功,如果因為系統設計的問題,導致可以繞過輸密碼這個環節,那這個業務就極度不安全了。
雖說「沒有絕對的安全」,「三份技術,七分管理」,架構師作為技術塔尖的代表,用技術來確保安全責無旁貸。架構師眼裡的一方世界應該是美好的,修起高牆堵上漏洞挖開護城河,保這一方世界的安寧。
本小節收個尾。曾有研究者分析了上百種氣味的化學結構,得到7種基本氣味(無從考證):樟腦味、麝香味、花卉味、薄荷味、乙醚味、辛辣味和腐腥味,有馨香有醒腦有刺鼻。那麼當把一個系統放到架構師的面前,架構師基本可以對系統的基本氣味(健康/健壯/安全…等)有感覺,算達標;若對各種氣味瞭然於胸並有針對性方案,就是高手了。
遙知不是雪,為有暗香來。
-王安石《梅花》

舌 識


舌嘗味。
HOLD ON, 到這裡我覺得不少人開始頭疼嗅覺和味覺的事了。沒錯,上面鼻識講的「味」是「氣味」,靠嗅覺;而舌識的「味」是「味道」,靠味覺。吃任何美食,舌頭上的味蕾和鼻內的嗅細胞需一同將訊號傳送至腦內,兩種官能訊號合併後成為人的最終體驗。其實,嗅覺在美食還沒入口之前就發生了,入口後才是嗅覺、味覺兩者的合作。試試捏著鼻子吃東西,90%情況都是感覺不到「味道」的,可見嗅覺之於味覺的重要性。我們可以把嗅覺(鼻識)當做一種遠觀和直接感受,把味覺(舌識)當做一種近觀和咀嚼感受。
那麼,架構師應該嚼吧出哪些味道呢?這裡我先把味道簡單劃分為三道層次:前味、主味和回味,能品出層次的人才是合格的美食家、品酒師。
1. 模型的味道
我在這裡把很多概念都歸類為了模型。通用設計模式之上搭建的系統可視作一種通用模型,DDD是一種領域模型,TDD是一種測試驅動模型,BDD是一種行為驅動模型,微服務本質就是一種高內聚松耦合服務模型,而近期火熱的中台更是一種說不清道不明但就是具有迷人氣息的抽象模型…模型單單放在那裡的時候,你聞不出什麼氣味(好與壞),而一旦模型實施落地,個中酸甜苦辣都端上來了任君品嘗。
第一步: 選型(前味)
任何從0到1的過程,架構選型是避不開的。需要架構師對模型本身要有充分的了解,模型之間的優劣更要了解,模型與團隊專業、業務戰略的匹配度也要充分評估。這一步是很重要的一道前菜,且稱為「前味」。
第二步: 落地(主味)
模型落地的學問就更大了。首先,模型所需的相關技術點和規範點需要與團隊明確吧,若團隊內部未達成一致,不管多牛逼的模型被不同的人向不同的方向拉扯,最終都會變成漿糊。其次,實操過程中難免會碰到一些或明或暗的坑,架構師應該仔細辨別,儘早提醒團隊。比如rpc框架序列化的坑,maven jar包依賴版本衝突的坑,日誌列印控制台導致catalina.out過大的坑等等。最後,等系統全部READY上線,儘管之前經過了大量縝密的測試,但還是讓人心慌慌,怎麼辦?這時候需要拿到線上系統的運行數據、工單數據,分析出瓶頸和隱患,給出最合理的優化修復方案。此步驟算是主菜即「主味」。
第三步: 演進(回味)
系統架構需要演進,其驅動力的本質是由於架構成熟度與業務規模/預期之間的不匹配。通常情況是技術被業務所驅動的升級,這時候的演進不能只靠監控和踩坑來實現,需要大局觀和更開闊的視野方可。另外近些年的情況有所轉變,隨著一些新型技術的噴薄而出,技術開始領先驅動業務場景的不斷落地,比如區塊鏈、圖計算、人工智慧、5G、IoT等技術催生出很多行業新玩法和想像空間。此步稱為「回味」,除了需對現有系統架構有充分的理解之外,還需對未來系統架構的走向路線有一個清晰的預判和決策。還是要再強調一下,演進不能無節制的追逐熱點,不管你有多想做中台,多想做cloud native,它們並不一定適合你。
2. 數據的味道
「數據」是程式世界的底座,無數據不架構。這句話不是危言聳聽,在沒有考慮好數據如何持久化,如何分片,如何快取的情況下,談架構都是耍流氓。通常的概要設計里都會有的ER圖,DDD里的領域實體,實際上都與最終的數據設計有千絲萬縷的聯繫。數據的味道該如何品嘗?
資料庫的選擇
現在的資料庫品類琳琅滿目,看看DB-Engines收錄的主要品類就這麼多了,關係/KV/文檔/列式/圖/時序/對象…等等。還是那句話套用一下就是,沒有最好的資料庫只有最適合的。有些人索性不選了直接找支援多模型的資料庫,期望一個資料庫一同將上基本不太現實,所以未來可期的架構方式八九不離十就是它了:
Polyglot Persistence 混合持久化
其實說的就是不同場景選用不同的資料庫,但是這種玩法的後遺症也很突出:複雜性和運維難度升高,哪天需要關聯兩種不同類型的資料庫,可能面臨「臣妾做不到」的尷尬。

資料庫的設計
關係型資料庫的設計無非表、列、主鍵、索引;列式資料庫如HBase無非rowkey、列族;KV數據如Redis無非Key、數據結構;圖資料庫無非點、邊和屬性…當面對不同的數據量級、請求量級和資料庫分散式部署方式,上面的設計會翻天覆地。
比如熱點數據的Key應該設計為怎樣的字元串組合,時間字元在開頭的話就是順序的Key,若想Hash均勻(分片)那就可以考慮時間字元放在Key的末尾之類;
打破固有認知,比如MYSQL里的唯一索引和普通索引,在業務允許的情況下,不出意外直接選擇唯一索引,有了業務約束又有了更高的性能(Really?) 其實並不然。唯一索引和普通索引的檢索性能基本近似,但是對於更新,唯一索引需要做唯一性校驗,壞就壞在這裡,校驗需要比對物,就需要讀出數據頁數據判斷與更新後的值是否衝突,唯一索引這裡無法使用普通索引可以使用的Change Buffer;
打破思維定勢,比如基於關係型的建模與基於圖的建模差別甚大。一個簡單的Friend社交關係,關係型就是一張表的兩個列,而圖裡面就是Person點+Friend邊;
數據的價值(數據倉庫到大數據平台)
數據的話題太大了,趕緊收個尾。數據的價值這裡把數據倉庫、數據挖掘、數據可視化等等都Cover了。架構師這裡有一項重要的取捨能力表現:實時數據 VS 離線數據。千萬不要為了展現自己強大的架構設計能力,把一些明明是OLAP需求非得做成OLTP業務,把線下離線分析做成了線上實時生成,不要對自己太狠。
現在的互聯網企業應該都有自己或大或小的「大數據平台」了,是不是名副其實不清楚,倒是有不少「大數據平台」本質就是個「數據倉庫」。真正的「大數據平台」其上應該會在多種類數據之外架設更多的內容,比如流式計算、批處理、元數據、調度、品質管理、告警監控等。
數據之上就是應用,做決策、推薦、預測、運營、可視化,它們的前提都是高品質的數據。先拋開數據清洗、數據完整性等概念,最基本的兩張表同一個冗餘欄位的類型都不同,坑就這麼埋下了。
3. 交付的味道
任何的軟體系統最終都是為了交付。架構師的成就感峰值有兩種極端:一種就是交付的那一刻,對自己架構作品的線上表現充滿了信心;還有一種是架構設計新鮮出爐的那一刻,因為還沒經過任何驗證和挑戰,成就感達到峰值,伴隨著測試的深入和BUG的湧現,交付的那一刻反而是成就感的低谷。
持續集成CI & 持續交付CD
我就不再重複解釋這兩個概念了,現在應該鮮有公司還沒玩過的。而架構這塊要強調的可不是怎麼玩持續交付(當然了解清楚是必要的),而是表達架構應該在持續交付的過程中不斷試錯、持續迭代的,讓每一次的交付成就一次全新的峰值體驗。
面向交付編程
有個說法,「任何你寫的程式碼,半年後再看,怎麼看怎麼像別人寫的。」 程式碼是什麼?是給人閱讀的,給電腦運行的,給後來人吐槽的。那麼程式碼編程到底應該面向什麼?正常點的有面向過程、面向對象、面向函數編程,搞笑點的有面向工資、面向工期、面向CP(Copy&Paste)編程,我這裡要嚴肅的提出「面向交付編程」,程式設計師的交付物可不僅僅只有程式碼,還有很多很多…
在部分人的眼裡,寫程式碼實現業務功能,沒有什麼BUG,就算是成功的交付了,其實這離成功還差十萬八千里。我這裡就拋出幾個問題作為基本的checklist:
– 系統是自恰的么?(單元測試、冪等、無狀態)
– 系統是透明的么?(介面說明、日誌軌跡、埋點監控、易於排障)
– 系統是靈活的么?(動態可配、動態伸縮、熱部署)
– 系統是安全的么?(防攻擊、防泄漏、反偽裝、防人禍…)
總結下四個單詞:
自恰 self-Consistent
透明 Transparent
靈活 Flexible
安全 Safe
縮寫為面向交付的四維度 CTFS,從易記的角度可以讀作 從頭反思。可不就是從需求到交付到運營整個流程從頭到尾的不斷反思不斷優化么?架構師的眼界必須放到產品研發的整個鏈路上,交付的是系統,是價值,更是未來。