天天看點

從小白到架構師的成長之路:走向微服務

作者:小柴學Java

還是從部落格開始

還是從我們熟悉的部落格網站開始,小明是個喜歡寫部落格的程式員,他覺得市面上的部落格網站都太醜了,就想自己搞一個。說幹就幹,小明抄起LAMP(Linux-Apache-MySQL-PHP)一把梭三下五除二就把網站搞了起來,網站的名字就暫定為「淘金網」

從小白到架構師的成長之路:走向微服務

由于淘金網界面美觀友善好用, 越來越多的網友開始入駐淘金網。漸漸地使用者們覺得隻能寫部落格功能太單一了,有些使用者想要把系列文章編輯成電子書、有些使用者想要做直播分享,有些使用者想要在這裡發狀态,小明也想要賣點會員補貼一下伺服器的支出……

沒關系,都可以有,繼續一把梭加邏輯就是了:

從小白到架構師的成長之路:走向微服務

日子也就這麼一天一天的過下去,淘金網逐漸從個人小站發展成了一家小有規模的創業公司。網站開發過程中也是小毛病不斷大毛病不少:

  1. 部落格、電子書、微網誌每個子產品都向使用者、訂單這些共用表裡塞了一堆字段,一動表結構線上就出 BUG, 誰都不敢動。
  2. 即使上線一個小功能也要釋出整個網站,有時候會不小心帶上了一些未經測試的代碼,有時候會在意想不到的地方出了錯誤。
  3. 一個業務容量不足需要加機器, 就相當于給所有子產品做了擴容,白白支出了其它子產品的固定開銷(無負載的服務所消耗的資源,比如 Spring 容器消耗的記憶體,背景線程消耗的 CPU)。

這些都還可以忍,直到那個陽光明媚的早晨小明美滋滋打開背景想要看一眼今天的入賬時,卻發現網站打不開了。。。

從小白到架構師的成長之路:走向微服務

檢查日志發現,在編寫直播的業務邏輯時不小心寫了一個不停向數組中插入元素的死循環,過不了多久伺服器就會 OOM 崩潰掉,隻有 supervisord 還在徒勞的嘗試重新開機伺服器。。。

小明是個未雨綢缪的人, 他覺得BUG 是無法杜絕的,這樣的故障早晚會再次發生。 于是小明靈機一動決定把部落格、電子書、會員、直播這些業務拆分成獨立的服務端程式,一個子產品拉起一個程序,這樣任你 OOM 還是 panic 都不會影響其它業務,萬一出了什麼故障損失也就小得多了。

從小白到架構師的成長之路:走向微服務

小明發現把服務拆開後一些老問題也解決了:未測試代碼被誤上線的情況幾乎沒有了;各個子產品可以按照需求各自規劃伺服器資源了;而且還有個意外之喜,每個子產品可以用不同的技術棧, 前端可以用 node.js 做 BFF, 資料分析可以用 Hadoop, 推薦系統可以用 Python...

還有個問題沒有解決,不同子產品依舊依賴同一個資料庫,表結構牽一發而動全身,每次修改都小心翼翼如履薄冰。小明決定一鼓作氣,将資料庫也按照業務闆塊進行拆分,每個子產品隻允許讀寫自己的資料庫,需要其它子產品資料時一律調 RPC 接口,禁止直接通路資料庫。

從小白到架構師的成長之路:走向微服務

ok, 現在一切都是那麼的完美,歲月如此靜好……

服務治理的難題

話說自從服務拆分之後再也沒有出現過全站崩潰的事故,小明美滋滋的準備開個年會,大家拿了年終獎回家過年。就在年會上,小明聽到程式員們在抱怨:

  • “支付系統一到有活動就擴容,活動結束就把臨時加的機器下掉,其它人也得跟着改配置檔案才能找到服務位址”
  • “一個請求經過了好幾個子產品,出了 BUG 找半天都查不清是哪個子產品的故障”
  • “一上促銷商城那邊的調用就特别多,差點把我們的支付壓垮,直播的人就來抱怨說沒法刷禮物”
  • “會員那邊不靠譜,付款失敗還照樣發會員,損失好多錢”

小明把這些問題一一記錄下來,開始尋找答案。

服務發現

支付系統一到有活動就擴容,活動結束就把臨時加的機器下掉,其它人也得跟着改配置檔案才能找到服務位址

我們服務部署在不同的伺服器上,而且會随着負載情況不時的增删機器,調用方如何及時準确的獲得服務的位址?執行個體之間如何均衡負載?我們将這個問題稱為服務發現。

DNS 系統也可以算是一種服務發現,服務提供方的節點在啟動後向 DNS 注冊自己的位址,節點下線前将自己從 DNS 的節點清單中删除。服務調用方通過域名向DNS查詢服務提供方的實際位址,DNS 會在節點清單中按預定政策挑選一個節點的 ip 位址傳回給調用方。

在實際使用中更多的還是采用 Zookeeper、Consul、Etcd 等高一緻性的 KV 元件做服務發現:

從小白到架構師的成長之路:走向微服務
從小白到架構師的成長之路:走向微服務

服務發現系統會通過心跳包等機制檢查節點健康狀态,并屏蔽不健康的節點。這樣即使節點在崩潰前沒有向配置中心報告故障,服務發現也能避免請求繼續到達異常節點:

從小白到架構師的成長之路:走向微服務

因為服務發現可以友善的控制調用方通路的節點,是以也常常用來實作灰階釋出,A/B測試等功能:

從小白到架構師的成長之路:走向微服務

限流、熔斷、降級

一上促銷商城那邊的調用就特别多,差點把我們的支付壓垮,直播的人就來抱怨說沒法刷禮物

雖然服務拆分之後單個程序崩潰不會波及其它程序,但是若下層的服務的實際負載超出了最大吞吐量出現響應過慢或逾時的情況仍然可能波及其它上層服務。

從小白到架構師的成長之路:走向微服務

是以,有必要在服務之間設定保護機制,防止小故障的影響不斷擴大,最終造成大面積的雪崩。常用的保護機制有三種:

熔斷:當某個服務或節點的調用失敗數或調用耗時超過門檻值時,調用方應停止繼續調用此節點并快速傳回失敗。防止自身請求大量堆積,整條鍊路浪費大量資源等待下遊響應。

從小白到架構師的成長之路:走向微服務

降級:當下遊服務停止工作後,如果該服務并非核心業務,則上遊服務應該降級,以保證核心業務不中斷。

限流:當服務提供方的負載接近最大處理能力時可以丢棄請求并立即傳回失敗,防止大量堆積的請求将自身壓垮。或者當某個上層服務的調用量過大時丢棄它的(部分)請求,避免自身崩潰影響其他上層服務。

從小白到架構師的成長之路:走向微服務

鍊路追蹤

一個請求經過了好幾個子產品,出了 BUG 找半天都查不清是哪個子產品的故障

要想查清故障原因就需要記錄一個請求在系統中經過了哪些子產品,進而找到故障子產品的相關日志,這種在服務系統中追蹤調用關系的技術稱為鍊路追蹤。

鍊路追蹤的原理是在請求進入系統時為它配置設定一個唯一的 traceID (通常使用雪花算法生成), 這個請求調用過程中産生的所有日志資料都要帶上這個 traceID 并上報到統一的日志資料庫。事後分析時隻要使用 traceID 進行查詢就可以找到相關日志了。

從小白到架構師的成長之路:走向微服務

比較出名的鍊路系統是 Google 的 Dapper,有興趣的朋友可以點連結看一下詳細的資料,這裡就不展開讨論了。

分布式事務

會員那邊不靠譜,付款失敗還照樣發會員,損失好多錢

付款和變更訂單狀态兩個操作應該是原子的,要麼都執行要麼都不執行,不應該出現一個執行另一個不執行的情況。這是典型的資料庫事務問題,在我們将資料庫拆分之前這個問題可以交給資料庫的事務機制解決,但是資料庫拆分之後一個事務涉及到多個資料庫執行個體甚至是異構的資料庫,這就需要一些分布式事務協調元件來處理了。

常見的分布式事務實作方案有 TCC(try-confirm-catch)事務、MQ 事務消息、Saga 事務等。分布式事務主要有兩種實作思路,第一種的典型代表是 TCC 事務,TCC 事務分為三個階段:

  1. Try 階段: 事務協調器要求參與方預留并鎖定事務所需資源;
  2. Confirm 階段: 若所有參與方都表示資源充足可以送出,事務協調器會向所有參與方發出 Confirm 指令,要求實際執行事務。
  3. Cancel 階段: 若 Try 或 Confirm 階段任一參與者表示無法繼續事務協調器會向所有參與方發出 Cancel 指令解鎖預留資源并復原事務。
從小白到架構師的成長之路:走向微服務
從小白到架構師的成長之路:走向微服務

第二種實作思路的典型代表是 Saga 事務,Saga 事務将一個大事務拆分成多個有序的子事務并且每個子事務都準備了撤銷操作,事務協調器會順序的執行子事務,如果某個步驟失敗,則根據相反順序一次執行一次撤銷操作。

上面我們隻簡單介紹了分布式事務保證原子性的機制,在實際實作中還要考慮分布式事務的一緻性(強一緻還是最終一緻)、隔離性(Saga 事務會暴露事務執行到一半時的狀态)、對業務的侵入性、并發量等各種問題,簡言之分布式事務是一種非常複雜、成本很高的技術。

由于分布式事務的高成本,在在實際開發中經常使用「對賬」的方式來保證多子產品事務的最終一緻性,即用離線任務定時掃描資料庫找出未正确處理的事務,然後按照預定政策進行補償(比如撤銷未成功付款使用者的會員身份)或者要求人工介入修複。

微服務時代的基礎設施

容器化和 Kubernetes

淘金網從最開始便是直接部署在雲伺服器上的,到了後來做了服務拆分也依舊沒有改變。每次擴容都要等待新的雲伺服器慢慢啟動、跑腳本裝環境最後拉起服務程序,一等就是半天。需要更新 JRE 的時候還要寫腳本一台一台連上去進行更新,有時候還會更新失敗需要人工介入進行處理。 還有些時候為了充分利用資源會在一台雲伺服器上部署好幾個服務,這些混部的機器管理起來也是各種麻煩。

這一切都讓程式員們苦不堪言,于是小明又開始了調研,這時一種叫「容器化」的新技術吸引他的視線。

我們都知道計算機可以分為三層:硬體、作業系統和應用程式。所謂的雲伺服器本質上是虛拟機,虛拟機可以模拟硬體的接口,這樣做最大的好處是可以在虛拟機上運作與主控端不同的作業系統程式,比如我們可以 Windows 系統上運作 Linux 虛拟機。但是作業系統核心的計算量十分龐大,又在軟體模拟出的硬體上運作,其性能可想而知。

從小白到架構師的成長之路:走向微服務

對于雲伺服器而言并不需要再運作一個作業系統核心,雲伺服器隻是需要獨立的目錄樹、程序空間、協定棧就可以了,就是說即使雲伺服器 A 和 B 運作在同一台的主控端上 A 的根目錄和 B 根目錄是獨立的。

容器化技術的實質是模拟作業系統核心,實際上運作的隻有主控端一個作業系統核心,但是主控端上的每個容器都認為自己擁有一個獨立的作業系統核心。

從小白到架構師的成長之路:走向微服務

由于容器中的“作業系統”是主控端虛拟出來的,并不會消耗太多資源。是以容器的運作速度與直接在主控端上運作幾乎無異,而且少了虛拟機中作業系統的開銷幾乎所有的資源都用于業務代碼極大的降低了成本。

Docker 是目前容器技術的事實标準,它使用使用 Linux Namespaces 技術隔離目錄樹、程序空間、協定棧等,使容器之間互不影響;使用 cgroups 機制分隔主控端的 CPU、記憶體等資源。

Docker 的另一個重要貢獻是定義了容器鏡像的标準。 Docker 鏡像使用分層檔案系統 AUFS。每層資料一旦送出便不可改變,隻能添加一個新層将其覆寫。

Docker 鏡像的不可變性保證了運作環境的一緻,免除了登入雲伺服器裝環境的痛苦,一緻的運作環境也減少了「測試環境是好的,怎麼一上正式環境就出問題了」的發生。 分層檔案系統使得每次打包 Docker 鏡像隻需要更新業務二進制,比虛拟機鏡像小很多。Docker 允許将任何現有容器作為基礎鏡像來使用,極大的友善了重用。

Docker 隻提供了單機上的容器化支援,而我們的生産環境是由很多伺服器組成的,有些子產品負載不足需要橫向擴容多加幾個容器,有些主控端會當機需要将上面運作的容器換台主控端重新開機。

解決這個問題的是大名鼎鼎的 Kubernetes, Kubernetes 不僅可以完成服務編排的工作,而且定義了描述叢集架構的規範。我們通過編寫 yml 配置檔案描述叢集的最終狀态,Kubernetes 可以将系統自動達到并維持在這個狀态。這種能力将人力從繁重的運維工作中解脫出來,實作了友善可靠的部署和擴縮容。

Service Mesh

由于微服務需要提供服務發現、熔斷限流等服務自治能力,是以微服務架構所要提供的功能比傳統的 Web 架構多很多。看到現在仍不少見的 Centos7、Java 5、Struts2 等各種老舊的基礎設施就可以想象更新基礎架構是一件多麼痛苦的事情。

為了解決基礎架構組和業務組之間為了更新架構帶來的瘋狂扯皮,小明找到了一個新的思路。這種方式稱為 Service Mesh,它将服務發現、認證授權、調用追蹤等服務治理所需的能力放到一個被稱為 SideCar 的代理元件中,所有出站入站的流量都通過 SideCar 進行處理和轉發, 業務方隻需要和 SideCar 進行簡單标準的 RPC 調用,完全不需要考慮那些雜亂的事務。

從小白到架構師的成長之路:走向微服務

Service Mesh 中還有個被稱為控制面(Control Panel) 的元件來統一管理所有 Sidecar 的配置,SideCar 和業務組成的部分稱為資料面(Data Panel), 控制面和資料面共同組成了 Service Mesh 架構。

從小白到架構師的成長之路:走向微服務

因為 Side Car 和業務隻通過 RPC 進行通信,兩者可以獨立更新,免去了更新基礎架構時需要改動業務代碼的種種麻煩。由于 RPC 調用天生可以跨語言,是以隻需要開發一次 SideCar 就可以對接多種語言開發的業務系統。

從小白到架構師的成長之路:走向微服務
圖檔來源: Pattern: Service Mesh

ServiceMesh 直譯是服務網格,大概是因為架構圖比較像網格才起了這個名字吧~

總結

何謂微服務

又是一年年終季,小明看着自己搞的這麼多東西打算整個 PPT 去行業交流會上吹吹牛。他左翻右找,終于找到了一篇論文:Microservices: a-definition-of-this-new-architectural-term, 原來自己做的這套結構有一個好聽的名字:「微服務」:

微服務是一種通過多個小型服務組合來建構單個應用的架構風格,這些服務圍繞業務能力而非特定的技術标準來建構。各個服務可以采用不同的程式設計語言,不同的資料存儲技術,運作在不同的程序之中。服務采取輕量級的通信機制和自動化的部署機制實作通信與運維。
從小白到架構師的成長之路:走向微服務

「什麼是微服務」這個問題是典型的一千個人眼裡有一千個哈姆雷特,不過回顧「淘金網」的曆程我們發現有一些理念已經是業界的共識:

  1. 按照業務闆塊将單體大服務拆分為多個獨立的小服務,通過分割解耦的方式控制代碼的複雜度。小服務的獨立性賦予了它更高的靈活性,比如采用異構技術和異構架構的自由。分散部署也有效的阻止了局部錯誤造成大範圍的故障。
  2. 資料去中心化: 各個服務獨立維護資料庫,降低子產品之間的耦合程度。
  3. 重視服務治理,但鼓勵各子產品自治。微服務不僅僅是将服務拆分,而且重視處理拆分後出現的一系列問題,比如控制流量路由的服務發現系統;避免連鎖故障的限流、熔斷、降級技術;用于調試和排查的鍊路追蹤系統;以及維護事務安全性的分布式事務機制。服務治理能力不是由中心或者基礎架構強加給各子產品的,而是各子產品根據自己的需要靈活選擇治理能力和實作方式。
  4. 重視彈性:服務的部署容量不是固定的,而是根據業務需求量随時增加或減少節點數,并且是以促進了 Kubernetes 等彈性平台的廣泛使用。
  5. 重視彈性:服務系統應該可以按照業務需要靈活的進行擴縮容。
原文連結:https://juejin.cn/post/7154563072797966366

繼續閱讀