搭建高併發、高可用的系統

Double-Jin發表於2021-01-18

前言:

以下全文分享的內容不涉及到開發高併發、高可用系統具體的實現方法,主要分享的是在編寫高併發、高可用系統的思想,具體的實現方法會隨著時間而過時,但思想思路是常青的。

文章會先從背景知識開始介紹,讓大家瞭解一些基本的概念,只有明白每樣東西的優勢和劣勢,才能跟據自己現有的需求跟現實的資源去選擇合適的工具。再從高併發、高可用系統的瓶頸和棘手的問題去反推需要我們去解決的問題。

以下全文需要用客觀辯證的眼光來閱讀,其實日常應該用客觀辯證的眼光來看待日常的問題,正反兩面系統地、完整地看待事物本身。

背景知識:

資源利用率

這裡的資源指的是硬體資源,如cpu、記憶體、硬碟、網路頻寬、網格延遲等。現實情況下資源都是有限的,不可能無限地增加硬體來滿足軟體服務。因此我們在使用軟體服務中,要跟據軟體的硬體使用率合理地安排機器,在現有的條件和成本下實現最大的資源利用。

Latency numbers every programmer should know

搭建高併發、高可用的系統

引用:gist.github.com/hellerbarde/284337...

當我們看到上面的表格對比 ,CPU 週期的延遲、CPU 快取訪問延遲、網路延遲、磁碟儲存延遲時,我們可以看到 CPU 通過網路或磁碟訪問與 I/O 相比是非常快的。 SSD 與老式的旋轉硬碟相比,也可以快很多。很明顯,跨機房的 TCP 延遲 與發生在一個例項或機器內部的延遲相比,要大得多。我們在構建高可靠系統的時候,也要牢記這些資料來權衡和平衡問題。

單體&微服務&中臺

在軟體工程中,核心思想之一是儘可能減少程式碼耦合,如果發現程式碼耦合,就要解耦,但模組間有依賴關係必然存在耦合,理論上的絕對零耦合是做不到的,但可以通過一些現有的方法將耦合度降至最低。核心思想之二是在不同的業務上選用最合適的工具,比如搜尋功能,你如果對Mysql一張超過50W的表做模糊搜尋,那慢查詢將會搞垮該機器的cpu,對於搜尋功能,只能選用搜尋引擎來實現,其他的功能也如此。

單體、微服務就是在專案的不同階段中同的解耦方案,單體專案就不能降低耦合度跟提高程式碼重用嗎?就不能搭建高併發系統嗎?答案是可以的,只不過微服務做得更徹底。

中臺又跟單體和微服務不同,是兩個維度的東西,中臺一般指的是業務中臺,可共享可複用的一組業務,這個是從業務視角出發的。中臺並不跟微服務繫結,完全可以基於單體服務去打造一個業務中臺。只是微服務技術的體系更合適,符合業務中臺長期不斷優化、不斷沉澱的思路,所以一般業務中臺也是基於微服務技術體系來構建的。

微服務跟中臺的優勢很明顯,但需要付出的代價也是相應的,如使用者量1000萬左右的專案單體應用技術團隊會在15人左右,當單體應用分拆為多個微服務後,單個微服務開發跟維護人員就要4人左右,加之運維跟排錯成本也會相應提高,開發週期也一延再延。中臺更是百人級的技術團隊才能玩得起的。
辯證的看待架構方案,回看10多年前SOA架構,到當下微服務、中臺、雲原生等分散式概念流行,架構的思想漸進會不斷更新,並不是說用了某某架構就等同於商業上的成功,技術是推動商業而不是綁架商業,行業內微服務化、中臺化失敗的案例太多了。
下面會有三個架構圖,其中單體跟微服務是我畫的,中臺的圖是網上找的,可以看出,單體應用一樣能做得很大型,但微服務的優勢也很明顯,這也是未來的方向。回頭辯證地看兩個架構的缺點,單體應用的解耦度相對高,微服務會浪費較多的資源到周邊服務,運維排錯的成本也相對高。

單體架構圖

搭建高併發、高可用的系統

微服務架構圖
搭建高併發、高可用的系統

中臺架構圖

搭建高併發、高可用的系統

程式&執行緒&協程&程式語言

要理解程式的執行方式恐怕不是三言兩語能解釋清楚,很多文章中所講的內容都過於片面和學術化,不易於理解。個人只有實際使用程式和協程的工作經歷,對於執行緒只侷限於網上文章,所以在這裡放幾篇寫得不錯的文章給大家閱讀,方便大家有個基礎的認識,在這建議大家能掌握多門不同的程式語言後,用對比的方式去看待程式語言的差異,比如php的程式、swoole/go的協程、java的執行緒等等,瞭解各程式語言的適用場景、執行方式、缺點等等之後,在實際的工作中跟據公司的技術團隊跟資源選擇相對適用的工具。

分享連結:

www.imooc.com/article/31751te

www.zhihu.com/question/63520385/an...

www.360doc.com/content/20/0417/14/3...

任務型別

應用程式中的任務可能是 I/O 密集的,也可能是 CPU 密集的。通過解耦 IO和 CPU 計算任務,我們可以最大限度地提高 IO 硬體和 CPU 及記憶體硬體的效能。
I/O 密集操作是在程式系統中,大部分時間都在等網路請求、磁碟I/O完成的邏輯操作就是I/O密集操作
CPU 密集操作是在程式系統中,大部分時間用來計算、邏輯判斷等CPU運算的邏輯操作就是CPU密集操作

實現方向

現實中很多專案都會存在前期資源不足、趕工期、開發人員水平低、架構不滿足現狀等諸多問題,效能問題是網際網路專案中最難解決的問題。解決的思路是先要找出整個系統的瓶頸,避免區域性優化,再站在未來回看現在,避免每隔數月重複修改。

導步任務佇列

將同步任務分拆成多個子任務派發到佇列中並立即返回結果,而不是等待任務結果的通知。
優點

  • 將順序阻塞執行流程解耦多路複用模式,提高資源利用率。
  • 每一個元件都不只是空閒等待,而是在一個執行流沒有準備好的情況下開始執行其他任務。
  • 每一個元件功能相對單一,程式碼塊相對簡結

缺點

  • 非同步不一定總是更快,每個執行單元之間有額外的通訊成本和管理成本
  • 順序邏輯被分割成多個小部分,管理起來更加複雜。
  • 我們必須做一些額外的工作來管理每個執行單元的使用容量和異常情況。

非阻塞IO

阻塞或非阻塞是指你的使用者區程式碼如何通過系統呼叫與作業系統核心進行通訊的模式,go、swoole裡的協程,java裡的執行緒都能實現非阻塞模式,在非阻塞模式下,系統會將控制權傳遞給呼叫者,並返回狀態,在 I/O 操作時不會被阻塞。阻塞IO只會等當前IO完成時才將控制權傳遞給呼叫者。如果當IO處於等待狀態,則一直等待。

如果一個 CPU 核以 3GHz 的速度執行,它每秒可以執行 30 億次 CPU 迴圈。一次非阻塞系統呼叫可能只需要幾個 CPU 週期,也就是幾納秒,相比之下,阻塞系統呼叫可能需要幾百毫秒。差距是1000 萬倍。

優點

  • 並行處理能力大大提高,解決了IO等待時間裡無法傳送更多請求問題

缺點

  • 非阻塞IO的排程器存在一定的cpu損耗
  • 程式碼編寫能力要求相對阻塞IO更高

系統可靠性-處理異常

應用程式可能會崩潰、退出、佔用太多記憶體、響應緩慢。

需要實現但不限於:

  • 完善且分類明細的日誌系統
  • 捕捉處理全域性異常和錯誤
  • 完善的監控報警機制
  • 完善程式碼稽核機制
  • 完善測試流程

網路可靠性-超時處理

如果應用程式正在使用上游服務,例如資料庫,快取服務,遠端 HTTP 服務,網路連線超時異常是十分正常的情況,因為我們可以控制如資料庫、快取服務等內部服務,但無法控制第三方服務,所以合理的超時處理機制十分重要。

需要實現但不限於:

  • 對第三方服務抱有悲劇感
  • 構建網路超時機制
  • 構建重試呼叫次數機制,重試一定次數後暫時停止該失連的第三方

系統安全

系統安全是個龐大的課題,內容多偏運維為主,其中包括前端+後端+資料庫等軟體服務漏洞、網路安全漏洞、伺服器漏洞等等,這些漏洞的存在,使得系統很容易被攻擊者利用,進而破壞系統的安全性。在實際的生產環境中,除了ddos這種大流量攻擊需要用到專業的硬體跟流量硬抗之外,其他問題都有一定的解決辦法。
系統安全的內容足夠寫幾本書了,由於篇幅有限,這裡就不按點列明,在此分享幾點書跟文章,個人覺得站在攻擊者的角度來學習會有意思過防守者。系統安全要跟系統體量掛鉤,不要因此影響業務流程與伺服器效能,也流費公司資源。
分享連結:

《黑客攻防—web安全實戰詳解》

《安全之路:Web滲透技術及實戰案例解析(第2版)》

服務隔離

服務隔離是生產環境中很重要的思想,任何在軟體系統中,故障是不可避免的,並且大多數還是不可預測的,越是使用者量大的系統,發生故障的影響越是嚴重。因此,我們只能在系統的設計之初就充分的考慮好應對措施,如何在故障發生時,去盡最大可能的止損和減少故障範圍,儘可能的去提高系統的整體可用率。

  • 按服務/功能做隔離
    按服務能來隔離可以說是微服務主打的賣點了,相信有了解過微服務的同學也都瞭解,但其實單體應用也是要部置多個服務,前端跟據業務請求對應的伺服器即可。除了應用系統的服務,如資料庫等周邊服務也應該按業務功能來隔離,常見的讀寫分離、微服務裡的分散式資料庫,還有搜尋模組轉移到ES,資料統計轉移到BI工具。最大可能的止損和減少故障範圍。

  • 按使用者分類隔離
    saas系統會遇到多租戶資料庫解決方案的問題,在saas裡使用者 = 租戶,隔離是為了在服務穩定性上跟資料上不受其他租戶影響。普通的系統也是可以按使用者來分類隔離的,如可以按會員分組。又或者按地區分組。按照自己的業務場景來分組。最大可能的止損和減少故障範圍。

監控、釋出、資料上報系統

當系統涉及的到多種服務叢集時,應用伺服器的程式碼釋出、全部伺服器的狀態監控/上報/分析的重要性不言而喻。如何管理好這麼多伺服器?如何彈性增加某個叢集的機器?如何能在某個指標不正常時提前吿知運維、發開人員?這些問題都是實際生產中經常遇到的,大廠有大廠的解決方案,小廠有小廠的解決方案,如何按照當前資源來完成這些工作,這也是運維崗位體現價值的地方。

總結

至此,主體內容分享完了,還有很多點其實是沒分享到的,以上的每一項的介紹都是在本人水平有限的情況下總結出來的淺顯之見,深入到每一項學問都很深,深到可以隨便在網上搜一個關鍵詞都能搜到收費的專案諮詢,深到值得大家再深入瞭解學習,大家辯證地按需吸收。在學習途中要學會區分那些垃圾文章,如上來就是快速入門xxx、中級必學、高階必學這些。每一個點、每一個服務,都要重視基礎,如果只瞭解表面的使用,卻不明白服務作者設定各個引數的意義,那麼出了問題你也不會處理。只求速成,不願深鑽,最終只會練成綿綿無力的貓貓拳。
在系統立項的第一天起,通過不斷地投入資源,不斷地發現問題解決問題,到站在更高緯度回來當前問題來投入資源重構,每一天每一步都伴隨著企業、專案組的壯大,如果提前預判到系統的併發量不會增加,也就不需要再投入了。
高併發、高可靠的系統是個龐大的工程,世界上的系統業務又大相徑庭,想要找一個完全一樣的方案來複制是不可能的,只能在清楚思路後跟據自身情況按需吸收。企圖通過一個單點的工具/策略/方法去解決一個系統問題是沒有成功可能的,因而不存在解決任何問題的銀彈。

參考文獻:

《非同步和⾼併發系統開發指南 - ⽤ Swoole PHP 協程構建非同步和⾼併發系統》 - Bruce Dou

《沒有銀彈》 - Fred Brooks

其他參考文章

本作品採用《CC 協議》,轉載必須註明作者和本文連結
未經允許禁止轉載 -- 苦力小林,

相關文章