北哥在前文陸續(xù)總結(jié)了程序員成長所具備的核心能力竿痰,以及Java程序員成長過程中應學習的基礎(chǔ)知識肖抱。
在一個Java程序員工作3、5年之后诫欠,已經(jīng)可以承擔起大部分的核心開發(fā)工作涵卵,成長為團隊中的高級開發(fā)人員。大部分工作中遇到的問題都已經(jīng)可以自行解決荒叼。這個階段很多同學會面臨著新的成長困惑轿偎,到底接下來自己還需要在哪些方面繼續(xù)提升?如何能夠成長為團隊里面的架構(gòu)師呢被廓?
市面上有很多分析和拆解架構(gòu)師能力的書籍坏晦,例如《聊聊架構(gòu)》《億級流量網(wǎng)站架構(gòu)核心技術(shù)》《大型網(wǎng)站技術(shù)架構(gòu):核心原理與案例分析》等,書中有一些相關(guān)的實戰(zhàn)和理論知識分享嫁乘。這些書籍如果有時間推薦大家去讀一讀昆婿。
今天北哥結(jié)合自己工作中的經(jīng)驗,也來淺談一下如何做應用系統(tǒng)架構(gòu)設(shè)計蜓斧。
需要說明的是仓蛆,架構(gòu)師本身要求的是綜合能力,架構(gòu)也分為業(yè)務架構(gòu)挎春、應用系統(tǒng)架構(gòu)看疙、技術(shù)架構(gòu)、數(shù)據(jù)架構(gòu)等多個維度多個細分領(lǐng)域直奋,但今天分享的關(guān)于架構(gòu)的相關(guān)內(nèi)容能庆,更多還是側(cè)重在服務端的應用系統(tǒng)架構(gòu)。
01?指導思想
作為一個架構(gòu)師帮碰,在做應用系統(tǒng)架構(gòu)時相味,最好逐步沉淀自己的一套指導思想,指導思想用于在做架構(gòu)設(shè)計過程中遇到困惑或遇事不決時的一個指引殉挽。我個人總結(jié)下來的經(jīng)驗有以下三點
平衡和取舍
架構(gòu)是一個復雜的工作丰涉,既要考慮當下的需求,還要關(guān)注未來可能的變化斯碌;既要考慮的足夠全面一死,還要簡單容易實現(xiàn);既要衡量實現(xiàn)成本傻唾,還要關(guān)注落地的效率投慈。這些無不意味著在做架構(gòu)時需做好平衡,學會取舍冠骄。
迭代和演進
一個好的架構(gòu)伪煤,一定是經(jīng)過長期迭代演進而來的。在做架構(gòu)設(shè)計時凛辣,受限于當前已經(jīng)明確的需求抱既,往往無法對未來考慮的那么全面。即使在做架構(gòu)設(shè)計時已經(jīng)考慮到了方方面面扁誓,系統(tǒng)上線后也會遇到一些新的未知問題防泵,并且隨著需求的不斷迭代蚀之,又會引入新的變化和挑戰(zhàn),因此持續(xù)的對架構(gòu)做優(yōu)化和迭代演進捷泞,是必須要重視的足删。
聚焦業(yè)務需求
沒有毫無瑕疵完美的架構(gòu),也沒有一成不變適合所有業(yè)務的架構(gòu)锁右,只有適合當前業(yè)務和產(chǎn)品需求的架構(gòu)失受。我們可以借鑒、吸收別人的經(jīng)驗和實踐總結(jié)咏瑟,但最合適的架構(gòu)一定是結(jié)合業(yè)務的實際需求設(shè)計和演變出來的贱纠。脫離業(yè)務需求設(shè)計的架構(gòu)一定會在開發(fā)中遇到新的問題。
02?架構(gòu)設(shè)計目標
在做應用系統(tǒng)架構(gòu)設(shè)計時响蕴,應遵循一些普適的架構(gòu)設(shè)計目標。這些目標包括
可實現(xiàn)的:架構(gòu)被設(shè)計出來惠桃,一定是需要能夠被實現(xiàn)和落地的浦夷,如果設(shè)計架構(gòu)所采用的技術(shù)還沒成熟或不具備生產(chǎn)環(huán)境可用性,這種架構(gòu)只能停留在理論階段辜王,不具備落地的可行性,這樣的架構(gòu)也就沒有特別大的價值和意義。
可擴展的:雖然無法預知需求未來的變化媳友,但有些場景下丛版,我們設(shè)計的架構(gòu)也應該具備當用戶量、帶寬汹来、數(shù)據(jù)量等增長時续膳,具備較好的擴展性;除此之外收班,在做需求分析時坟岔,把系統(tǒng)、領(lǐng)域服務甚至模塊做好規(guī)劃和設(shè)計摔桦,能夠合理的應對未來需求膨脹可能帶來的問題社付,也是架構(gòu)可擴展性的一種體現(xiàn)。
高可用的:系統(tǒng)架構(gòu)需要考慮各種異常情況下系統(tǒng)可用性邻耕,如流量的突然爆發(fā)的消峰限流鸥咖,某個服務或接口故障后的降級等,通過在架構(gòu)中設(shè)計的各種措施保障整個應用系統(tǒng)是高可用的兄世。
可衡量的:應用系統(tǒng)架構(gòu)設(shè)計最終都需要落地到代碼層面實現(xiàn)啼辣,提供出可線上發(fā)布的系統(tǒng)和服務。我們在做系統(tǒng)架構(gòu)設(shè)計時碘饼,需要根據(jù)業(yè)務數(shù)據(jù)的預估熙兔,設(shè)計出滿足一定量級的系統(tǒng)數(shù)據(jù)指標悲伶,將架構(gòu)設(shè)計的結(jié)果量化。在遇到系統(tǒng)負載過高需要擴容時住涉,就可以參考定量的數(shù)據(jù)指標預估出所需的服務器和資源等麸锉。
接下來從業(yè)務需求分析開始,到最終系統(tǒng)上線運營舆声,按照不同階段需要關(guān)注的知識和內(nèi)容花沉,來說明整個應用系統(tǒng)架構(gòu)設(shè)計過程需要考慮的問題。
03?需求分析階段
正如前文所述媳握,系統(tǒng)架構(gòu)應該是聚焦業(yè)務需求的碱屁,業(yè)務需求分析,是做系統(tǒng)架構(gòu)設(shè)計的必要前置蛾找。這里簡述下我在需求分析階段工作的大致思路娩脾。
正常情況下,進入到架構(gòu)師層面的需求打毛,都經(jīng)過了業(yè)務人員和產(chǎn)品人員至少一輪的討論溝通柿赊,或已經(jīng)有基本的業(yè)務和產(chǎn)品需求雛形,或已經(jīng)產(chǎn)出業(yè)務的MRD(業(yè)務需求說明書)或產(chǎn)品的PRD(產(chǎn)品需求說明書)幻枉。這時架構(gòu)師進入后需要再和業(yè)務及產(chǎn)品同學進行充分的討論溝通碰声,明確和產(chǎn)出以下幾點信息:
確定清楚業(yè)務目標,搞清楚業(yè)務需要解決的核心問題是什么熬甫,通過產(chǎn)品和系統(tǒng)期望帶來的效果是怎樣的胰挑。
進行業(yè)務流程梳理和業(yè)務建模。業(yè)務建模的核心在于梳理清楚用戶角色椿肩、業(yè)務場景瞻颂、流程和相關(guān)規(guī)則策略。弄清楚不同用戶角色在哪些場景下可以進行什么樣的操作覆旱,由此對業(yè)務有更清晰的全局認識蘸朋。
根據(jù)業(yè)務模型、業(yè)務產(chǎn)出的MRD或產(chǎn)品產(chǎn)出的PRD扣唱,整理出大概的子系統(tǒng)和功能列表藕坯。功能列表先從第一層級梳理,再層層細化噪沙,一般以不超過四層為宜炼彪。如第一層級分為用戶管理、訂單管理正歼、售后退換貨管理等辐马。第二層級中用戶管理再細分為注冊登陸,基礎(chǔ)資料維護局义、禁用啟用等喜爷,依次類推冗疮。
根據(jù)業(yè)務模型和功能列表,預估和完善非功能性需求檩帐,如對總用戶量术幔、同時在線用戶量、系統(tǒng)訪問量等非功能需求湃密,系統(tǒng)性能诅挑、響應時間、qps等系統(tǒng)性能要求泛源,系統(tǒng)工期拔妥、人員投入、項目組織結(jié)構(gòu)达箍、其它成本等項目約束條件等没龙。
在需求分析階段,我會最終產(chǎn)出一份基于業(yè)務模型拆解出的功能需求列表和非功能需求列表缎玫。這將作為后續(xù)應用系統(tǒng)架構(gòu)的核心參考和依據(jù)兜畸。
04?架構(gòu)設(shè)計階段
在需求分析階段完成后,就進入到系統(tǒng)架構(gòu)設(shè)計階段碘梢。
在系統(tǒng)架構(gòu)設(shè)計階段,我一般會首先做數(shù)據(jù)建模伐蒂。根據(jù)業(yè)務模型和功能列表煞躬,已經(jīng)可以分清楚大概的系統(tǒng)、模塊和功能逸邦,由此數(shù)據(jù)庫的概念模型基本能夠確定下來恩沛。通過數(shù)據(jù)庫的概念模型設(shè)計,結(jié)合需求分析階段產(chǎn)出的功能需求列表缕减,整個系統(tǒng)的詳細需求基本可以被印在大腦中了雷客。同時經(jīng)過概念模型的設(shè)計,不同數(shù)據(jù)實體之間的關(guān)系已經(jīng)相對清晰桥狡,服務或領(lǐng)域的劃分也具備初步的雛形了搅裙。
完成數(shù)據(jù)庫的概念模型后,開始進行詳細的系統(tǒng)架構(gòu)設(shè)計和技術(shù)選型階段裹芝。
在系統(tǒng)架構(gòu)設(shè)計階段部逮,我會按照分層架構(gòu)設(shè)計的思想,逐步做細化展開嫂易。
在目前的主流技術(shù)方案中兄朋,前后端分離基本上是默認的標準,核心原因一方面是前后端技術(shù)演進快速發(fā)展怜械,技術(shù)專業(yè)人員分工更明確颅和;另一方面當前的產(chǎn)品開始移動化傅事,前后端一體的架構(gòu)無法支撐當前移動端特別是App類應用的開發(fā),前后端分離也成為不得不為之的舉措峡扩。
按照分層架構(gòu)設(shè)計的思想蹭越,將整個架構(gòu)分為前端接入層、服務層有额、數(shù)據(jù)訪問層般又、數(shù)據(jù)存儲層。在部分高并發(fā)的系統(tǒng)中巍佑,還會有緩存相關(guān)技術(shù)貫穿在整個架構(gòu)層級之間茴迁。
前端接入層
首先,在前端接入層萤衰,主要解決用戶流量從終端發(fā)起請求到被應用服務器接收之前這一段的問題堕义。
在前端接入層,核心需要解決終端展現(xiàn)時的響應速度和穩(wěn)定性脆栋。
在傳統(tǒng)的PC互聯(lián)網(wǎng)場景下倦卖,大部分產(chǎn)品和應用以瀏覽器為終端,通過網(wǎng)頁的方式展現(xiàn)在用戶面前椿争,如京東怕膛、淘寶等電商類的網(wǎng)頁版。在終端層面秦踪,因為用戶請求量巨大褐捻,會借助頁面靜態(tài)化、CDN等方式椅邓,提升頁面加載速度柠逞。在請求層面,為了提升穩(wěn)定性和應對更大的流量挑戰(zhàn)景馁,在流量進入到應用服務器前板壮,會通過使用DNS、軟硬件負載的方式進行流量分流合住,將請求打到不同的應用服務器上绰精。這里會使用到的硬件負載有F5等,而軟件負載主要通過nginx實現(xiàn)透葛,早期還有LVS茬底、Haproxy的方案等。
服務層
說完接入層获洲,再來看服務層阱表。服務是應用系統(tǒng)的核心,承擔著業(yè)務和產(chǎn)品核心需求和邏輯的實現(xiàn)。
在服務層最爬,需要解決的重點問題問題是涉馁,服務是否可以靈活快速的水平擴展,以應對未來系統(tǒng)可能的訪問量劇增爱致。
為解決這個問題烤送,需要對是否進行分布式架構(gòu)設(shè)計進行權(quán)衡。分布式架構(gòu)設(shè)計的核心是通過對服務的解耦糠悯,來解決應用的水平擴展問題帮坚,降低單服務單機器的壓力。如果系統(tǒng)所需要承擔的流量在可預期的未來會有較大的增長互艾,分布式架構(gòu)設(shè)計就是有必要的试和。而如果系統(tǒng)流量在很長一段時間內(nèi)都相對有限且平穩(wěn),則分布式架構(gòu)就顯得沒那么必要纫普。這里即需要做好平衡和取舍阅悍。分布式架構(gòu)雖然能夠帶來服務水平擴展的便捷性,以應對未來可能的系統(tǒng)流量大幅增長昨稼,但需要付出較大的系統(tǒng)維護成本节视。
如果選擇暫時不采用分布式架構(gòu),服務層也同樣可以在工程層面假栓,按照服務的接口層寻行、服務的邏輯層和服務的數(shù)據(jù)層進行劃分模塊和子模塊,然后在代碼構(gòu)建打包時集成到一個單體應用作為一個jar或war包一起發(fā)布匾荆。根據(jù)我之前的經(jīng)驗寡痰,在一個業(yè)務和產(chǎn)品的早期階段,采用模塊化劃分的單體應用方式的架構(gòu)棋凳,可以快速完成產(chǎn)品MVP版本的上線試錯,當業(yè)務發(fā)展到一定規(guī)模后再啟動分布式架構(gòu)的服務化改造连躏,也不失為一個較好的選擇剩岳。
無論在一開始就選定分布式架構(gòu)方案,還是在后期做分布式架構(gòu)的服務化改造時入热,都會面臨著另外一個選型問題:到底是采用分布式服務還是微服務方式來構(gòu)建應用拍棕。
分布式服務和微服務是在分布式架構(gòu)演進過程中產(chǎn)生的不同概念,按照我的理解勺良,分布式服務關(guān)注的重點是分布式绰播,重點解決服務的壓力負載分擔,而微服務重點關(guān)注的是服務的單元顆粒度大小尚困,更關(guān)注服務的原子性蠢箩、獨立性。二者都是為了解耦合,但在解耦合的顆粒度上稍有不同谬泌。具體的選擇也需要根據(jù)應用系統(tǒng)的規(guī)模滔韵、領(lǐng)域劃分等綜合考量。
落地到分布式服務的架構(gòu)技術(shù)選型上掌实,有以傳統(tǒng)的RPC框架為主導的技術(shù)體系陪蜻,和以Spring Boot微服務框架為基礎(chǔ)的Spring Cloud全家桶。傳統(tǒng)的RPC框架以早期阿里開源的Dubbo框架為代表贱鼻,核心的實現(xiàn)是基于動態(tài)代理+反射的方式來實現(xiàn)服務之間的接口通信宴卖,服務和服務之間的底層調(diào)用是基于socket來實現(xiàn)數(shù)據(jù)傳輸交互的。而Spring Cloud全家桶邻悬,核心是基于Http協(xié)議實現(xiàn)的一套微服務框架症昏,服務和服務之間的調(diào)用是通過Http接口實現(xiàn)交互的的。
相比Spring Cloud拘悦,RPC框架一般具有可自定義數(shù)據(jù)結(jié)構(gòu)齿兔、網(wǎng)絡傳輸速度快、效率高等特點础米,而Spring?Cloud因為是基于Http協(xié)議進行網(wǎng)絡傳輸分苇,消息的包體大小受Http協(xié)議限制做了封裝顯得相對臃腫,在網(wǎng)絡傳輸時效率相對低一些屁桑,但Spring Cloud因有一整套組件和生態(tài)的支撐医寿,因此在不做過多性能苛求的情況下,也是目前可以快速采用的方案之一蘑斧。另外一種將二者結(jié)合的方案靖秩,利用Spring Cloud設(shè)計和實現(xiàn)接口的接入網(wǎng)關(guān),再通過網(wǎng)關(guān)將請求轉(zhuǎn)發(fā)到RPC服務中竖瘾,則是目前一些大型應用普遍采用的方案沟突。
在使用Spring Cloud或RPC框架的過程中,另外一個需要關(guān)注的問題是服務之間的耦合問題捕传。雖然分布式服務或微服務本身就是為了解決耦合問題惠拭,但應用和應用、服務與服務之間的依賴關(guān)系并不因為使用Spring Cloud或RPC框架就消失了庸论,在一些場景下职辅,適度的服務之間的依賴是允許且必要的,但過度的服務之間依賴聂示,甚至發(fā)展成服務與服務之間相互依賴域携,就是需要避免的一種情況了。
通過消息中間件鱼喉,將原本有上下游關(guān)系的依賴秀鞭,通過消息解耦趋观,從而讓服務和服務之間避免強依賴,是其中的一個解決方案气筋。
常用的消息中間件有RabbitMQ拆内、RocketMQ、Kafka等宠默。
數(shù)據(jù)訪問層
說完服務層麸恍,再來看數(shù)據(jù)訪問層。所有的應用都離不開數(shù)據(jù)搀矫,數(shù)據(jù)是一個系統(tǒng)的靈魂所在抹沪。數(shù)據(jù)訪問層主要用于完成服務層和數(shù)據(jù)存儲層之間數(shù)據(jù)的交互。
因為數(shù)據(jù)存儲方式的多樣性瓤球,數(shù)據(jù)訪問層一個重要功能是實現(xiàn)對不同數(shù)據(jù)存儲方式的封裝融欧,盡量做到對服務層屏蔽具體的數(shù)據(jù)存儲細節(jié)。
另一個數(shù)據(jù)訪問層的重要作用卦羡,是解決數(shù)據(jù)存儲場景下的資源管理問題噪馏,包括連接池資源、分庫分表绿饵、讀寫分離等欠肾。具體是否需要做分庫分表、讀寫分離等拟赊,需要根據(jù)應用的實際數(shù)據(jù)量大小刺桃、選擇的數(shù)據(jù)存儲方式等做綜合考量。
數(shù)據(jù)存儲層
最后吸祟,再來考慮數(shù)據(jù)存儲層的搭建和選型瑟慈。
數(shù)據(jù)存儲層是用于將具體應用產(chǎn)生的數(shù)據(jù)持久化。不同的數(shù)據(jù)存儲方式適用的業(yè)務場景也有很大不同屋匕。如對事務要比較高葛碧,一般采用關(guān)系型數(shù)據(jù)庫如Mysql、Oracle等过吻;對數(shù)據(jù)結(jié)構(gòu)和表結(jié)構(gòu)擴展性要求較靈活的可以采用NoSQL數(shù)據(jù)庫如MongoDB进泼、CouchDB等;如果需要存儲的數(shù)據(jù)量非常龐大疮装,可以選擇目前基于hdfs的存儲方案如Hbase、Hive等粘都;還會有對文件廓推、圖片等有存儲需求的,可以采用分布式文件存儲或云存儲的方式等翩隧。數(shù)據(jù)存儲的方案本身沒有好壞之分樊展,具體還是要分析業(yè)務的實際需求來做平衡和選擇。
關(guān)于緩存
除了以上所述的幾個層級,在大部分互聯(lián)網(wǎng)應用中专缠,當系統(tǒng)訪問用戶量達到一定量級雷酪,或QPS較高的場景下,還會通過增加緩存來降低系統(tǒng)和接口的響應時間涝婉,提升系統(tǒng)的性能哥力。常用的緩存框架有Redis、Memcache等墩弯。
在架構(gòu)設(shè)計階段吩跋,我們把整個系統(tǒng)按照分層的思想做拆解說明,根據(jù)業(yè)務的實際需求渔工,對各層中采用的技術(shù)和方案做平衡和取舍锌钮。架構(gòu)設(shè)計階段完成并不意味著整個應用系統(tǒng)架構(gòu)的完成。接下來需要將架構(gòu)設(shè)計落地到代碼中引矩。
05?編碼階段
在架構(gòu)設(shè)計方案完成后梁丘,對于開發(fā)工程師來說,已經(jīng)可以根據(jù)架構(gòu)設(shè)計方案來指導進行詳細需求設(shè)計和編碼旺韭。
對于架構(gòu)師來說氛谜,在這個階段需要重點關(guān)注的有以下幾點:
工程結(jié)構(gòu):良好的工程結(jié)構(gòu),可以大幅降低后續(xù)代碼的維護成本和大團隊的協(xié)作成本茂翔。每一層的工程結(jié)構(gòu)也會有所側(cè)重和不同混蔼,我們可以在平時沉淀適合自己企業(yè)的工程結(jié)構(gòu)腳手架,在需要時一鍵創(chuàng)建珊燎,既能提升效率惭嚣,還可以統(tǒng)一工程模板。
設(shè)計模式:針對復雜的需求悔政,架構(gòu)師需要重點關(guān)注晚吞,將不同設(shè)計模式組合、擴展應用到代碼設(shè)計中谋国,提升代碼的可擴展性和可維護性槽地。
代碼模板:代碼模板可以統(tǒng)一代碼風格,提升代碼的可讀性和可維護性芦瘾。
代碼檢查:通過對代碼的檢查捌蚊,發(fā)現(xiàn)不合理的代碼組織和設(shè)計、潛在的代碼隱患等等近弟。也可以借助一些第三方插件或工具如PMD缅糟、Findbugs等輔助做檢查。
06?上線運營階段
一個好的架構(gòu)祷愉,一定是需要經(jīng)歷線上系統(tǒng)的檢驗的窗宦。在測試人員完成質(zhì)量測試后赦颇,需要對應用系統(tǒng)做上線發(fā)布,進入到上線運營階段赴涵。
在上線運營階段媒怯,架構(gòu)師核心職責是做好各項監(jiān)控和告警指標的設(shè)定。
在監(jiān)控層面髓窜,首選需要對應用系統(tǒng)相關(guān)的各項性能指標做監(jiān)控扇苞。通過一些開源或企業(yè)自研的監(jiān)控工具,需要對應用系統(tǒng)所采用的物理資源如磁盤纱烘、內(nèi)存杨拐、CPU、網(wǎng)絡等進行監(jiān)控擂啥;需要對數(shù)據(jù)存儲組件如數(shù)據(jù)庫的慢查詢哄陶、I/O、庫大小等進行監(jiān)控哺壶;對中間件如RabbitMQ屋吨、Redis等讀寫、容量等進行監(jiān)控山宾;對應用系統(tǒng)性能如接口響應時長至扰、95線、99線的監(jiān)控等等资锰。
另外一個重要的監(jiān)控對象是業(yè)務指標的監(jiān)控敢课,如一段時間內(nèi)的登陸用戶量、短信發(fā)送量绷杜、訂單下單量直秆、訂單支付量等等。業(yè)務指標的監(jiān)控往往能反映出一些系統(tǒng)層面的異常鞭盟,引導去追究引起業(yè)務指標異常的根本原因圾结,從而提升系統(tǒng)的可靠性和穩(wěn)定性。
具體建立哪些監(jiān)控指標需要根據(jù)不同的監(jiān)控對象進行合理的設(shè)定齿诉。
建立完監(jiān)控指標后筝野,還需要建立告警體系,將異常的指標及時告警通知出來粤剧。一般的監(jiān)控系統(tǒng)都會有提供類似短信歇竟、釘釘?shù)雀婢绞降慕尤搿?/p>
最后,在上線運營階段還需要收集和關(guān)注系統(tǒng)的日志抵恋,及時排查修復異常焕议,保障系統(tǒng)的穩(wěn)健運行。
以上是從一個業(yè)務的需求階段開始馋记,到整個業(yè)務的應用系統(tǒng)上線運營号坡,作為一個架構(gòu)師需要關(guān)注的方方面面。
當然梯醒,其中的每一個架構(gòu)點和技術(shù)方案宽堆,展開來看都是一個很大的課題。當你具備架構(gòu)的全局思維之后茸习,接下來就可以深入到不同領(lǐng)域?qū)Q行罅ィ⒃诠ぷ髦胁粩鄬嵺`和試錯,最終完成自身技能的進階号胚。
自計算機誕生以來籽慢,各項軟硬件技術(shù)即在不斷迭代更新,催生著軟件架構(gòu)的不斷升級換代猫胁。而隨著大數(shù)據(jù)云計算5G時代的帶領(lǐng)箱亿,新的業(yè)務場景如音視頻、直播弃秆、物聯(lián)網(wǎng)等等業(yè)務挑戰(zhàn)也會隨之而來届惋,我們對架構(gòu)的追求也永無止境。
希望你我都可以在持續(xù)的架構(gòu)演進中菠赚,不斷學習脑豹,持續(xù)進步。
相關(guān)閱讀