關(guān)于架構(gòu)優(yōu)化和設(shè)計,架構(gòu)師必須知道的事情

英文原文來源: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)的幾個要點,同時你也可以把它當作系統(tǒng)設(shè)計的準則或者用它來驗證現(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ù)需要。可能當時是最好的架構(gòu),但是后來我們還是要跟著業(yè)務(wù)的變化去做改進。這并不是一件壞事情,我們只要做好應(yīng)對變化的準備即可。

與代碼無關(guān)

架構(gòu)師這個詞的意義非常廣泛,有些架構(gòu)師是指在公司負責編寫軟件的某些模塊的人。當然多數(shù)公司并沒有這樣的職位,他們會有一些技術(shù)leader來負責具體的功能。我們這里所要講的架構(gòu)師不會太過于關(guān)注代碼的細節(jié),而是更關(guān)注系統(tǒng)各個模塊之間如何協(xié)同、交互等一些更全局的一些事情。他們主要關(guān)注一些可能經(jīng)常會被人遺忘但是又會為系統(tǒng)帶來惡劣影響的部分,職責是確保所有的功能都能夠以較好的質(zhì)量及時被交付。這種人對于軟件產(chǎn)品的成功有著舉足輕重的地位,當然他們往往在一個公司里面可能同時負責好幾個項目。

想象一下,兩個不同的架構(gòu)師來建造一艘太空飛船。第一個選擇用紙來糊一個樣子比較好看的,然后把這艘飛船放到一個漂亮、大小合適的玻璃櫥窗里面保護起來。飛般看起來可能像下面的這個樣子:

第二個架構(gòu)師決定用樂高模型來拼一個太空飛船,它們可以隨意組合并且比較堅固,所以并不需要額外的特殊保護。

兩艘飛船看起來都是挺不錯的,但是第一個用了較長的時間來完成并且后來當他們需要對這艘飛船做改進的時候,問題就暴露出來了。

第一位架構(gòu)師幾乎炸了,因為每一次的改動時候,他們必須要移除那個保護罩,并且需要重新再造一艘完整的飛船。雖然他們已經(jīng)有了所有的模型,再加上造飛船對他來說已經(jīng)很熟悉,但是他們還是花了很長的時間去完成每一次改造,另外還需要再做一個新的保護罩才能裝的下那艘新的飛船。

但是對于第二位架構(gòu)師來說,這一切都是不需要的。他只需要對產(chǎn)生影響的一些組件進行改造,制作新的組件,當一切準備就緒再添加到原來的飛船即可。

再后來,第二位架構(gòu)師希望能更進一步的優(yōu)化他們的制作過程,因為他們現(xiàn)在投入了很多的時間在上面。在經(jīng)過一段時間的研究之后,他們決定嘗試用一種新的材料和方法來制作這艘飛船。也就是3D打印,他們申請了一臺3D打印機,制作了所有的模型,這樣他們就可以將一些常規(guī)的任何通過3D打印機自動完成了。

當然,這只是一個很簡單的例子。但是我們能從中學(xué)到什么呢?雖然兩位架構(gòu)師在最開始的時候都能成功的完成最初始的功能,但是他們都面臨著變化所帶來的系統(tǒng)的調(diào)整。在集成階段,復(fù)雜性開始顯現(xiàn)出來,和最開始的目標無關(guān),最終整個設(shè)計是否足夠靈活、可調(diào)整、以及模塊化起著至關(guān)重要的作用。

軟件的架構(gòu)至關(guān)重要,僅僅有較好的代碼來完成功能不足以成為一個優(yōu)秀的解決方案。因為它不僅僅涉及到代碼,還有我們所寫的各個模塊之間如果交互和集成、數(shù)據(jù)如何存儲、我們怎么樣來進行開發(fā)和測試、以及在引進變動的時候的難易程度等等。

這些事情都是和編寫代碼無關(guān),但是需要我們來花時間考慮, 并且是整個系統(tǒng)最后是否成功的決定性因素。

要去考慮的細節(jié)

還有一些原則比如:模塊化、輕耦合、無共享架構(gòu);減少各個組件之前的依懶、注意服務(wù)之間依懶所有造成的鏈式失敗及影響等。

DDD給我們提供了在不同的特定領(lǐng)域上下文以及業(yè)務(wù)功能的基礎(chǔ)上拆分組件的指導(dǎo)方法。

把服務(wù)獨立出來提供特定的功能,同時方便在應(yīng)對變化的時候能夠不影響其它的服務(wù)。

在大多數(shù)情況下,如果需要同步更新很多個服務(wù)則說明系統(tǒ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ā)、測試和部署流程,那就和自殺無異。我們需要花費一定時間來計劃和準備開發(fā)、測試、生產(chǎn)環(huán)境,可能還需要一些額外的環(huán)境以備不時之需。

測試流程和策略也是非常重要的。一些最佳實踐使用Blue-Green開發(fā)、金絲雀部署、A/B測試等。盡量保持測試環(huán)境與生產(chǎn)環(huán)境是一致的,至少硬件結(jié)構(gòu)上來說應(yīng)該是一樣的。一定要做壓力和負載測試,并且盡可能快的在生產(chǎn)上進行這樣的測試,這樣能夠更快速、精確的幫我們找到線上的問題。
可調(diào)整的架構(gòu)同時也意味著服務(wù)要可以被靈活獨立的部署以及簡單的基礎(chǔ)運維操作。

利用不可變基礎(chǔ)設(shè)施的優(yōu)勢。
不可變基礎(chǔ)架構(gòu),就是說系統(tǒ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ò)通迅層、代理、負載均衡等等。很多生產(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)中,如果某個目標服務(wù)調(diào)用慢或者有大量超時,此時,熔斷該服務(wù)的調(diào)用,對于后續(xù)調(diào)用請求,不在繼續(xù)調(diào)用目標服務(wù),直接返回,快速釋放資源。如果目標服務(wù)情況好轉(zhuǎn)則恢復(fù)調(diào)用。

艙壁模式(bulkheads)該模式像艙壁一樣對資源或失敗單元進行隔離,如果一個船艙破了進水,只損失一個船艙。比如采用微服務(wù)是首選,比如docker。Docker是進程隔離的,單個 docker失效不會影響其他docker容器?;蛘甙汛蟮牟⑿刑幚砉ぷ鳎啥鄠€線程池來負荷分擔。(譯者注)
當然,如果當它開始工作的時候,說明我們的系統(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)時間,當它有異常的時候需要尋找原因以及采取相應(yīng)的行動。

搭建日志、監(jiān)控、以及系統(tǒng)操作的自動化操作平臺。由于微服務(wù)相對來說較獨立,可以更方便檢測失敗 所以監(jiān)控起來會更容易一些。一些比較不錯的方式是在收集和分析日志的時候使用關(guān)聯(lián)ID、通用日志數(shù)據(jù)格式等。注意日志數(shù)據(jù)可能會非常龐大,所以要考慮日志的時間周期,定義對日志進行歸檔。另外還有一些不錯的工具可以將數(shù)據(jù)可視化在頁面中,可以更直觀的看到一些重要的進程。

為了保證服務(wù)的更新不會影響客戶端的使用,對于服務(wù)的版本控制也是非常重要的。有些情況下同時運行不同版本的服務(wù)也是很常見的情形,我們要做好長期向后兼容的準備。

務(wù)必要記住的事情

大多數(shù)情況下我們并不是從零開始去構(gòu)建,而是在現(xiàn)有的系統(tǒng)上繼續(xù)添磚加瓦,而原有的系統(tǒng)在開發(fā)、運維、以及架構(gòu)靈活性上都存在一些問題。想必很多優(yōu)秀的開發(fā)人員在經(jīng)歷這樣的情況的時候,都會想去拆解、重構(gòu)整個系統(tǒng),但是我們需要謹慎地來完成這個事情。當系統(tǒng)以錯誤的方式成拆分成組件或者服務(wù)單元之后也是一件很危險的事情 。

大多數(shù)系統(tǒng)在一開始的時候都是一個單體應(yīng)用,后來不斷地被拆解成為微服務(wù)。下面有一些基本的理念可以在我們做拆解地時候當作參考:

在開始拆分前了解具體的業(yè)務(wù)需求和業(yè)務(wù)領(lǐng)域
注意一些和其它業(yè)務(wù)共享的功能和數(shù)據(jù),它們需要被正確地模塊化
這種遷移和升級適合一步一步、一點一點地來完成,僅僅做當前最合適的事情
在開始之前很好地理解業(yè)務(wù)領(lǐng)域的范圍及邊界,因為對邊界的調(diào)整將是非常昂貴的
對于改造有清晰的結(jié)構(gòu)此次會涉及到哪些團隊的調(diào)整

人、團隊、和組織的影響

這個話題太大,大到我們需要專門寫一篇文章來細述。在這里簡單地概括一下,在本文中我們所提到的架構(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

路由和負載均衡
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
等。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,533評論 19 139
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,872評論 25 709
  • 最近再看阮一峰的一篇博客提到了一本書《Software Architecture Patterns》(PDF),寫...
    卓_然閱讀 8,230評論 0 22
  • 感恩昨天下午在會場高頻連接的正能量,有好的狀態(tài)保持平和與歡喜心。感恩孩子越來越懂事,昨晚對我說還是在媽媽的懷抱里幸...
    棋心118閱讀 179評論 0 0
  • 零、前情提要 一、概念 java API所提供的一系列類的實例,用于存放對象。 數(shù)組也可以存儲一系列對象,但是不可...
    PanPan1127閱讀 166評論 0 0

友情鏈接更多精彩內(nèi)容