概述
這篇譯文最早發布在infoQ下面的一個微信公眾號:“聊聊架構”上,想著我在園子幾乎沉寂了接近兩年之久,于是借機復活。哈哈哈,這是一篇關于架構的譯文,會介紹比較多的一些工具、以及框架,給對架構感興趣的同學一個知識擴充。
近幾年來隨著互聯網的飛速發展,新的架構實踐方式不斷涌現,但是有一件事情是永恒不變的,那就是-“架構之道”;關于如何設計出靈活、高可用性以及能夠快速適應變化的系統架構,我們依舊還有很大的發揮空間。本文會介紹關于如何構建前沿的、易維護的、安全的架構的幾個要點,同時你也可以把它當作系統設計的準則或者用它來驗證現有的架構是否合理。
就像我們經常所說的:沒有最好的架構,只有最合適的架構。一個好的架構師,可以根據具體的需求、所擁有的資源等因素綜合考慮而設計出最優的架構方案。特別是現在,業務的飛速變化、數據無處不在等這些因素的影響下,技術和框架也需要在變化的過程中不斷地打磨和提升以適應新的業務需要。可能當時是最好的架構,但是后來我們還是要跟著業務的變化去做改進。這并不是一件壞事情,我們只要做好應對變化的準備即可。
與代碼無關
架構師這個詞的意義非常廣泛,有些架構師是指在公司負責編寫軟件的某些模塊的人。當然多數公司并沒有這樣的職位,他們會有一些技術leader來負責具體的功能。我們這里所要講的架構師不會太過于關注代碼的細節,而是更關注系統各個模塊之間如何協同、交互等一些更全局的一些事情。他們主要關注一些可能經常會被人遺忘但是又會為系統帶來惡劣影響的部分,職責是確保所有的功能都能夠以較好的質量及時被交付。這種人對于軟件產品的成功有著舉足輕重的地位,當然他們往往在一個公司里面可能同時負責好幾個項目。
想象一下,兩個不同的架構師來建造一艘太空飛船。第一個選擇用紙來糊一個樣子比較好看的,然后把這艘飛船放到一個漂亮、大小合適的玻璃櫥窗里面保護起來。飛般看起來可能像下面的這個樣子:
第二個架構師決定用樂高模型來拼一個太空飛船,它們可以隨意組合并且比較堅固,所以并不需要額外的特殊保護。
兩艘飛船看起來都是挺不錯的,但是第一個用了較長的時間來完成并且后來當他們需要對這艘飛船做改進的時候,問題就暴露出來了。
第一位架構師幾乎炸了,因為每一次的改動時候,他們必須要移除那個保護罩,并且需要重新再造一艘完整的飛船。雖然他們已經有了所有的模型,再加上造飛船對他來說已經很熟悉,但是他們還是花了很長的時間去完成每一次改造,另外還需要再做一個新的保護罩才能裝的下那艘新的飛船。
但是對于第二位架構師來說,這一切都是不需要的。他只需要對產生影響的一些組件進行改造,制作新的組件,當一切準備就緒再添加到原來的飛船即可。
再后來,第二位架構師希望能更進一步的優化他們的制作過程,因為他們現在投入了很多的時間在上面。在經過一段時間的研究之后,他們決定嘗試用一種新的材料和方法來制作這艘飛船。也就是3D打印,他們申請了一臺3D打印機,制作了所有的模型,這樣他們就可將一些常規的任何通過3D打印機自動完成了。
當然,這只是一個很簡單的例子。但是我們能從中學到什么呢?雖然兩位架構師在最開始的時候都能成功的完成最初始的功能,但是他們都面臨著變化所帶來的系統的調整。在集成階段,復雜性開始顯現出來,和最開始的目標無關,最終整個設計是否足夠靈活、可調整、以及模塊化起著至關重要的作用。
軟件的架構至關重要,僅僅有較好的代碼來完成功能不足以成為一個優秀的解決方案。因為它不僅僅涉及到代碼,還有我們所寫的各個模塊之間如果交互和集成、數據如何存儲、我們怎么樣來進行開發和測試、以及在引進變動的時候的難易程度等等。
這些事情都是和編寫代碼無關,但是需要我們來花時間考慮, 并且是整個系統最后是否成功的決定性因素。
需要考慮的細節
還有一些原則比如:模塊化、輕耦合、無共享架構;減少各個組件之前的依懶、注意服務之間依懶所有造成的鏈式失敗及影響等。
DDD給我們提供了在不同的特定領域上下文以及業務功能的基礎上拆分組件的指導方法。
把服務獨立出來提供特定的功能,同時方便在應對變化的時候能夠不影響其它的服務。
在大多數情況下,如果需要同步更新很多個服務則說明系統的耦合還不夠低。當然,再完美的原則也會有例外的時候。比如當你想把系統部署在一些IoT設備上的時候,你可能要一次性部署所有的組件。這是允許的,但是,請盡量考慮服務之間的耦合及靈活性以應對將系統部署在不同平臺上的需求。
即便如此,我們也不可能完全避免耦合,它總是會出現在某些場景下。這就需要我們提取一些抽象層將服務之間的交互定在契約上來避免復雜,提升靈活性。這就需要我們有一種辨別能力,能夠找到那些必須放在一起來做處理而不能拆解的功能。如果這些功能是值得放在一起的,那我們就可以將它獨立成一個微服務,遵循高聚合的設計原因。
我們要記住的是,系統的設計要做到比較容易地增加或者修改原來的組件。無狀態的架構是系統高擴展性的基石。
特別需要注意服務和組件之間如何交互,了解不同的協議的優缺點,包括速度以及可用性,來幫助我們來決定哪一種是最適合我們的。
基礎設施、配置、測試、開發、運維
為配置管理定義策略,因為同時發生的配置變化對系統所有造成的影響也是很重要的,所以需要由全局層面的的自動化更新方案來完成。
在如今,對于一個數據敏感的大型解決方案來說如果沒有自動化的一些基礎設施和穩固的開發、測試和部署流程,那就和自殺無異。我們需要花費一定時間來計劃和準備開發、測試、生產環境,可能還需要一些額外的環境以備不時之需。
測試流程和策略也是非常重要的。一些最佳實踐使用Blue-Green開發、金絲雀部署、A/B測試等。盡量保持測試環境與生產環境是一致的,至少硬件結構上來說應該是一樣的。一定要做壓力和負載測試,并且盡可能快的在生產上進行這樣的測試,這樣能夠更快速、精確的幫我們找到線上的問題。
可調整的架構同時也意味著服務要可以被靈活獨立的部署以及簡單的基礎運維操作。
利用不可變基礎設施的優勢
不可變基礎架構,就是說系統一旦部署,就不再更變升級。當服務/應用需要升級時,只要部署一個新版系統,摧毀舊版就好了。在這個過程中,系統對外服務是幾乎是持續的。(譯者注)
保證打包/持續集成進程是基于統一的途徑,并且不會對正在運行的服務進行任何更改(比如 禁用ssh),所有的更新都應該通過定義好的自動化配置和打包操作將它們應用到所有的對應的系統上,來避免配置遺漏。比如手工某個環境上修改配置,可能會漏掉其它環境的配置。
開發團隊不應該過度關注基礎設施,因為有一天可能基礎設施也會發生改變,所以與業務相關的開發不要和基礎設施有過重的綁定。
代碼之間的東西(in-between the code)
"in-between the code" 可以統一的概括為一些基礎設施所提供的功能,比如:服務發現、請求路由、網絡通迅層、代理、負載均衡等等。很多生產上的錯誤并不是因為代碼的業務邏輯錯誤或者每個獨立組件本身的問題,而是由于這些把各個組件協調起來的一些通用基礎設施。
隨著系統的變化越來越快,更要注意我們所更改的一些組件,考慮可用性和擴展性。制定最小風險的計劃來應對出現的問題。
無處不在的坑
做一個偏執狂。為失敗而設計架構 - 列舉所有可能失敗的地方。和團隊頭腦風暴對所有可能失敗的地方進行質疑然后提出保護方案。
- 如果連接建立失敗怎么辦?
- 如果花費的時間超出預計怎么辦?
- 如果請求返回不清楚的數據或者不正確的答案怎么辦?
- 如果請求返回的數據不是約定好的怎么辦?
- 如果出現很高的并發能應對嗎?
- 如果服務掛、機組、整個數據中心掛掉了怎么辦?
- 如果數據庫損壞了怎么辦?
- 如果部署的時候失敗了怎么辦?
- 如果在部署成功之后生產環境上某些功能失敗了怎么辦?
- 集成性這方面的錯誤可以有千萬種可能,那么我們應該如何來避免?
使用一些技術比如:熔斷(circuit breaker)、超時設定(timeouts)、握手信號(handshaking)、艙壁(bulkheads)來幫助我們保護這些系統集成之前的問題。
熔斷模式(circuit breaker)可以參考電路熔斷,如果一條線路電壓過高,保險絲會熔斷,防止火災。放到我們的系統中,如果某個目標服務調用慢或者有大量超時,此時,熔斷該服務的調用,對于后續調用請求,不在繼續調用目標服務,直接返回,快速釋放資源。如果目標服務情況好轉則恢復調用。
艙壁模式(bulkheads)該模式像艙壁一樣對資源或失敗單元進行隔離,如果一個船艙破了進水,只損失一個船艙。比如采用微服務是首選,比如Docker。Docker是進程隔離的,單個 Docker失效不會影響其他Docker容器。或者把大的并行處理工作,由多個線程池來負荷分擔。
當然,如果當它開始工作的時候,說明我們的系統出現了比較大的問題,需要我們去調查分析。
注意那些不能看到代碼的組件、依懶項以及共享的資源。除了有正確的開發和測試流程以外,還應該盡量使用和真實生產環境一樣的數據、以及硬件網絡配置來進行測試。
跟蹤系統的響應來防止一些比較常見的問題比如服務不可用的情況,留意系統的平均響應時間,當它有異常的時候需要尋找原因以及采取相應的行動。
搭建日志、監控、以及系統操作的自動化操作平臺。由于微服務相對來說較獨立,可以更方便檢測失敗 所以監控起來會更容易一些。一些比較不錯的方式是在收集和分析日志的時候使用關聯ID、通用日志數據格式等。注意日志數據可能會非常龐大,所以要考慮日志的時間周期,定義對日志進行歸檔。另外還有一些不錯的工具可以將數據可視化在頁面中,可以更直觀的看到一些重要的進程。
為了保證服務的更新不會影響客戶端的使用,對于服務的版本控制也是非常重要的。有些情況下同時運行不同版本的服務也是很常見的情形,我們要做好長期向后兼容的準備。
務必要記住的事情
大多數情況下我們并不是從零開始去構建,而是在現有的系統上繼續添磚加瓦,而原有的系統在開發、運維、以及架構靈活性上都存在一些問題。想必很多優秀的開發人員在經歷這樣的情況的時候,都會想去拆解、重構整個系統,但是我們需要謹慎地來完成這個事情。當系統以錯誤的方式成拆分成組件或者服務單元之后也是一件很危險的事情 。
大多數系統在一開始的時候都是一個單體應用,后來不斷地被拆解成為微服務。下面有一些基本的理念可以在我們做拆解地時候當作參考:
- 在開始拆分前了解具體的業務需求和業務領域
- 注意一些和其它業務共享的功能和數據,它們需要被正確地模塊化
- 這種遷移和升級適合一步一步、一點一點地來完成,僅僅做當前最合適的事情
- 在開始之前很好地理解業務領域的范圍及邊界,因為對邊界的調整將是非常昂貴的
- 對于改造有清晰的結構此次會涉及到哪些團隊的調整
人、團隊、和組織的影響
這個話題太大,大到我們需要專門寫一篇文章來細述。在這里簡單地概括一下,在本文中我們所提到的架構的靈活性以及穩健的開發、測試、運維等流程都會影響企業的組織結構。合適的組織結構能夠給團隊更大的靈活性并且更有機會持續地創新,在這種組織結構下,團隊可以根據自己的節奏來工作。
組織不應該按技術來拆分團隊,比如前端團隊、移動端團隊、后端團隊或者根據不同的技術語言拆分團隊等,而是應該按照微服務來拆分團隊(也可以理解為按獨立的業務單元來拆分)。這樣在一個團隊里面就會包含各種不同的技術,可以用不同的語言來實現服務,這也給團隊更多的自由和自主權。
如何實踐?
容器化和集群工具
- Docker
- Docker Swarm
- Kubernetes
- Mesos
- Serf
- Nomad
基礎設施自動化/部署
- Jenkins
- Terraform
- Vagrant
- Packer
- Otto
- Chef、Puppet、 Ansible
配置
- Edda
- Archaius
- Decider
- ZooKeeper
服務發現
- Eureka
- Prana
- Finagle
- ZooKeeper
- Consul
路由和負載均衡
- Denominator
- Zuul
- Netty
- Ribbon
- HAProxy
- NGINX
監控、跟蹤、日志
- Hystrix
- Consul health checks
- Zipkin
- Pytheus
- SALP
- Elasticsearch logstash
數據協議
- Protocol Buffers
- Thrift
- JSON/XML/Other text
一些關于以上工具的介紹
由于本文涉及到大量的開源組件,下面簡單列舉一些以供參考(部分內容來自于互聯網)。
Docker Swarm
Swarm發布于2014年12月的DockerCon,用以管理Docker集群,并將其抽象為一個虛擬整體暴露給用戶。其架構以及命令比較簡單,同時也為希望管理Docker集群的Docker愛好者降低了學習和使用門檻。
Kubernetes
Kubernetes是Google開源的容器集群管理系統,其提供應用部署、維護、 擴展機制等功能,利用Kubernetes能方便地管理跨機器運行容器化的應用。
Apache Mesos
Apache Mesos是由加州大學伯克利分校的AMPLab首先開發的一款開源群集管理軟件,支持Hadoop、ElasticSearch、Spark、Storm 和Kafka等應用架構。
Mesos Aurora
Aurora 也是Apache的開源項目之一,是長期運行服務和計劃作業的 Mesos 框架。Aurora 通過一個共享機器池運行應用和服務,并且負責保持它們的運行,知直到永遠。當機器失效的時候,Aurora 會智能的重新規劃這些作業到健康的機器上。
Vagrant
Vagrant是一個基于Ruby的工具,用于創建和部署虛擬化開發環境。它 使用Oracle的開源VirtualBox虛擬化系統,使用 Chef創建自動化虛擬環境。
Packer
Packer是一個開源工具,用于從單一配置來源為多平臺創建相同的機器映像。目前支持的平臺包括Amazon EC2、DigitalOcean、OpenStack、VirtualBox和VMware。
Terraform
Terraform 是一個安全和高效的用來構建、更改和合并基礎架構的工具。采用 Go 語言開發。Terraform 可管理已有的流行的服務,并提供自定義解決方案。
Consul
Consul是HashiCorp公司推出的開源工具,用于實現分布式系統的服務發現與配置。與其他分布式服務注冊與發現的方案,Consul的方案更"一站式",內置了服務注冊與發現框 架、分布一致性協議實現、健康檢查、Key/Value存儲、多數據中心方案,不再需要依賴其他工具(比如ZooKeeper等)。使用起來也較為簡單。Consul用Golang實現,因此具有天然可移植性(支持Linux、windows和Mac OS X);安裝包僅包含一個可執行文件,方便部署,與Docker等輕量級容器可無縫配合。
Eureka
Eureka 是一個基于 REST 的服務,它主要是用于定位服務,以實現 AWS 云端的負載均衡和中間層服務器的故障轉移。
Ribbon
Ribbon 是 Netflix 發布的云中間層服務開源項目,其主要功能是提供客戶側軟件負載均衡算法。
Zuul
Zuul 是提供動態路由,監控,彈性,安全等的邊緣服務。Zuul 相當于是設備和 Netflix 流應用的 Web 網站后端所有請求的前門。Zuul 可以適當的對多個 Amazon Auto Scaling Groups 進行路由請求。
Finagle
Finagle是Twitter基于Netty開發的支持容錯的、協議無關的RPC框架,該框架支撐了Twitter的核心服務。
Zipkin
Zipkin 是 Twitter 的一個開源項目,允許開發者收集 Twitter 各個服務上的監控數據,并提供查詢接口。該系統讓開發者可通過一個 Web 前端輕松的收集和分析數據,例如用戶每次請求服務的處理時間等,可方便的監測系統中存在的瓶頸。
Hystrix
Hystrix旨在通過控制那些訪問遠程系統、服務和第三方庫的節點,從而對延遲和故障提供更強大的容錯能力。Hystrix具備擁有回退機制和斷路器功能的線程和信號隔離,請求緩存和請求打包(request collapsing,即自動批處理,譯者注),以及監控和配置等功能。Hystrix源于Netflix API團隊在2011年啟動的彈性工程工作,而目前它在Netflix每天處理著數百億的隔離線程以及數千億的隔離信號調用。Hystrix是基于Apache License 2.0協議的開源的程序庫,目前托管在GitHub上。
ZooKeeper
ZooKeeper是一個分布式的,開放源碼的分布式應用程序協調服務,是Google的Chubby一個開源的實現,是Hadoop和Hbase的重要組件。它是一個為分布式應用提供一致性服務的軟件,提供的功能包括:配置維護、域名服務、分布式同步、組服務等。
etcd
etcd是一個高可用的鍵值存儲系統,主要用于共享配置和服務發現。etcd是由CoreOS開發并維護的,靈感來自于 ZooKeeper 和 Doozer,它使用Go語言編寫,并通過Raft一致性算法處理日志復制以保證強一致性。Raft是一個來自Stanford的新的一致性算法,適用于分布式系統的日志復制,Raft通過選舉的方式來實現一致性,在Raft中,任何一個節點都可能成為Leader。Google的容器集群管理系統Kubernetes、開源PaaS平臺Cloud Foundry和CoreOS的Fleet都廣泛使用了etcd。
Protocol Buffers
Protocol Buffers是Google公司開發的一種數據描述語言,類似于XML能夠將結構化數據序列化,可用于數據存儲、通信協議等方面。它不依賴于語言和平臺并且可擴展性極強。現階段官方支持C++、JAVA、Python等三種編程語言,但可以找到大量的幾乎涵蓋所有語言的第三方拓展包。
通過它,你可以定義你的數據的結構,并生成基于各種語言的代碼。這些你定義的數據流可以輕松地在傳遞并不破壞你已有的程序。并且你也可以更新這些數據而現有的程序也不會受到任何的影響。
Thrift
Thrift是一種可伸縮的跨語言服務的發展軟件框架。它結合了功能強大的軟件堆棧的代碼生成引擎,以建設服務,工作效率和無縫地與C + +,C#,Java,Python和PHP和Ruby結合。thrift是facebook開發的,我們現在把它作為開源軟件使用。thrift允許你定義一個簡單的定義文件中的數據類型和服務接口。以作為輸入文件,編譯器生成代碼用來方便地生成RPC客戶端和服務器通信的無縫跨編程語言。
原文地址 http://lenadroid.github.io/posts/adjustable-flexible-architecture.html
文章列表