英文原文來源:http://lenadroid.github.io/posts/adjustable-flexible-architecture.html
本文來源:http://www.infoq.com/cn/articles/architecture-optimization-and-design-the-architect-must-know
版權(quán)歸原作者所有。本文有改動扎即。
更多干貨:ID:geekweekly
概述
你可以叫它SOA的新玩法、微服務(wù)、或者任意其它酷炫的名字坟比。近幾年來隨著互聯(lián)網(wǎng)的飛速發(fā)展,新的架構(gòu)實踐方式不斷涌現(xiàn)嚷往,但是有一件事情是永恒不變的葛账,那就是-“架構(gòu)之道”;關(guān)于如何設(shè)計出靈活皮仁、高可用性以及能夠快速適應(yīng)變化的系統(tǒng)架構(gòu)籍琳,我們依舊還有很大的發(fā)揮空間。本文會介紹關(guān)于如何構(gòu)建前沿的贷祈、易維護的趋急、安全的架構(gòu)的幾個要點,同時你也可以把它當(dāng)作系統(tǒng)設(shè)計的準(zhǔn)則或者用它來驗證現(xiàn)有的架構(gòu)是否合理势誊。
就像我們經(jīng)常所說的:沒有最好的架構(gòu)呜达,只有最合適的架構(gòu)。一個好的架構(gòu)師粟耻,可以根據(jù)具體的需求查近、所擁有的資源等因素綜合考慮而設(shè)計出最優(yōu)的架構(gòu)方案。特別是現(xiàn)在挤忙,業(yè)務(wù)的飛速變化霜威、數(shù)據(jù)無處不在等這些因素的影響下,技術(shù)和框架也需要在變化的過程中不斷地打磨和提升以適應(yīng)新的業(yè)務(wù)需要饭玲〗募溃可能當(dāng)時是最好的架構(gòu),但是后來我們還是要跟著業(yè)務(wù)的變化去做改進。這并不是一件壞事情矮冬,我們只要做好應(yīng)對變化的準(zhǔn)備即可谈宛。
與代碼無關(guān)
架構(gòu)師這個詞的意義非常廣泛,有些架構(gòu)師是指在公司負(fù)責(zé)編寫軟件的某些模塊的人胎署。當(dāng)然多數(shù)公司并沒有這樣的職位吆录,他們會有一些技術(shù)leader來負(fù)責(zé)具體的功能。我們這里所要講的架構(gòu)師不會太過于關(guān)注代碼的細(xì)節(jié)琼牧,而是更關(guān)注系統(tǒng)各個模塊之間如何協(xié)同恢筝、交互等一些更全局的一些事情。他們主要關(guān)注一些可能經(jīng)常會被人遺忘但是又會為系統(tǒng)帶來惡劣影響的部分巨坊,職責(zé)是確保所有的功能都能夠以較好的質(zhì)量及時被交付撬槽。這種人對于軟件產(chǎn)品的成功有著舉足輕重的地位,當(dāng)然他們往往在一個公司里面可能同時負(fù)責(zé)好幾個項目趾撵。
想象一下侄柔,兩個不同的架構(gòu)師來建造一艘太空飛船。第一個選擇用紙來糊一個樣子比較好看的占调,然后把這艘飛船放到一個漂亮暂题、大小合適的玻璃櫥窗里面保護起來。飛般看起來可能像下面的這個樣子:
第二個架構(gòu)師決定用樂高模型來拼一個太空飛船究珊,它們可以隨意組合并且比較堅固薪者,所以并不需要額外的特殊保護。
兩艘飛船看起來都是挺不錯的剿涮,但是第一個用了較長的時間來完成并且后來當(dāng)他們需要對這艘飛船做改進的時候言津,問題就暴露出來了。
第一位架構(gòu)師幾乎炸了幔虏,因為每一次的改動時候纺念,他們必須要移除那個保護罩,并且需要重新再造一艘完整的飛船想括。雖然他們已經(jīng)有了所有的模型陷谱,再加上造飛船對他來說已經(jīng)很熟悉,但是他們還是花了很長的時間去完成每一次改造瑟蜈,另外還需要再做一個新的保護罩才能裝的下那艘新的飛船烟逊。
但是對于第二位架構(gòu)師來說,這一切都是不需要的铺根。他只需要對產(chǎn)生影響的一些組件進行改造宪躯,制作新的組件,當(dāng)一切準(zhǔn)備就緒再添加到原來的飛船即可位迂。
再后來访雪,第二位架構(gòu)師希望能更進一步的優(yōu)化他們的制作過程详瑞,因為他們現(xiàn)在投入了很多的時間在上面。在經(jīng)過一段時間的研究之后臣缀,他們決定嘗試用一種新的材料和方法來制作這艘飛船坝橡。也就是3D打印,他們申請了一臺3D打印機精置,制作了所有的模型计寇,這樣他們就可以將一些常規(guī)的任何通過3D打印機自動完成了。
當(dāng)然脂倦,這只是一個很簡單的例子番宁。但是我們能從中學(xué)到什么呢?雖然兩位架構(gòu)師在最開始的時候都能成功的完成最初始的功能赖阻,但是他們都面臨著變化所帶來的系統(tǒng)的調(diào)整蝶押。在集成階段,復(fù)雜性開始顯現(xiàn)出來政供,和最開始的目標(biāo)無關(guān)播聪,最終整個設(shè)計是否足夠靈活、可調(diào)整布隔、以及模塊化起著至關(guān)重要的作用。
軟件的架構(gòu)至關(guān)重要稼虎,僅僅有較好的代碼來完成功能不足以成為一個優(yōu)秀的解決方案衅檀。因為它不僅僅涉及到代碼霎俩,還有我們所寫的各個模塊之間如果交互和集成哀军、數(shù)據(jù)如何存儲、我們怎么樣來進行開發(fā)和測試杉适、以及在引進變動的時候的難易程度等等猿推。
這些事情都是和編寫代碼無關(guān),但是需要我們來花時間考慮, 并且是整個系統(tǒng)最后是否成功的決定性因素坦喘。
要去考慮的細(xì)節(jié)
還有一些原則比如:模塊化盲再、輕耦合、無共享架構(gòu)瓣铣;減少各個組件之前的依懶答朋、注意服務(wù)之間依懶所有造成的鏈?zhǔn)绞〖坝绊懙取?/p>
DDD給我們提供了在不同的特定領(lǐng)域上下文以及業(yè)務(wù)功能的基礎(chǔ)上拆分組件的指導(dǎo)方法。
把服務(wù)獨立出來提供特定的功能坯沪,同時方便在應(yīng)對變化的時候能夠不影響其它的服務(wù)绿映。
在大多數(shù)情況下,如果需要同步更新很多個服務(wù)則說明系統(tǒng)的耦合還不夠低腐晾。當(dāng)然叉弦,再完美的原則也會有例外的時候。比如當(dāng)你想把系統(tǒng)部署在一些IoT設(shè)備上的時候藻糖,你可能要一次性部署所有的組件淹冰。這是允許的,但是巨柒,請盡量考慮服務(wù)之間的耦合及靈活性以應(yīng)對將系統(tǒng)部署在不同平臺上的需求樱拴。
即便如此,我們也不可能完全避免耦合洋满,它總是會出現(xiàn)在某些場景下晶乔。這就需要我們提取一些抽象層將服務(wù)之間的交互定在契約上來避免復(fù)雜,提升靈活性牺勾。
這就需要我們有一種辨別能力正罢,能夠找到那些必須放在一起來做處理而不能拆解的功能。如果這些功能是值得放在一起的驻民,那我們就可以將它獨立成一個微服務(wù)翻具,遵循高聚合的設(shè)計原因。
我們要記住的是回还,系統(tǒng)的設(shè)計要做到比較容易地增加或者修改原來的組件裆泳。無狀態(tài)的架構(gòu)是系統(tǒng)高擴展性的基石。
特別需要注意服務(wù)和組件之間如何交互柠硕,了解不同的協(xié)議的優(yōu)缺點工禾,包括速度以及可用性,來幫助我們來決定哪一種是最適合我們的仅叫。
基礎(chǔ)設(shè)施帜篇、配置、測試诫咱、開發(fā)笙隙、運維
為配置管理定義策略,因為同時發(fā)生的配置變化對系統(tǒng)所有造成的影響也是很重要的坎缭,所以需要由全局層面的的自動化更新方案來完成竟痰。
在如今签钩,對于一個數(shù)據(jù)敏感的大型解決方案來說如果沒有自動化的一些基礎(chǔ)設(shè)施和穩(wěn)固的開發(fā)、測試和部署流程坏快,那就和自殺無異铅檩。我們需要花費一定時間來計劃和準(zhǔn)備開發(fā)、測試莽鸿、生產(chǎn)環(huán)境昧旨,可能還需要一些額外的環(huán)境以備不時之需。
測試流程和策略也是非常重要的祥得。一些最佳實踐使用Blue-Green開發(fā)兔沃、金絲雀部署、A/B測試等级及。盡量保持測試環(huán)境與生產(chǎn)環(huán)境是一致的乒疏,至少硬件結(jié)構(gòu)上來說應(yīng)該是一樣的。一定要做壓力和負(fù)載測試饮焦,并且盡可能快的在生產(chǎn)上進行這樣的測試怕吴,這樣能夠更快速、精確的幫我們找到線上的問題县踢。
可調(diào)整的架構(gòu)同時也意味著服務(wù)要可以被靈活獨立的部署以及簡單的基礎(chǔ)運維操作转绷。
利用不可變基礎(chǔ)設(shè)施的優(yōu)勢。
不可變基礎(chǔ)架構(gòu)硼啤,就是說系統(tǒng)一旦部署暇咆,就不再更變升級。當(dāng)服務(wù)/應(yīng)用需要升級時丙曙,只要部署一個新版系統(tǒng),摧毀舊版就好了其骄。在這個過程中亏镰,系統(tǒng)對外服務(wù)是幾乎是持續(xù)的。(譯者注)
保證打包/持續(xù)集成進程是基于統(tǒng)一的途徑拯爽,并且不會對正在運行的服務(wù)進行任何更改(比如 禁用ssh)索抓,所有的更新都應(yīng)該通過定義好的自動化配置和打包操作將它們應(yīng)用到所有的對應(yīng)的系統(tǒng)上,來避免配置遺漏毯炮。比如手工某個環(huán)境上修改配置逼肯,可能會漏掉其它環(huán)境的配置。
開發(fā)團隊不應(yīng)該過度關(guān)注基礎(chǔ)設(shè)施桃煎,因為有一天可能基礎(chǔ)設(shè)施也會發(fā)生改變篮幢,所以與業(yè)務(wù)相關(guān)的開發(fā)不要和基礎(chǔ)設(shè)施有過重的綁定。
代碼之間的東西(in-between the code)
"in-between the code" 可以統(tǒng)一的概括為一些基礎(chǔ)設(shè)施所提供的功能为迈,比如:服務(wù)發(fā)現(xiàn)三椿、請求路由缺菌、網(wǎng)絡(luò)通迅層、代理搜锰、負(fù)載均衡等等伴郁。很多生產(chǎn)上的錯誤并不是因為代碼的業(yè)務(wù)邏輯錯誤或者每個獨立組件本身的問題,而是由于這些把各個組件協(xié)調(diào)起來的一些通用基礎(chǔ)設(shè)施蛋叼。
隨著系統(tǒng)的變化越來越快焊傅,更要注意我們所更改的一些組件,考慮可用性和擴展性狈涮。制定最小風(fēng)險的計劃來應(yīng)對出現(xiàn)的問題狐胎。
無處不在的坑
做一個偏執(zhí)狂。為失敗而設(shè)計架構(gòu) - 列舉所有可能失敗的地方薯嗤。和團隊頭腦風(fēng)暴對所有可能失敗的地方進行質(zhì)疑然后提出保護方案顽爹。
如果連接建立失敗怎么辦?
如果花費的時間超出預(yù)計怎么辦骆姐?
如果請求返回不清楚的數(shù)據(jù)或者不正確的答案怎么辦镜粤?
如果請求返回的數(shù)據(jù)不是約定好的怎么辦?
如果出現(xiàn)很高的并發(fā)能應(yīng)對嗎玻褪?
如果服務(wù)掛肉渴、機組、整個數(shù)據(jù)中心掛掉了怎么辦带射?
如果數(shù)據(jù)庫損壞了怎么辦同规?
如果部署的時候失敗了怎么辦?
如果在部署成功之后生產(chǎn)環(huán)境上某些功能失敗了怎么辦窟社?
集成性這方面的錯誤可以有千萬種可能券勺,那么我們應(yīng)該如何來避免?
使用一些技術(shù)比如:熔斷(circuit breaker)灿里、超時設(shè)定(timeouts)关炼、握手信號(handshaking)、艙壁(bulkheads)來幫助我們保護這些系統(tǒng)集成之前的問題匣吊。
熔斷模式(circuit breaker)可以參考電路熔斷儒拂,如果一條線路電壓過高,保險絲會熔斷色鸳,防止火災(zāi)社痛。放到我們的系統(tǒng)中,如果某個目標(biāo)服務(wù)調(diào)用慢或者有大量超時命雀,此時蒜哀,熔斷該服務(wù)的調(diào)用,對于后續(xù)調(diào)用請求咏雌,不在繼續(xù)調(diào)用目標(biāo)服務(wù)凡怎,直接返回校焦,快速釋放資源。如果目標(biāo)服務(wù)情況好轉(zhuǎn)則恢復(fù)調(diào)用统倒。
艙壁模式(bulkheads)該模式像艙壁一樣對資源或失敗單元進行隔離寨典,如果一個船艙破了進水,只損失一個船艙房匆。比如采用微服務(wù)是首選耸成,比如docker。Docker是進程隔離的浴鸿,單個 docker失效不會影響其他docker容器井氢⌒沤危或者把大的并行處理工作淑仆,由多個線程池來負(fù)荷分擔(dān)。(譯者注)
當(dāng)然揉燃,如果當(dāng)它開始工作的時候掸哑,說明我們的系統(tǒng)出現(xiàn)了比較大的問題约急,需要我們?nèi)フ{(diào)查分析。
注意那些不能看到代碼的組件苗分、依懶項以及共享的資源厌蔽。除了有正確的開發(fā)和測試流程以外,還應(yīng)該盡量使用和真實生產(chǎn)環(huán)境一樣的數(shù)據(jù)摔癣、以及硬件網(wǎng)絡(luò)配置來進行測試奴饮。
跟蹤系統(tǒng)的響應(yīng)來防止一些比較常見的問題比如服務(wù)不可用的情況,留意系統(tǒng)的平均響應(yīng)時間择浊,當(dāng)它有異常的時候需要尋找原因以及采取相應(yīng)的行動戴卜。
搭建日志、監(jiān)控琢岩、以及系統(tǒng)操作的自動化操作平臺叉瘩。由于微服務(wù)相對來說較獨立,可以更方便檢測失敗 所以監(jiān)控起來會更容易一些粘捎。一些比較不錯的方式是在收集和分析日志的時候使用關(guān)聯(lián)ID、通用日志數(shù)據(jù)格式等危彩。注意日志數(shù)據(jù)可能會非常龐大攒磨,所以要考慮日志的時間周期,定義對日志進行歸檔娩缰。另外還有一些不錯的工具可以將數(shù)據(jù)可視化在頁面中,可以更直觀的看到一些重要的進程谒府。
為了保證服務(wù)的更新不會影響客戶端的使用拼坎,對于服務(wù)的版本控制也是非常重要的浮毯。有些情況下同時運行不同版本的服務(wù)也是很常見的情形,我們要做好長期向后兼容的準(zhǔn)備泰鸡。
務(wù)必要記住的事情
大多數(shù)情況下我們并不是從零開始去構(gòu)建债蓝,而是在現(xiàn)有的系統(tǒng)上繼續(xù)添磚加瓦,而原有的系統(tǒng)在開發(fā)盛龄、運維饰迹、以及架構(gòu)靈活性上都存在一些問題。想必很多優(yōu)秀的開發(fā)人員在經(jīng)歷這樣的情況的時候余舶,都會想去拆解啊鸭、重構(gòu)整個系統(tǒng),但是我們需要謹(jǐn)慎地來完成這個事情匿值。當(dāng)系統(tǒng)以錯誤的方式成拆分成組件或者服務(wù)單元之后也是一件很危險的事情 赠制。
大多數(shù)系統(tǒng)在一開始的時候都是一個單體應(yīng)用,后來不斷地被拆解成為微服務(wù)挟憔。下面有一些基本的理念可以在我們做拆解地時候當(dāng)作參考:
在開始拆分前了解具體的業(yè)務(wù)需求和業(yè)務(wù)領(lǐng)域
注意一些和其它業(yè)務(wù)共享的功能和數(shù)據(jù)钟些,它們需要被正確地模塊化
這種遷移和升級適合一步一步、一點一點地來完成曲楚,僅僅做當(dāng)前最合適的事情
在開始之前很好地理解業(yè)務(wù)領(lǐng)域的范圍及邊界厘唾,因為對邊界的調(diào)整將是非常昂貴的
對于改造有清晰的結(jié)構(gòu)此次會涉及到哪些團隊的調(diào)整
人、團隊龙誊、和組織的影響
這個話題太大抚垃,大到我們需要專門寫一篇文章來細(xì)述。在這里簡單地概括一下趟大,在本文中我們所提到的架構(gòu)的靈活性以及穩(wěn)健的開發(fā)鹤树、測試、運維等流程都會影響企業(yè)的組織結(jié)構(gòu)逊朽。合適的組織結(jié)構(gòu)能夠給團隊更大的靈活性并且更有機會持續(xù)地創(chuàng)新罕伯,在這種組織結(jié)構(gòu)下,團隊可以根據(jù)自己的節(jié)奏來工作叽讳。組織不應(yīng)該按技術(shù)來拆分團隊追他,比如前端團隊、移動端團隊岛蚤、后端團隊或者根據(jù)不同的技術(shù)語言拆分團隊等邑狸,而是應(yīng)該按照微服務(wù)來拆分團隊(也可以理解為按獨立的業(yè)務(wù)單元來拆分)。這樣在一個團隊里面就會包含各種不同的技術(shù)涤妒,可以用不同的語言來實現(xiàn)服務(wù)单雾,這也給團隊更多的自由和自主權(quán)。
如何實踐?
容器化和集群工具
Docker
Docker Swarm
Kubernetes
Mesos
Serf
Nomad
基礎(chǔ)設(shè)施自動化/部署
Jenkins
Terraform
Vagrant
Packer
Otto
Chef硅堆, Puppet屿储, Ansible
配置
Edda
Archaius
Decider
ZooKeeper
服務(wù)發(fā)現(xiàn)
Eureka
Prana
Finagle
ZooKeeper
Consul
路由和負(fù)載均衡
Denominator
Zuul
Netty
Ribbon
HAProxy
NGINX
監(jiān)控、跟蹤渐逃、日志
Hystrix
Consul health checks
Zipkin
Pytheus
SALP
Elasticsearch logstash
數(shù)據(jù)協(xié)議
Protocol Buffers
Thrift
JSON/XML/Other text
等够掠。