高併發設計筆記(續篇)

chenlixin發表於2019-12-02

感謝大家對上篇的閱讀和點贊,由於內容比較多,選擇分開記錄。

如果想深入瞭解,還是建議去購買學習該門課程 《高併發系統設計》

分散式服務篇

系統架構:每秒1萬次請求的系統要做服務化拆分嗎?

瞭解實際業務中會基於什麼樣的考慮,對系統做微服務化拆分,其實,系統的 QPS 並不是決定性的因素。影響的因素,歸納為以下幾點:

  • 系統中,使用的資源出現擴充套件性問題,尤其是資料庫的連線數出現瓶頸;
  • 大團隊共同維護一套程式碼,帶來研發效率的降低,和研發成本的提升;
  • 系統部署成本越來越高。

從中你應該有所感悟:在架構演進的初期和中期,效能、可用性、可擴充套件性是我們追求的主要目標,高效能和高可用給使用者帶來更好的使用體驗,擴充套件性可以方便我們支撐更大量級的併發。但是當系統做的越來越大,團隊成員越來越多,我們就不得不考慮成本了。

這裡面的“成本”有著複雜的含義,它不僅代表購買伺服器的費用,還包括研發團隊,內部的開發成本,溝通成本以及運維成本等等,甚至有些時候,成本會成為架構設計中的決定性因素。

比方說,你做一個直播系統,在架構設計時除了要關注起播速度,還需要關注 CDN 成本;再比如作為團隊 Leader,你在日常開發中除了要推進正常的功能需求開發,也要考慮完善工具鏈建設,提高工程師的研發效率,降低研發成本。

這很好理解,如果在一個 100 個人的團隊中,你的工具為每個人每天節省了 10 分鐘,那麼加起來就是接近 17 小時,差不多增加了 2 個人工作時間。而正是基於提升擴充套件性和降低成本的考慮,我們最終走上了微服務化的道路。

微服務架構:微服務化後,系統架構要如何改造?

為了能夠更好地進行服務化的拆分,需要了解微服務化拆分的原則,此處延伸一些內容:

  1. “康威定律”提到,設計系統的組織,其產生的設計等同於組織間的溝通結構。通俗一點說,就是你的團隊組織結構是什麼樣的,你的架構就會長成什麼樣。如果你的團隊分為服務端開發團隊,DBA 團隊,運維團隊,測試團隊,那麼你的架構就是一體化的,所有的團隊共同為一個大系統負責,團隊內成員眾多,溝通成本就會很高;而如果你想實現微服務化的架構,那麼你的團隊也要按照業務邊界拆分,每一個模組由一個自治的小團隊負責,這個小團隊裡面有開發、測試、運維和 DBA,這樣溝通就只發生在這個小團隊內部,溝通的成本就會明顯降低。

  2. 微服務化的一個目標是減少研發的成本,其中也包括溝通的成本,所以小團隊內部成員不宜過多。按照亞馬遜 CEO,貝佐斯的“兩個披薩”的理論,如果兩個披薩不夠你的團隊吃,那麼你的團隊就太大了,需要拆分,所以一個小團隊包括開發、運維、測試以 6~8 個人為最佳;

  3. 如果你的團隊人數不多,還沒有做好微服務化的準備,而你又感覺到研發和部署的成本確實比較高,那麼一個折中的方案是,你可以優先做工程的拆分
    比如說,如果你使用的是 Java 語言,你可以依據業務的邊界,將程式碼拆分到不同的子工程中,然後子工程之間以 jar 包的方式依賴,這樣每個子工程程式碼量減少,可以減少打包時間;並且子工程程式碼內部,可以做到高內聚低耦合,一定程度上減少研發的成本,也不失為一個不錯的保守策略。

RPC框架:10萬QPS下如何實現毫秒級的服務呼叫?

為了最佳化 RPC 框架的效能,需要了解網路 I/O 模型序列化方式的選擇,它們是實現高併發 RPC 框架的要素,總結起來有三個要點:

  1. 選擇高效能的 I/O 模型,推薦使用同步多路 I/O 複用模型;
  2. 除錯網路引數,這裡面有一些經驗值的推薦。比如將 tcp_nodelay 設定為 true,也有一些引數需要在執行中來除錯,比如接受緩衝區和傳送緩衝區的大小,客戶端連線請求緩衝佇列的大小(back log)等等;
  3. 序列化協議依據具體業務來選擇。如果對效能要求不高,可以選擇 JSON,否則可以從 Thrift 和 Protobuf 中選擇其一。

建議閱讀成熟的 RPC 框架的原始碼。比如,阿里開源的 Dubbo,微博的 Motan 等等,理解它們的實現原理和細節,這樣會更有信心維護好自己的微服務系統;同時,也可以從優秀的程式碼中,學習到程式碼設計的技巧,比如說 Dubbo 對於 RPC 的抽象,SPI 擴充套件點的設計,這樣可以有助提升程式碼能力。

註冊中心:分散式系統如何定址?

本章重點:

  • 註冊中心可以讓我們動態地,變更 RPC 服務的節點資訊,對於動態擴縮容,故障快速恢復,以及服務的優雅關閉都有重要的意義;
  • 心跳機制是一種常見的探測服務狀態的方式,你在實際的專案中也可以考慮使用;
  • 我們需要對註冊中心中管理的節點提供一些保護策略,避免節點被過度摘除導致的服務不可用。

註冊中心雖然是一種簡單易懂的分散式元件,但是它在整體架構中的位置至關重要,不容忽視。同時,在它的設計方案中,也蘊含了一些系統設計的技巧,比如上,面提到的服務狀態檢測的方式,還有上面提到的優雅關閉的方式,瞭解註冊中心的原理,能給研發工作提供一些思路。

分散式Trace:橫跨幾十個分散式元件的慢請求要如何排查?

本章重點:

  • 服務的追蹤的需求主要有兩點,一點對程式碼要無侵入,可以使用切面程式設計的方式來解決;另一點是效能上要低損耗,建議採用靜態代理和日誌取樣的方式,來儘量減少追蹤日誌對於系統效能的影響;
  • 無論是單體系統還是服務化架構,無論是服務追蹤還是業務問題排查,都需要在日誌中增加 requestId,這樣可以將日誌串起來,呈現一個完整的問題場景。如果 requestId 可以在客戶端上生成,在請求業務介面的時候傳遞給服務端,那麼就可以把客戶端的日誌體系也整合進來,對於問題的排查幫助更大。

其實,分散式追蹤系統不是一項新的技術,而是若干項已有技術的整合,在實現上並不複雜,卻能夠幫助實現跨程式呼叫鏈展示、服務依賴分析,在效能最佳化和問題排查方面提供資料上的支援。所以,在微服務化過程中,它是一個必選項,無論是採用 Zipkin,Jaeger 這樣的開源解決方案,還是團隊內自研,都應該在微服務化完成之前,儘快讓它發揮應有的價值。

負載均衡:怎樣提升系統的橫向擴充套件能力?

本章重點:

  • 網站負載均衡服務的部署,是以 LVS 承接入口流量,在應用伺服器之前,部署 Nginx 做細化的流量分發,和故障節點檢測。當然,如果你的網站的併發不高,也可以考慮不引入 LVS。

  • 負載均衡的策略可以優先選擇動態策略,保證請求傳送到效能最優的節點上;如果沒有合適的動態策略,那麼可以選擇輪詢的策略,讓請求平均分配到所有的服務節點上。

  • Nginx 可以引入 nginx_upstream_check_module,對後端服務做定期的存活檢測,後端的服務節點在重啟時,也要秉承著“先切流量後重啟”的原則,儘量減少節點重啟對於整體系統的影響。

你可能會認為,像 Nginx、LVS 應該是運維所關心的元件,作為開發人員不用操心維護。其實,負載均衡服務是提升系統擴充套件性,和效能的重要元件,在高併發系統設計中,它發揮的作用是無法替代的。理解它的原理,掌握使用它的正確姿勢,應該是每一個後端開發同學的必修課。

API閘道器:系統的門面要如何做呢?

本章重點:

  1. API 閘道器分為入口閘道器出口閘道器兩類,入口閘道器作用很多,可以隔離客戶端和微服務,從中提供協議轉換、安全策略、認證、限流、熔斷等功能。出口閘道器主要是為呼叫第三方服務提供統一的出口,在其中可以對呼叫外部的 API 做統一的認證、授權,審計以及訪問控制;

  2. API 閘道器的實現重點在於效能和擴充套件性,你可以使用多路 I/O 複用模型執行緒池併發處理,來提升閘道器效能,使用責任鏈模式來提升閘道器的擴充套件性;

  3. API 閘道器中的執行緒池,可以針對不同的介面或者服務做隔離和保護,這樣可以提升閘道器的可用性;

  4. API 閘道器可以替代原本系統中的 Web 層,將 Web 層中的協議轉換、認證、限流等功能挪入到 API 閘道器中,將服務聚合的邏輯下沉到服務層。

API 閘道器可以為 API 的呼叫提供便捷,也可以為將一些服務治理的功能獨立出來,達到複用的目的,雖然在效能上可能會有一些損耗,但是一般來說,使用成熟的開源 API 閘道器元件,這些損耗都是可以接受的。所以,當微服務系統越來越複雜時,可以考慮使用 API 閘道器作為整體系統的門面。

多機房部署:跨地域的分散式系統如何做?

為了提升系統的可用性和穩定性,本章探討了多機房部署的難點,以及同城雙機房和異地多活的部署架構,本章重點:

  1. 不同機房的資料傳輸延遲,是造成多機房部署困難的主要原因,你需要知道,同城多機房的延遲一般在 1ms~3ms,異地機房的延遲在 50ms 以下,而跨國機房的延遲在 200ms 以下。

  2. 同城多機房方案可以允許有跨機房資料寫入的發生,但是資料的讀取,和服務的呼叫應該儘量保證在同一個機房中。

  3. 異地多活方案則應該避免跨機房同步的資料寫入和讀取,而是採取非同步的方式,將資料從一個機房同步到另一個機房。

多機房部署是一個業務發展到一定規模,對於機房容災有需求時,才會考慮的方案,能不做則儘量不要做。一旦你的團隊決定做多機房部署,那麼同城雙活已經能夠滿足你的需求了,這個方案相比異地多活要簡單很多。而在業界,很少有公司,能夠搭建一套真正的非同步多活架構,這是因為這套架構在實現時過於複雜,所以,輕易不要嘗試

總之,架構需要依據系統的量級和對可用性、效能、擴充套件性的要求,不斷演進和調整,盲目地追求架構的“先進性”只能造成方案的複雜,增加運維成本,從而給系統維護帶來不便。

Service Mesh:如何遮蔽服務化系統的服務治理細節?

本章重點:

  1. Service Mesh 分為資料平面和控制平面。資料平面主要負責資料的傳輸;控制平面用來控制服務治理策略的植入。出於效能的考慮,一般會把服務治理策略植入到資料平面中,控制平面負責服務治理策略資料的下發。
  2. Sidecar 的植入方式目前主要有兩種實現方式,一種是使用 iptables 實現流量的劫持;另一種是透過輕量級客戶端來實現流量轉發。

目前,在一些大廠中,比如微博、螞蟻金服,Service Mesh 已經開始在實際專案中大量的落地實踐,建議你持續關注這項技術。

它本身是一種將業務與通訊基礎設施分離的技術,如果你的業務上遇到多語言環境下,服務治理的困境,如果你的遺留服務,需要快速植入服務治理策略,如果你想要將你在服務治理方面積累的經驗,快速地與其他團隊共享,那麼 Service Mesh 就是一個不錯的選擇。

維護篇

給系統加上眼睛:服務端監控要怎麼做?

本章重點:

  1. 耗時、請求量和錯誤數是三種最通用的監控指標,不同的元件還有一些特殊的監控指標,你在搭建自己的監控系統的時候可以直接使用;

  2. Agent、埋點和日誌是三種最常見的資料採集方式;

  3. 訪問趨勢報表用來展示服務的整體執行情況,效能報表用來分析資源或者依賴的服務是否出現問題,資源報表用來追查資源問題的根本原因。這三個報表共同構成了你的服務端監控體系。

總之,監控系統是你發現問題,排查問題的重要工具,你應該重視它,並且投入足夠的精力來不斷地完善它。只有這樣,才能不斷地提高對系統運維的掌控力,降低故障發生的風險。

未完待續

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章