從0開始學架構-04複雜度來源-高性能

週四,我為你講了架構設計的主要目的是為了解決軟件系統複雜度帶來的問

題。那麼從今天開始,我將為你深入分析複雜度的 6 個來源,先來聊聊複雜度的來源之一高性能。

對性能孜孜不倦的追求是整個人類技術不斷髮展的根本驅動力。例如計算機,從電子管計算機到晶體管計算機再到集成電路計算機,運算性能從每秒幾次提升到每秒幾億次。但伴隨性能越來越高,相應的方法和系統複雜度也是越來越高。現代的計算機 CPU 集成了幾億顆晶體管,邏輯複雜度和製造複雜度相比最初的晶體管計算機,根本不可同日而語。

軟件系統也存在同樣的現象。最近幾十年軟件系統性能飛速發展,從最初的計算機只能進行簡單的科學計算,到現在 Google 能夠支撐每秒幾萬次的搜索。與此同時,軟件系統規模也從單臺計算機擴展到上萬臺計算機;從最初的單用戶單工的字符界面 Dos 操作系統,到現在的多用戶多工的 Windows 10 圖形操作系統。

當然,技術發展帶來了性能上的提升,不一定帶來複雜度的提升。例如,硬件存儲從紙帶→磁帶→磁盤→SSD,並沒有顯著帶來系統複雜度的增加。因為新技術會逐步淘汰舊技術,這種情況下我們直接用新技術即可,不用擔心繫統複雜度會隨之提升。只有那些並不是用來取代舊技術,而是開闢了一個全新領域的技術,才會給軟件系統帶來複雜度,因為軟件系統在設計的時候就需要在這些技術之間進行判斷選擇或者組合。就像汽車的發明無法取代火車,飛機的出現也並不能完全取代火車,所以我們在出行的時候,需要考慮選擇汽車、火車還是飛機,這個選擇的過程就比較複雜了,要考慮價格、時間、速度、舒適度等各種因素。

軟件系統中高性能帶來的複雜度主要體現在兩方面,一方面是單臺計算機內部為了高性能帶來的複雜度;另一方面是多臺計算機集群為了高性能帶來的複雜度。

單機複雜度

計算機內部複雜度最關鍵的地方就是操作系統。計算機性能的發展本質上是由硬件發展驅動的,尤其是 CPU 的性能發展。著名的“摩爾定律”表明了 CPU 的處理能力每隔 18 個月就翻一番;而將硬件性能充分發揮出來的關鍵就是操作系統,所以操作系統本身其實也是跟隨硬件的發展而發展的,操作系統是軟件系統的運行環境,操作系統的複雜度直接決定了軟件系統的複雜度。

操作系統和性能最相關的就是進程和線程。最早的計算機其實是沒有操作系統的,只有輸入、計算和輸出功能,用戶輸入一個指令,計算機完成操作,大部分時候計算機都在等待用戶輸入指令,這樣的處理性能很顯然是很低效的,因為人的輸入速度是遠遠比不上計算機的運算速度的。

為了解決手工操作帶來的低效,批處理操作系統應運而生。批處理簡單來說就是先把要執行的指令預先寫下來(寫到紙帶、磁帶、磁盤等),形成一個指令清單,這個指令清單就是我們常說的“任務”,然後將任務交給計算機去執行,批處理操作系統負責讀取“任務”中的指令清單並進行處理,計算機執行的過程中無須等待人工手工操作,這樣性能就有了很大的提升。

批處理程序大大提升了處理性能,但有一個很明顯的缺點:計算機一次只能執行一個任務,如果某個任務需要從 I/O 設備(例如磁帶)讀取大量的數據,在 I/O 操作的過程中,CPU 其實是空閒的,而這個空閒時間本來是可以進行其他計算的。

為了進一步提升性能,人們發明了“進程”,用進程來對應一個任務,每個任務都有自己獨立的內存空間,進程間互不相關,由操作系統來進行調度。此時的

CPU 還沒有多核和多線程的概念,為了達到多進程並行運行的目的,採取了分時的方式,即把 CPU 的時間分成很多片段,每個片段只能執行某個進程中的指令。雖然從操作系統和 CPU 的角度來說還是串行處理的,但是由於

CPU 的處理速度很快,從用戶的角度來看,感覺是多進程在並行處理。

多進程雖然要求每個任務都有獨立的內存空間,進程間互不相關,但從用戶的角度來看,兩個任務之間能夠在運行過程中就進行通信,會讓任務設計變得更加靈活高效。否則如果兩個任務運行過程中不能通信,只能是 A 任務將結果寫到存儲,B 任務再從存儲讀取進行處理,不僅效率低,而且任務設計更加複雜。為了解決這個問題,進程間通信的各種方式被設計出來了,包括管道、消息隊列、信號量、共享存儲等。多進程讓多任務能夠並行處理任務,但本身還有缺點,單個進程內部只能串行處理,而實際上很多進程內部的子任務並不要求是嚴格按照時間順序來執行的,也需要並行處理。例如,一個餐館管理進程,排位、點菜、買單、服務員調度等子任務必須能夠並行處理,否則就會出現某個客人買單時間比較長(比如說信用卡刷不出來),其他客人都不能點菜的情況。為了解決這個問題,人們又發明了線程,線程是進程內部的子任務,但這些子任務都共享同一份進程數據。為了保證數據的正確性,又發明了互斥鎖機制。有了多線程後,操作系

統調度的最小單位就變成了線程,而進程變成了操作系統分配資源的最小單位。

多進程多線程雖然讓多任務並行處理的性能大大提升,但本質上還是分時系統,並不能做到時間上真正的並行。解決這個問題的方式顯而易見,就是讓多個 CPU 能夠同時執行計算任務,從而實現真正意義上的多任務並行。目前這樣的解決方案有 3 種:SMP(Symmetric Multi-Processor,對稱多處理器結構)、NUMA(Non-Uniform Memory Access,非一致存儲訪問結構)、MPP

(Massive Parallel Processing,海量並行處理結構)。其中 SMP 是我們最常見的,目前流行的多核處理器就是 SMP 方案。

操作系統發展到現在,如果我們要完成一個高性能的軟件系統,需要考慮如多進程、多線程、進程間通信、多線程併發等技術點,而且這些技術並不是最新的就是最好的,也不是非此即彼的選擇。在做架構設計的時候,需要花費很大的精力來結合業務進行分析、判斷、選擇、組合,這個過程同樣很複雜。舉一個最簡單的例子:Nginx 可以用多進程也可以用多線程,JBoss 採用的是多線程;Redis 採用的是單進程,Memcache 採用的是多線程,這些系統都實現了高性能,但內部實現差異卻很大。

集群的複雜度

雖然計算機硬件的性能快速發展,但和業務的發展速度相比,還是小巫見大巫了,尤其是進入互聯網時代後,業務的發展速度遠遠超過了硬件的發展速度。

例如:

•2016 年“雙 11”支付寶每秒峰值達 12 萬筆支付。

•2017 年春節微信紅包收發紅包每秒達到 76 萬個。要支持支付和紅包這種複雜的業務,單機的性能無論如何是無法支撐的,必須採用機器集群的方式來達到高性能。例如,支付寶和微信這種規模的業務系統,後臺系統的機器數量都是萬臺級別的。

通過大量機器來提升性能,並不僅僅是增加機器這麼簡單,讓多臺機器配合起來達到高性能的目的,是一個複雜的任務,我針對常見的幾種方式簡單分析一下。

1. 任務分配

任務分配的意思是指每臺機器都可以處理完整的業務任務,不同的任務分配到不同的機器上執行。

我從最簡單的一臺服務器變兩臺服務器開始,來講任務分配帶來的複雜性,整體架構示意圖如下。

從圖中可以看到,1 臺服務器演變為 2 臺服務器後,架構上明顯要複雜多了,主要體現在:

•需要增加一個任務分配器,這個分配器可能是硬件網絡設備(例如,

F5、交換機等),可能是軟件網絡設備(例如,LVS),也可能是負載均衡軟件(例如,Nginx、HAProxy),還可能是自己開發的系統。選擇合適的任務分配器也是一件複雜的事情,需要綜合考慮性能、成本、可維護性、可用性等各方面的因素。

•任務分配器和真正的業務服務器之間有連接和交互(即圖中任務分配器到業務服務器的連接線),需要選擇合適的連接方式,並且對連接進行管理。例如,連接建立、連接檢測、連接中斷後如何處理等。

•任務分配器需要增加分配算法。例如,是採用輪詢算法,還是按權重分配,又或者按照負載進行分配。如果按照服務器的負載進行分配,則業務服務器還要能夠上報自己的狀態給任務分配器。

這一大段描述,即使你可能還看不懂,但也應該感受到其中的複雜度了,更何況還要真正去實踐和實現。

上面這個架構只是最簡單地增加 1 臺業務機器,我們假設單臺業務服務器每秒能夠處理 5000 次業務請求,那麼這個架構理論上能夠支撐 10000 次請求,實際上的性能一般按照 8 折計算,大約是 8000 次左右。

如果我們的性能要求繼續提高,假設要求每秒提升到 10 萬次,上面這個架構會出現什麼問題呢?是不是將業務服務器增加到 25 臺就可以了呢?顯然不是,因為隨著性能的增加,任務分配器本身又會成為性能瓶頸,當業務請求達到每秒 10 萬次的時候,單臺任務分配器也不夠用了,任務分配器本身也需要擴展為多臺機器,這時的架構又會演變成這個樣子。

這個架構比 2 臺業務服務器的架構要複雜,主要體現在:

•任務分配器從 1 臺變成了多臺(對應圖中的任務分配器 1 到任務分配器 M),這個變化帶來的複雜度就是需要將不同的用戶分配到不同的任

務分配器上(即圖中的虛線“用戶分配”部分),常見的方法包括 DNS 輪詢、智能 DNS、CDN(Content Delivery Network,內容分發網

絡)、GSLB 設備(Global Server Load Balance,全局負載均衡)等。

•任務分配器和業務服務器的連接從簡單的“1 對多”(1 臺任務分配器連接多臺業務服務器)變成了“多對多”(多臺任務分配器連接多臺業務服務器)的網狀結構。

•機器數量從 3 臺擴展到 30 臺(一般任務分配器數量比業務服務器要少,這裡我們假設業務服務器為 25 臺,任務分配器為 5 臺),狀態管理、故障處理複雜度也大大增加。

上面這兩個例子都是以業務處理為例,實際上“任務”涵蓋的範圍很廣,可以指完整的業務處理,也可以單指某個具體的任務。例如,“存儲”“運算”“緩存”等都可以作為一項任務,因此存儲系統、運算系統、緩存系統都可以按照任務分配的方式來搭建架構。此外,“任務分配器”也並不一定只能是物理上存在的機器或者一個獨立運行的程序,也可以是嵌入在其他程序中的算法,例如

Memcache 的集群架構。

(http://my.oschina.net/uploads/space/2010/1015/163250_g2tS_98095.png)

2. 任務分解

通過任務分配的方式,我們能夠突破單臺機器處理性能的瓶頸,通過增加更多的機器來滿足業務的性能需求,但如果業務本身也越來越複雜,單純只通過任務分配的方式來擴展性能,收益會越來越低。例如,業務簡單的時候 1 臺機器擴展到 10 臺機器,性能能夠提升 8 倍(需要扣除機器群帶來的部分性能損耗,因此無法達到理論上的 10 倍那麼高),但如果業務越來越複雜,1 臺機器擴展到 10 臺,性能可能只能提升 5 倍。造成這種現象的主要原因是業務越來越複雜,單臺機器處理的性能會越來越低。為了能夠繼續提升性能,我們需要採取第二種方式:任務分解。

繼續以上面“任務分配”中的架構為例,“業務服務器”如果越來越複雜,我們可以將其拆分為更多的組成部分,我以微信的後臺架構為例。

(http://image.jiagoushuo.com/2016/qAnayi.jpg)

通過上面的架構示意圖可以看出,微信後臺架構從邏輯上將各個子業務進行了拆分,包括:接入、註冊登錄、消息、LBS、搖一搖、漂流瓶、其他業務(聊天、視頻、朋友圈等)。

通過這種任務分解的方式,能夠把原來大一統但複雜的業務系統,拆分成小而簡單但需要多個系統配合的業務系統。從業務的角度來看,任務分解既不會減少功能,也不會減少代碼量(事實上代碼量可能還會增加,因為從代碼內部調

用改為通過服務器之間的接口調用),那為何通過任務分解就能夠提升性能呢?

主要有幾方面的因素:

•簡單的系統更加容易做到高性能

系統的功能越簡單,影響性能的點就越少,就更加容易進行有針對性的優化。而系統很複雜的情況下,首先是比較難以找到關鍵性能點,因為需要考慮和驗證的點太多;其次是即使花費很大力氣找到了,修改起來也不容易,因為可能將 A 關鍵性能點提升了,但卻無意中將 B 點的性能降低了,整個系統的性能不但沒有提升,還有可能會下降。

•可以針對單個任務進行擴展

當各個邏輯任務分解到獨立的子系統後,整個系統的性能瓶頸更加容易發現,而且發現後只需要針對有瓶頸的子系統進行性能優化或者提升,不需要改動整個系統,風險會小很多。以微信的後臺架構為例,如果用戶數增長太快,註冊登錄子系統性能出現瓶頸的時候,只需要優化登錄註冊子系統的性能(可以是代碼優化,也可以簡單粗暴地加機器),消息邏輯、LBS 邏輯等其他子系統完全不需要改動。

既然將一個大一統的系統分解為多個子系統能夠提升性能,那是不是劃分得越細越好呢?例如,上面的微信後臺目前是 7 個邏輯子系統,如果我們把這 7 個邏輯子系統再細分,劃分為 100 個邏輯子系統,性能是不是會更高呢?

其實不然,這樣做性能不僅不會提升,反而還會下降,最主要的原因是如果系統拆分得太細,為了完成某個業務,系統間的調用次數會呈指數級別上升,而系統間的調用通道目前都是通過網絡傳輸的方式,性能遠比系統內的函數調用要低得多。我以一個簡單的圖示來說明。

從圖中可以看到,當系統拆分 2 個子系統的時候,用戶訪問需要 1 次系統間的請求和 1 次響應;當系統拆分為 4 個子系統的時候,系統間的請求次數從 1 次增長到 3 次;假如繼續拆分下去為 100 個子系統,為了完成某次用戶訪問,系統間的請求次數變成了 99 次。為了描述簡單,我抽象出來一個最簡單的模型:假設這些系統採用 IP 網絡連接,理想情況下一次請求和響應在網絡上耗費為 1ms,業務處理本身耗時為

50ms。我們也假設系統拆分對單個業務請求性能沒有影響,那麼系統拆分為 2 個子系統的時候,處理一次用戶訪問耗時為 51ms;而系統拆分為 100 個子系統的時候,處理一次用戶訪問耗時竟然達到了 149ms。

雖然系統拆分可能在某種程度上能提升業務處理性能,但提升性能也是有限的,不可能系統不拆分的時候業務處理耗時為 50ms,系統拆分後業務處理耗時只要 1ms,因為最終決定業務處理性能的還是業務邏輯本身,業務邏輯本身沒有發生大的變化下,理論上的性能是有一個上限的,系統拆分能夠讓性能逼近這個極限,但無法突破這個極限。因此,任務分解帶來的性能收益是有一個度的,並不是任務分解越細越好,而對於架構設計來說,如何把握這個粒度就非常關鍵了。

小結

今天我給你講了軟件系統中高性能帶來的複雜度主要體現的兩方面,一是單臺計算機內部為了高性能帶來的複雜度;二是是多臺計算機集群為了高性能帶來的複雜度,希望對你有所幫助。

這就是今天的全部內容,留一道思考題給你吧。你所在的業務體系中,高性能的系統採用的是哪種方式?目前是否有改進和提升的空間?

歡迎你把答案寫到留言區,和我一起討論。相信經過深度思考的回答,也會讓

你對知識的理解更加深刻。

從0開始學架構-04複雜度來源-高性能


分享到:


相關文章: