天天看點

微服務思想篇

微服務思想篇

      • 1.什麼是微服務
      • 2.微服務的拆分
      • 3 API網關
      • 4.同步架構和異步架構
      • 5. 服務注冊中心
        • 5.1. 強一緻性
        • 5.2. 弱一緻性
      • 6.負載均衡,限流,熔斷
      • 7.一緻性
        • 7.1 幂等
        • 7.2分布式鎖
        • 7.3分布式事務
          • 7.3.1 剛性事務
          • 7.3.2 柔性事務
        • 7.4 CAP
      • 8. 資料通路層
      • 9. DB,Cache,存儲
        • 9.1. 分布式存儲
        • 9.2. 分布式資料庫
      • 10. 微服務的基礎設施
        • 10.1. 配置管理
        • 10.2 CICD
        • 10.3 監控系統
        • 10.4 日志系統
        • 10.5 鍊路追蹤
      • 11. service mesh
        • 11.1 微服務1.0痛點
        • 11.2 service mesh
      • 12. 秒殺,搜尋,推薦
      • 13. 收尾

1.什麼是微服務

想了很久,什麼才是微服務?找了一大圈,各種定義都有,書和搜尋好像沒能給出一個标準答案,那就從源頭抓起:

原文(點選原文檢視)中的第二段定義就是這一小段話:

In short, the microservice architectural style [1] is an approach to developing a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms, often an HTTP resource API. These services are built around business capabilities and independently deployable by fully automated deployment machinery. There is a bare minimum of centralized management of these services, which may be written in different programming languages and use different data storage technologies.
           

中文翻譯過來就是如下:

“微服務是一種架構模式,它提倡将單一應用程式劃分成一組小的服務,每個服務運作在其獨立的程序中,服務與服務間采用輕量級的通信機制互相協作(通常是基于HTTP協定的RESTful API)。每個服務都圍繞着具體業務進行建構,并且能夠被充分自動化的獨立部署。對這些服務有一個最低限度的集中式的管理,可以使用不同的開發語言進行開發和使用不同的資料存儲技術。”

有點抽象,這好像不是我要的答案,不好寫啊,因為它首先是一種架構模式,架構必定首先得設計,談論到設計,哪還有什麼标準,談論最多的是風格,理念,思想。是以微服務沒有标準,硬要說标準,以上這段抽象的原話就是标準,遵從了以上的原則,它就是微服務。

但是設計的産品總歸是要給人使用的,好用是第一需求,包含了快速穩定可擴充等等;好看也很重要,顔值高的東西誰都喜歡,誰都更喜歡用(就用秒殺服務,搜尋服務,推薦服務比喻好看吧),而用的人多了,好用和好看不停的循環促進。微服務架構也是如此,是以說微服務是圍繞着具體業務進行建構的,你的具體業務就是産品。

微服務從來就不是突然冒出來的,從軟體設計的MVC,到分布式,到SOA,到微服務到service mesh(微服務2.0),它就是不斷的為了滿足業務需求被設計和演化出來的。

從定義上看微服務就是實作分布式架構的一種方式,分布式系統是要滿足高并發需求的,而滿足高并發也可以前後端分離+nginx或者lvs,但這種粗粒度的擴張其實早已滿足不了現代需求,高并發隻是滿足微服務的最低需求,篇幅有限,詳細的不讨論,個人認為現代微服務更多的需求是快和效率。

是以微服務架構本質上它就是建構你的具體業務架構,好了,廢話還是少點,想一篇文章徹底搞定微服務是不現實的,這篇既不是純理論(理論書籍可以檢視《領域驅動設計》,《微服務設計》兩本書),也非純代碼(github上有大量的java和go語言開源代碼)文章,也許隻是作為一個從業技術人員分享一點淺薄知識的水文,如果能留下一點思想那就更好了,直入主題,定義中第一步該做的事情就是劃分。

2.微服務的拆分

好了,該如何劃分或者拆分,新的業務就是劃分,現有業務就是拆分。

那麼問題來了,該如何拆?

書中專家告訴我們要劃分“領域”,大緻意思是:把領域劃分成一個個子領域,按“限界上下文”尋找邊界,按邊界拆。(唉!專家有時候就是這麼不接地氣)

《領域驅動設計》中是這樣的:

微服務思想篇

網絡上找到的各種都有,有這樣的:

微服務思想篇

或者這樣(如果隻是簡單這樣,那肯定是錯的,為什麼錯,看看互相調用REST紅箭頭,下文會講到):

微服務思想篇

istio(service mesh)官方文檔上是這樣的(官方文檔可能一般大多都是專家寫的吧)。service mesh也被定義為微服務2.0

微服務思想篇
微服務思想篇

是不是有點懵,個人覺得其實他們都對,隻是把細節給隐去了而已,是以PPT上的東西看看就好。

其實對于經曆過mvc,soa演變過程的人來說,好像都不是事!

mvc(module,view,control)本質上就是把代碼給一層層松耦合的方式給分開(邏輯上的水準拆分),soa(面向服務程式設計),就是把一個個服務(例如:使用者,訂單,庫存,支付)這些服務松耦合的方式給拆開(邏輯上的垂直拆分),那微服務怎麼拆,2種思想結合,把系統水準拆分和垂直拆分成一個個小的服務,直接畫張圖吧

微服務思想篇

畫圖水準就這樣了。。。将就着吧,典型的電商網際網路簡約架構。其實大公司在前端和API網關層之間一般還有負載均衡層(DNS,F5,LVS)和Nginx反向代理層。

水準拆分和垂直拆分,貌似垂直拆分看着更簡單,其實垂直拆分更難,甚至難度不是一個數量級的,下文會慢慢延伸到,垂直拆分将會帶來的問題以及怎麼去解決,特别是資料處理層面。圖上API網關層及以上不涉及到拆分。API網關層随後會講到,DB層用的資料庫也不必糾結,随便寫了幾種DB,先看拆分問題

前後端分離的優點好處不用多說,但也不是絕對,比如網站排名seo,前端也是塊超大的内容,水準有限,少BB_。

回歸主題,API網關層是整個後端的出入口,請求從上往下調用,再從下往上傳回,通常在上層的叫上遊,下層的叫下遊。

同層之間不要互相調用,比如業務邏輯層中使用者服務和商品服務互相調用,如果業務非得需要調用,那就調用對方服務的下遊,比如使用者服務調用商品資料通路服務。至于為什麼,從網絡角度上講就叫不能有環路,廣播風暴去了解下,嚴重了直接引起系統雪崩。但是這樣又會引起代碼的重複,比如使用者服務中需要實作商品服務的部分功能,如果是調用多了,其實這就是服務邊界自然而然的産生了,那就在業務邏輯層中再下沉一層基礎服務。

好像終于要開始邊界問題了,那就直接拿支付清算業務聊聊吧。

支付清算系統該如何劃分業務,例如:給你如下選擇:

  1. 按借記,貸記業務拆分
  2. 按業務種類拆分,人民币業務,外币業務
  3. 按交易碼類型拆分,資金業務,清算業務,賬戶服務,查詢服務,對賬服務等
  4. 按票據種類拆分,比如:支票業務,本票業務,彙票業務,轉賬業務,代扣業務,定期業務等等。。每種票據一個服務

很多人都有着理想中的完美領域劃分,劃分完美了一勞永逸。其實我覺得以上拆分都對,隻是對應業務發展的不同階段而已,一個成熟的業務其實你自然而然的知道邊界在哪裡,一個新業務或者不熟悉的業務讨論再多的邊界都是多餘(但認識邊界還是需要的),是以說,架構要圍繞業務建構,沒有設計就是最好的設計你聽聽就好,過度的設計肯定也走不遠,你将會一直在不停的重構。就好比計劃經濟和市場經濟一樣,這個過程中,設計和演化是相輔相成的,強行的鑽牛角尖,現實終将狠狠的打你的臉。最終你也許會發現微服務架構就是一個追求平衡的調節器。

微服務思想篇

上圖中基礎業務邏輯層就是下沉的一層基礎服務,去解決同層之間互相調用的問題,也是處理邊界的一種方法,其中。。。省略了很多業務,比如:國庫業務,非稅業務,公積金業務等等,詳細的業務不再讨論,金融類業務相對比較難了解,那就将就着吧,我相信還是有很多人能看懂的,再舉個邊界的例子,比如上圖中如果對公轉賬個人,和個人轉賬對公業務如何處理邊界,那就在業務邏輯層上遊再上浮一層聚合層吧,比如就叫人民币業務,就不再畫圖了(其實很多API網關層含有聚合服務的功能,我個人覺得并不優雅,某些業務場景甚至帶來嚴重問題,寫到網關層再說吧)。有句話叫:沒有是再增加一層解決不了的問題,如果有,那就再增加一層。想想好像也挺有道理的。網上有很多關于網際網路大公司的中台的概念,比如使用者中台,資料中台等等,但仔細想想中台的定義,你會明白它到底在哪一層。

好了,劃分問題差不多了,有關更專業的理論,感興趣的還是去看書吧,《領域驅動設計》和《微服務設計》還是很不錯的,其實以上篇幅關于劃分問題也遠沒這麼簡單,但這樣的劃分和處理邊界在工程實踐中能解決絕大部分問題。而更難的是你的組織結構是否适合這樣的架構設計,組織合适的人去完美協調的處理一件大事情本身就是困難的事,這需要管理層,上司層的管理能力和大智慧,不是麼?很多時候架構設計強行為了迎合組織架構而調整,個人認為并不可取,可行性的業務戰略和業務演化才是正确的方向。 想想淘寶以前的支付業務和現在的支付寶,以前的淘寶二手業務和現在的閑魚,京東的物流,騰訊的遊戲,比比皆是,不瞎扯了,有點跑題了。

好像漏了微服務中的“微”,有人把微服務定義為很小,單個服務高内聚,2周内可以重構一個微服務;也有人認為微服務它就是個服務,一個服務就是微服務;這種概念強行定義的标準看看就好,《微服務設計》和《cloud native go》這2本書中有關于“微”的不同角度的詳細的描述,而根本問題在于根據你組織結構和業務架構劃分合适大小就好,和不同的階段選擇适合自己的才是最好的。

總之,如何劃分或者拆分,大方向上的思想就是水準拆分和垂直拆分,在處理大規模增長上,這種思想比比皆是,不管是微觀還是宏觀上;比如資料結構中的hashmap,數組就是水準增長,連結清單垂直增加, 又比如資料庫的分庫分表(資料通路層再說),網絡上的OSI7層模型,再一個個連接配接起來形成網絡,雲計算的彈性排程(根據監控設定的門檻值名額自動伸縮)也是如此,大到公司組織架構(想想那些大公司的組織架構的頻繁變動),甚至一個國家的政府部門架構(想想國地稅的合并,銀保監的合并不也是在處理邊界問題麼)。

為何好像很多廢話講拆分,因為拆分真的很重要,微服務定義中第一步講的就是拆分,其實微服務拆分後帶來的一系列問題,這些問題有:

  • 網絡調用問題
  • 服務管理問題
  • 配置管理問題
  • 資料一緻性問題
  • 業務上線更新問題
  • 業務和故障排查問題
  • 性能監控問題

如何解決這些問題?那就一個個來。

3 API網關

網上關于api網關層寫的我覺得挺複雜的,對于不是網際網路應用的人不是很好了解,看到網關2字,搞網絡的可能相對更好了解,你就參照你家裡的路由器去了解,API網關層就是幹的路由器類似的活。

  • 首先它就是外部和内部連接配接的一道關口,路由器控制的是網絡,而API網關控制的是接口API。
  • 路由器要分發路由,而API網關也要路由前端API請求到對應的後端服務上。
  • 連接配接無線路由器需要賬戶密碼,API網關需要處理session和JWT認證。
  • 有些路由器還要處理一部分的安全(比如防dos攻擊),API網關同樣需要控制大規模請求(特别是"秒殺"業務的時候,後面會講到"秒殺"架構,很有意思)。
  • 都需要處了解包,一般現在網際網路的API網關開源架構大都是基于HTTP的RESTFUL架構,處理前端發送的json請求,是以還得處了解包和跨域問題。
  • 路由器還得管理和限制一部分的裝置,API網關管理一部分的後端服務提供給前端調用。

是以API網關很忙,壓力很大,結果你還要讓它管理業務邏輯的聚合,是以你覺得合不合适,是以正确的姿勢是把服務聚合給下沉到業務邏輯層。

個人覺得對于某些傳統業務模型,網關當然也并非需要,或者把服務的聚合做在API網關也沒關系,當然并非所有傳統業務都是如此,特别是面對C端的,比如12306,是以微服務讨論你是傳統公司和網際網路公司沒有意義,讨論它的業務場景更有價值。

常見的開源API網關架構:

大名頂頂的spring cloud zuul ,但zuul2好像閉源了,此外還有nginx+lua實作的kong,國人開源的orange,悟空api,golang寫的tyk,而比較大的網際網路公司API網關層都是根據自己的業務場景自己實作的,核心功能就是實作動态路由功能,代碼實作就是用動态代理和反射去實作路由。從架構層面了解就是web架構的靠前的一部分功能和動态代理。

說到RESTFUL ,那就連跨網絡遠端通訊一起,回到開頭在看下微服務中定義的服務間的通訊機制,通常是用RESTFUL,那常用的通訊機制有哪幾種,目前有3種,如下:

  1. RESTFUL --> 面向資源
  2. RPC --> 面向方法
  3. MQ --> 面向消息

而輕量級的基本可以排除MQ,MQ的主要作用不在于服務間的調用,但它的作用可大了,也很有意思,稍後會有很多地方講到MQ。RESTFUL它就是一種提供了原則和限制的風格,基于http協定用URL去定位資源。

其實很多架構和産品都說基于RESTFUL,但100%完全遵守RESTFUL風格的很少,就連K8S都不完全是(當然,人家提供的api何其多,100%也不可能),其實簡單了解RESTFUL不難,看它URL中是不是隻有名詞,用HTTP動詞(GET,POST,PUT,DELETE)描述操作。而K8S中的聲明式是什麼呢?也是基于RESTFUL風格,其實我個人了解一句話概括就可以了:讓你有種當老闆(上司)的感覺。,一般大boss都不會和你瞎bb,比如人家隻會和你說我下午有個會,他不會告訴你怎麼安排會議室,要做些什麼準備,又比如大老闆今年定個小目标,先掙個一個億,他不會去問今年已經掙了多少,再去掙多少,甚至怎麼掙。RESTFUL是好東西,比如簡單,輕量,基于HTTP等等,既然是http,給多語言開發提供了基礎。http也是未來的方向,但是它目前缺點也很明顯,比如:并發相對RPC來說要低,延遲高,有人拿http和rpc做過測試,通常并發量和延遲要相差一倍,而http用于工程實踐來說,還需要用web架構,啟動慢。這對于某些業務場景下是不可接受的,比如:網際網路大公司,人家的服務規模很大,調用鍊很長,并發低和延遲高是不可接受的,又比如金融業務,并發量稍低也許可以接受,但延遲高是不行的。

RPC種類很多,比如jsonrpc,通過json資料格式傳輸,雖然json是各種語言都支援的标準格式,但目前基本也被抛棄,原因就在于json序列化性能低,而高性能的rpc的必要條件就在于資料序列化方式都是二進制的,通常都是采用protobuff二進制協定。性能好的代價,帶來的問題就是耦合度相對比較高,重用戶端,實作不同語言的通訊很難,而要實作不同語言的用戶端和維護,這代價太大。是以就有了grpc,基于http2,protobuff二進制協定,netty通訊架構,把定義好的proto檔案生成各個語言的用戶端提供不同語言間的通訊,它最新的v3版本性能幾乎可以追平tcp方式的rpc。有興趣的可以網上找找grpc和dubbo rpc性能對比等等,而具體的使用方式檢視github上grpc的example檔案夾下的例子。

是以采用微服務的一般都是前後端通訊用RESTFUL,過了API網關層,絕大部分都是RPC調用。

接下來一個問題,什麼是同步架構,什麼是異步架構?

4.同步架構和異步架構

至于同步和異步這裡不解釋了,也不去區分BIO和AIO,越到底層越複雜,異步了解起來不難,實作可不簡單,可以看看大名鼎鼎netty nio通訊架構,目前幾乎大部分的上層應用架構通訊層面都是使用它的。

先來看一個問題,一個請求分2種,一個請求讀,一個請求寫,哪個需要異步處理的,請求讀用異步是沒有意義的,不了解自己想想,至于請求寫,在哪些業務場景下用到異步架構?給你列舉幾個,比如買個東西,點個外賣等寫個評價,發個朋友圈,微網誌,短視訊等等,那怎麼才能架設異步架構?MQ來了。

先來看下MQ主要的好處:

  1. 異步通訊
  2. 應用解耦
  3. 削峰填谷
  4. 消息轉存

其中,消息的存儲能力,消息丢失機率,處理能力,堆積能力,能否順序消費,支不支援事務,批量消息是否支援,推拉模式,可用性和擴充性,一緻性處理等等問題都是MQ需要處理的問題。

而MQ的選擇,其它的能力這裡先不談,等到資料一緻性處理的時候再說吧,這裡先說異步問題,MQ異步問題本質上就是一次RPC調用變成兩次RPC調用,中間資料轉存了下。

回看下上面畫的圖,那麼微服務異步架構該把MQ放在哪一層?

其實放在任何一層都可以把同步架構變成異步架構,但通常的做法都是放在API網關層和業務邏輯層之間,為何放在這一層,下面的秒殺架構再繼續,不然需要直接引入很多知識太複雜。再順便抛出個MQ應用場景,想想你的災備機房,以及異地多活_,那就再抛一個較簡單問題,為什麼MQ的消息處理能力這麼強,資料庫的處理能力為什麼相對這麼弱雞,都是比較有意思的問題,想想他們的應用場景,好好去了解下随機讀寫和順序讀寫,本質問題其實并不難。而我們有時候需要的是停下來思考,問問為什麼?

說到同步異步問題,順便聊聊支付上的實時交易和普通交易,這個問題曾經一度讓我覺得困惑。

首先,什麼才是實時交易,什麼才是普通交易,後來我想明白了,其實從技術角度上很難定義,它本身就是個業務問題,我轉賬到對方賬戶的錢“馬上”就能到賬,或者從其它銀行賬戶“立刻”取錢到現在的賬戶,它就是實時交易。而普通交易就是錢到帳的生效時間延後,一般至少需要隔日。而如果你要從技術角度上去嚴格區分實時交易很難定義,一般能想到的同步的就是實時交易,異步的就是普通交易,可這樣真的準确麼?(這裡先不談安全性問題)例如:銀行本票,所謂本票,是要見票即付的,可從技術角度,本票的業務流程真的必須是同步的麼?而有時普通的交易為什麼也能很快到賬呢?它不應該是異步的麼?是以,實時交易和普通交易,它就是業務角度問題,與同步異步技術關系不大,在整個交易的局部範圍内它可以是實時的,比如實時清算,比如高頻交易,但從整個業務角度,實作實時交易的根本原因在于第三方的信用背書。本不想談業務的,說這麼多其實也隻是想說明,技術不能脫離業務,微服務架構需要圍繞業務建設。

5. 服務注冊中心

其實業務邏輯沒啥好說的,微服務劃分中,主要講的都是業務邏輯和邊界,而服務多了後,怎麼管理?其實大家都知道是注冊中心,可我要說的是你真的了解注冊中心麼?市面上有多種相似功能的産品出現時,人們往往希望知道這些産品相比較的優劣,包含了哪些功能,往往停留在表面的功能對比上,對架構或者性能并沒有非常深入的探讨。

先來看看業界主流的注冊中心對比:

功能 Eureka consul Zookeeper etcd
健康檢查 Client Beat TCP/HTTP/gRPC Keep Alive HTTP/gRPC
監聽支援 支援 支援 支援 支援
負載均衡 Ribbon Fabio
CAP AP CP+AP CP CP
一緻性協定 定制p2p raft zab raft
用戶端接口 java/http 多語言client/http/dns 多語言client 多語言用戶端/http/grpc
多資料中心 不支援 支援 不支援 不支援
。。。

以上4種可能是相對比較常用,社群較好的注冊中心,拿來做的對比,如果是你,你怎麼選擇?真的看哪個支援的功能多?哪個用的人多?其實最重要的是根據你自己的業務需求選擇,首要關注的是CAP,CAP定理可以說是整個分布式理論的靈魂。其實大部分人可能都知道CAP,但看過知道後很可能也就忽略了,那就再簡單闡述下:C:Consistency 一緻性,A:Availability 可用性,P:Partition tolerance分區容忍性,3個要素中隻能選擇2個,稍微認識的可能會知道分布式系統要二選一,首先分布式系統肯定要滿足P,因為跨網絡通訊了,分區容忍性必須滿足,其它隻能2選1, 隻能選擇 CP(一緻性 + 分區容忍性) 或者 AP (可用性 + 分區容忍性)架構,在一緻性和可用性做折中選擇。

先來看幾個問題?為什麼dubbo微服務架構要用zk做注冊中心?,k8s要用etcd做注冊中心?,為什麼有些人說eureka性能最好最快?,為什麼zk,eureka,etcd等不能多機房部署?,為何zk,etcd注冊中心服務規模上去後可用性将大大降低,甚至不可用?這些問題在你了解CAP定理後,甚至你能了解所有的分布式系統,包括分布式MQ,分布式緩存,分布式資料庫,分布式存儲。

先來看看服務注冊和發現的場景,不管是服務注冊還是服務發現,最終都是要處理一緻性的問題,隻是過程到底是要強一緻性,還是弱一緻性。

5.1. 強一緻性

例如:在頻繁的服務注冊和服務下線的過程中,各中心節點先處理各服務的一緻性再傳回給調用方結果,貌似完美,用戶端當場得到正确的節點服務資訊。但問題出在中心節點在處理一緻性的過程中,發生網絡分區了,不管是重新再選舉主節點,還是裂腦徹底挂了,為了保證強一緻性,整個服務注冊叢集在這過程不可用。這可不太好玩,不是麼?

5.2. 弱一緻性

在服務注冊和服務下線的過程中,各個用戶端向注冊中心請求時,都是注冊中心立刻給最新的節點服務資訊,不管這個服務資訊是不是最新的,如果是最新的,最好,一切正常,如果不是,那就照常調用,比如有的服務下線了,調用失敗,那就用戶端重試或者剔除,最終節點服務資訊都将更新到正确狀态。對業務影響的情況有限,頂多造成多調用幾次的資源消耗。

是以服務注冊中心也得看業務場景,我到底是強一緻性重要,還是可用性重要,很明顯,業務可用性相對更重要一點,特别是對于大型的網際網路公司而言,那絕對必須是AP模型。而CP模型也在不斷的改善,比如在服務的用戶端中緩存了節點服務資訊,一旦注冊中心不可用,整個服務調用過程還是可用狀态,隻是暫時不能服務注冊和下線了。CP模型用在比如支付等業務場景下,可能是更好的選擇。

其實從規模上說,對于服務數量少于1k的情況下,哪種注冊中心都不會有問題,但是服務數量達到一定數量級後,比如上W級别的服務,這種情況下CP模型的注冊中心可能都不滿足了,直接不能用了,原因就在于一緻性的處理過程,不停的處理心跳,資料的一緻性保證,選主節點的過程,master節點性能是有限度的。特别是如果中心節點還跨機房的情況下。目前強一緻性協定主要有paxos和raft,zk注冊中心就是用的paxos的簡化版zab,consul和etcd都是用的raft,有興趣的可以看看raft協定,網上有一個raft動畫,很不錯,送上連結,paxos協定相對較早,而且了解難度較大,現在的大部分用raft協定。

好了,現在我相信大部分能了解剛才抛出的幾個問題了,其中還有幾個細節順便說下

  1. zk本身就不是為注冊中心而設計的,原生的用戶端甚至沒緩存,這樣是很危險的。
  2. consul到底是什麼模型,各個說法都有,很有意思,其實它是CP模型,為了滿足多資料中心支援,用了gossip協定做了AP,這個協定很有意思,看看詞意有大緻有了概念,是個最終一緻性協定,可以用這個協定來做AP模型的注冊中心應該也是不錯的選擇。
  3. k8s中用了etcd,可用于服務注冊中心,可相對較原生,要滿足基于業務的服務注冊中心的需求,需要很多基于etcd的定制化開發,成本較大。
  4. eureka是AP模型,是sping cloud OSS套件的核心元件,貌似不錯,但在多資料中心部署上效果并非很好,原因在于性能問題,而且關鍵的是它閉源了,包括API網關zuul2,熔斷器hystrix都閉源了,呵呵,一個閉源的産品,不想多說。
  5. 想想更複雜的分布式資料庫和分布式存儲的需求是什麼,他們是什麼模型應該很好知道了,他們是怎麼實作的?為什麼說最大支援多少個節點?CAP定理給你很好的答案了。到存儲層再接着說。

其實注冊中心還有另外3個選擇,coreDNS,nacos,pilot;coreDNS目前可以在K8S中看到,service就是通過dns找到對應的服務,但它太簡單了,不太适合注冊中心;pilot是istio的注冊中心。nacos不錯,同時支援AP和CP模型,阿裡開源的注冊中心和配置中心,因spring cloud的3大核心元件閉源,nacos可能是spring cloud使用者和dubbo使用者的最佳選擇,而且提供K8S和istio的支援,這點很關鍵,這個是未來,目前隻支援java,缺點是目前暫時缺少其它語言用戶端的支援,比如GO,這點對我來說太遺憾了,保留關注吧。

6.負載均衡,限流,熔斷

負載均衡其實沒什麼好說的,nginx,lvs了解的了解起來一點都不難,有興趣的可以去了解他們的算法,rr,wrr,hash等。從語言角度來說,用interface去直接簡單實作個也不難,但這裡有個有意思的問題,直連的負載均衡和中間代理的負載均衡如何選擇,比較有意思的就是你的微服務如果上K8S(這也是可能必須将走的路),負載均衡如何處理,K8S中的service也是有lvs和iptables負載均衡的,是以spring cloud要和k8s很好的融合是困難的。

限流和熔斷這2者,主要目的其實都是為了快速失敗,防止整個系統大量阻塞造成雪崩,但應用場景不一樣,限流主要用于API網關層,在API網關層其實還維護着一個請求隊列,當請求隊列排滿的時候,多餘的請求直接快速失敗傳回null,由前端去處理,其實這就是為了滿足大并發的需求,想想你雙11搶購,12306搶票,春節搶紅包的時候,你的請求真的都是打到後端處理的麼?這是實作秒殺架構的最重要的根本。難點是維護這個請求隊列到底需要多大,小了體驗不好,大了系統可能直接雪崩了,這個隻能根據實際業務情況慢慢調整。 那熔斷是什麼,先來一個問題,服務A,服務B,注冊中心C,服務A和服務B到注冊中心C,都沒問題,我能斷定A調用B,就沒問題? 不管是agent的探針檢測到服務的IP,端口都沒問題就肯定是正常的?試問隻要是程式,哪有沒bug的,死循環,記憶體溢出等問題不是能通過IP和端口就能确定異常的,熔斷就是處理這類異常的,它會統計每隔一段時間,服務的上遊調用下遊的失敗情況,如果超過一個門檻值,就認為這個服務是異常的,那就熔斷,讓上遊關閉該下遊的服務,簡單了解就是和家裡電源斷路器一樣一樣的,熔斷粒度分别有節點,服務和方法級别,并且需要手動開關。熔斷器的實作可以看看hystrix實作方法和思想。

再想想負載均衡和熔斷會帶來什麼問題?負載均衡在網絡不穩,斷斷續續的情況下會碰到什麼問題?熔斷服務後會帶來什麼問題?問題就在如何處理一緻性問題

7.一緻性

在說資料通路層及DB之前,先來聊一緻性的問題,這個問題可以說是整個微服務的核心必要問題,在單機時代,通常都是通過資料庫ACID保證資料的一緻性,但微服務中,因為跨網絡,資料被分散了,如何保證資料的一緻性是個最大的難點,特别涉及到重要資料問題,比如錢。

先來一個網絡問題,一個網絡通訊,從發起方來講,能得到幾種結果?答案是2種,我收到應答了和我沒收到應答。從發送業務請求角度來說,會有幾種結果?我覺得是3種,1.成功,2.失敗,3.不确定。成功和失敗很好了解,都收到明确結果了,這個不确定狀态我們剔除業務定義的問題,還有什麼?逾時或者傳回的通訊錯。可以說大部分的一緻性問題都是由它引起的。有人說在一個局部内網環境下,網絡相對很穩定,不容易出錯,在傳統單機架構中,是不容易出錯,但在分布式系統中,每個微服務都是通過網絡通訊的,單次的網絡通訊比如成功機率為99%,1次業務調用比如10個服務,中間不出網絡問題的機率大約隻有90%,如果更多的服務呢?這還隻是1次調用,每天你有多少的請求?,道理和硬體的損壞機率一樣。是以怎麼辦?再發一遍,好了,第一個問題,因網絡延遲等原因,“再發一次”的場景不就是類似并發的問題麼,而有些并發操作就可能引起幂等問題。

7.1 幂等

這2個字不要害怕,真的不難,就一句話,一次或者多次相同的操作産生具有相同的副作用。再通俗點,就是任意次相同的操作産生的結果相同。這個操作分為請求讀和請求寫,對應的資料操作就是增删改查(CRUD),請求讀(查詢)天然幂等,新增不幂等,修改和删除看情況,比如修改賬戶餘額,“update account set banance=banance-100”,它就不是幂等,“update account set banance=banance-100 where banance=500”,它就是幂等,同理:我删除賬戶金額最高的一個就不幂等,我删除一個固定的賬戶它就是幂等的。在并發和分布式場景下,那如何讓它幂等?本質問題是讓這些多次執行不幂等的操作,我就讓它執行1次,多餘的次數抛棄,方法就是給它加把鎖,讓它串行去執行,隻能執行1次。這裡不要去陷入業務問題,業務允許的多次相同操作由業務去決定。

7.2分布式鎖

鎖分為樂觀鎖和悲觀鎖(抽象概念的鎖)。這裡就把互斥鎖就認為是悲觀鎖。

  1. 樂觀鎖
為什麼叫樂觀鎖,首先它樂觀的先認為沒有并發沖突問題,個人了解就是基于可變條件的修改,如果成功了,ok;不成功,那說明這個條件在這過程中變化了,那就再查詢下最新的這個條件再修改,循環執行到成功修改。CAS(compaie and swap)就是樂觀鎖,很多人知道elasticsearch搜尋引擎,但它首先是個分布式nosql資料庫,elasticsearch解決并發問題就是用的CAS樂觀鎖。CAS很好,但有2個問題:1. 适合讀多寫少的場景,如果寫很多,查詢的最新結果再更新一直不成功。2. ABA的問題;詳細的自己科普,簡單點說就是原來是A,但中間經過一些列變化又回到了A,這時候更新是可以成功的,但此A非彼A,這如果僅僅是數值場景沒問題,但如果比如是連結清單,棧的情況,那就不一樣了,他們上下是有關聯的,是以一般還要加個version狀态,version每次遞增。隻要version不一樣,雖然A一樣,也是不同的。
  1. 悲觀鎖

相反,它悲觀的認為所有的操作都會引起并發沖突問題,語言層面的mutex,作業系統,mysql的鎖這裡就不多讨論了,有興趣的自己去看看,比較有意思的給2個問題:1. 語言層面是有讀寫鎖mutex.rwlock,在并發場景下,預設的slice(list),map等資料結構都是不安全的,想想它的使用場景 2. mysql的鎖,哪種級别是在down機的情況下,資料還是安全的,為什麼?

這裡隻說在分布式場景下,怎麼才能保證互斥鎖。網上有人大量用redis的setnx做分布式鎖,但它有什麼問題?首先你得先解決redis單點問題,其次想想CAP定理,而redis叢集它就是個AP模型,你讓AP模型去解決互斥鎖這種CP需求?肯定不是個好的方案,況且redis鎖的續租怎麼處理呢?是以redis做分布式鎖不是最好的方案。那就找CP模型,而更合适的就是etcd。業務處理過程大緻如下:

  1. 業務申請鎖,調用api時提供key(全局唯一ID)和ttl過期時間
  2. etcd生成一個目前鎖的唯一憑證uuid,把key,ttl,uuid去寫入etcd
  3. 檢查etcd中有沒有這個key,沒有就寫入成功拿到了鎖,有就寫入失敗,拿鎖失敗了
  4. 拿到鎖,心跳線程維持一個比ttl小的時間,将key續租
  5. 調用如果正常結束,釋放鎖,清除key;如果異常,等待ttl時間過期,釋放資源

詳細可參考etcd分布式鎖demo,這裡還有個CP模型問題,如果etcd徹底挂了咋辦?沒辦法,切換到備的一套etcd叢集,2套etcd叢集整體看就是個AP,保證可用性,畢竟不用處理資料同步問題,對于使用K8S來說,etcd是重點,它是整個K8S的重點,api server,所有的操作幾乎都離不開它,從go語言層面看,etcd的源碼是值得一看的,不然K8S也不會用它。

分布式鎖的常用應用場景是什麼?

  1. 重複下單問題
  2. 業務接口幂等問題
  3. MQ發送消息和消費消息去重問題

7.3分布式事務

這又是個涵蓋範圍很大的問題。事務的具體定義和4大特性ACID,以及本地事務自己去科普吧,畢竟是說微服務的。由于網絡劃分問題,資料已經不是存儲在單一執行個體上了,本地事務已經失效,那隻能通過分布式事務去解決,分布式事務分為剛性事務和柔性事務2種:

7.3.1 剛性事務

它的特性就是保證強一緻性,對應于CAP定理的CP,它遵循XA規範标準,具體内容這裡不詳細叙述,剛性事務的應用場景不多,原因就在于它的缺點太多,典型的實作有二階段送出(2PC),首先需要有個主協調器去協調,進行準備投票階段,都沒問題進入下一階段,各節點執行事務操作,寫redo和undo日志,但并不送出事務,如果都執行成功,給協調器回報yes,再commit,成功就OK,失敗執行undo或者redo。 核心思想就是對每一個事務都采用先嘗試後送出的處理方式,處理後所有的讀操作都要能獲得最新的資料。缺點有:

  • 它同步阻塞的,資源鎖定時間較長
  • 要全局鎖,并發很低
  • 長事務的話更低
  • CP問題導緻不可用

mysql就有XA規範的二階段送出,官方給的資料是同等條件下和本地事務性能相差10倍,這可是不能接受的。3PC也是如此,不再叙述。有人認為像高頻交易,實時清算等場景下用XA,我個人保留觀點,10倍的性能差距我覺得是不可接受的,甯願用硬體提升,本地事務和軟體架構去處理該類問題。如果隻是10%的性能損失,那還可以接受。

其實哪怕支付場景,我也不覺得要用剛性事務,其中絕大部分的支付清算系統并非想象的需要強一緻性,當然實時清算的是強一緻性,但也隻是針對中心的局部問題,從整個支付過程,它也不是強一緻性的,畢竟還有用戶端的業務系統,你并不能保證用戶端的過程也是一個強一緻性的過程,而實時清算的系統為什麼都是貸記并且有業務時間?考慮的更多的是業務安全問題,但帶來的可能是體驗,業務擴充,效率等問題,好了,到此打住,不在繼續讨論下去了,個人認為在額度控制的基礎上保持近實時就可以。隻代表個人觀點_。

7.3.2 柔性事務

所謂柔性事務,它其實就是對強一緻性的妥協,做不到CP,那就隻能AP,再去想辦法做到一緻性,是以就有了在CAP定理之後的BASE理論。它就是在AP的基礎上去滿足一緻性要求。

BASE理論:

  • BA(Basically Available) 基本可用
  • S(Soft state) 柔性狀态
  • E(Eventual consistency) 最終一緻性

架構模型有長事務和短事務:

  • 長事務
  1. TCC
這個模型其實全都是由業務方實作的,每個都需要實作相應的try-confirm-cancel接口實作,
  1. try第一階段,嘗試執行任務,完成業務檢查,預留業務資源,也可了解為當機資源。
  2. confirm 執行真正的業務,不再嘗試了。
  3. cancel 釋放掉try階段當機的資源。
怎麼了解?主要在第一階段,比如A貸記轉賬B100元,那就先try階段,檢查各業務要素,沒問題就當機A賬戶100塊,然後就進行2或3,全部正常執行确認100塊扣除或者取消操作解除100塊的當機,這個操作過程要滿足幂等,保證資料一緻性。
相比XA剛性事務,好處是資源鎖的粒度變小了,不再是全局鎖定。不再有單點協調器,由業務方控制。但缺點也有,想想業務方要實作tcc每個階段的子接口,如果是長事務的話代碼量吓人,成本較大。
  1. SAGA
saga模型把一個長分布式事務拆分為多個本地事務,每個本地事務都有對應的确認和取消接口,其實和TCC類似,首先差別在于saga模型沒有try這個階段,那就帶來了什麼問題?隔離性的問題,ACID中的“I”沒了,這又回到并發的問題了,如何處理?那就在它的上遊業務層控制并發,怎麼控制?加鎖串行。saga适用于長事務,比較有意思的是任何一個本地子事務失敗了怎麼辦,這時候肯定是幂等操作補償,而它可以有2個方向去補償,向confirm方向去補償失敗的子事務,和調用cancel去復原成功的子事務,而具體用哪種取決于你的業務。
saga模型在微服務中是必然要去好好了解的模型,雖然它帶來了補償方案的複雜性,2種補償方式更是帶來了調用鍊追蹤的困難度。但這是解決長事務的關鍵,實際案例就比如下單整個過程,簡單的就扣減庫存,然後建立訂單;複雜了有積分,信用,紅包,會員,優惠券,物流這些都确認後才能下單,中間任何一個小的事務處理失敗,就進行接口幂等補償。
  • 短事務
  1. MQ
在短事務之前,首先要來看看MQ。首先來看看有哪些開源MQ?RocketMQ,ActiveMQ,ZeroMQ,kafka,RocketMQ,NSQ等等,常見的就有這麼多,但你分布式場景下不太合适用單點MQ吧,雖然有叢集方案,但性能,可用性等等考慮進去,可用的分布式MQ也就剩下NSQ,RocketMQ和kafka三種MQ了,NSQ很好,簡單,高性能,還是go語言寫的,個人很喜歡,但考慮到現實,它的功能是受限的,比如不支援事務,不支援順序等等讓它的使用場景受限,但用在普通大量日志傳輸中還是杠杠的。那還剩下kafka和RocketMQ,首先kafka的事務了解有限,業務場景大多應用在高吞吐量的日志傳輸上,其次kafka用到了zk,把各節點,生産者和消費者都注冊到zk中,雖然安裝配置不難,但它有點重了,zk是個CP模型,要是在處理事務的時候挂了,好像不太好,當然僅僅代表個人意見。是以還剩下RocketMQ(java太複雜龐大了,個人也不會,但最喜歡的java應用就包含RocketMQ和上面提到的ElasticSearch,又都支援GO用戶端),首先它是支援同步雙寫刷盤的,保證了消息不丢,而且官方給的性能損耗比異步刷盤降低10%,這是可以接受的,畢竟在重要業務場景下這很重要,還有它出名的原因在于支援事務,至于RocketMQ的架構,順序消費,推拉模式,重發機制,群組,廣播模式等概念還是去看看apache rocketMQ官方文檔吧,其實RocketMQ早期也是用的zk做注冊中心,後來重寫了AP模型的name server做為注冊中心,把各元件給注冊進去。
  1. 支援事務的MQ
RocketMQ是通過類似于prepare先發給broker一條half message,而這條half message暫時并不發送給消費端,執行完本地事務,再發送commit消息給broker确認,這時候broker才會把這條消息發送給消費端。最後如果發送commit失敗了,逾時時間過後,broker會主動到生産端查詢該業務情況,成功就再commit發送消費端,失敗就取消發送。RocketMQ通過雙11等這麼多年的洗禮和考驗,你還有何顧慮和害怕的。
RocketMQ事務的整個機制可是經典中的經典,詳細的思想還是值得好好看看的,但也不是沒有缺點,需要消息生産者提供消息回查接口,業務侵入還是挺大的。
  1. 本地事務+MQ
可能有些人覺得MQ不好用,丢資料等等,那可能不一定是MQ的問題,而是你姿勢不對,處理資料庫和MQ的一緻性問題其實也是個AP問題,和處理資料庫和redis的問題是類似的,本質上都和時序無關,因為它無法保證強一緻性,隻能去通過最終一緻性去解決,不了解去科普下。是以正确姿勢是處理最終一緻性問題,在本地資料庫中新增一張消息表,業務處理和生成的消息做為一個本地事務放入資料庫中,生産者從本地消息表拉取消息并發送MQ,消息表有狀态字段,發送成功,失敗,逾時等記錄狀态,再補發處理。
利用本地事務再發送MQ的方案具有通用型,也不需要回查接口,業務侵入性較小,大部分處理短事務都是采用這種方案。

不管用事務MQ還是本地事務消息發送MQ,在消費端都需要去處理幂等問題保持一緻性。

最後,分布式事務方案送上一張對比表:

對比 2PC 3PC TCC SAGA 事務MQ 本地消息+MQ
一緻性
複雜性
性能
維護成本

好了,一緻性問題到此要結束了。

7.4 CAP

到此再想想CAP定理,我覺得這張圖很好,就送上吧。

微服務思想篇

關于CAP更高層次的了解,我覺得可以看看這篇文章,超棒!點選檢視

8. 資料通路層

好了,處理微服務的一緻性是必要核心問題,處理分布式環境下資料一緻性從來都沒那麼簡單,不信就再接着來。嗯,資料通路層,這層是幹什麼的?它最主要的作用就是安全性和屏蔽上層的複雜性:

  1. 你不可能讓業務邏輯層直接寫sql調用DB,DB直接暴露給業務邏輯層會産生什麼後果你得想清楚,多了不說。一般重要的業務處理系統隻提供業務必要接口,一般是不提供批量接口,不提供删除接口。
  2. 什麼是上層複雜性?上層業務邏輯隻需要知道的是基本的CRUD資料,并不關心用的什麼資料庫,不需要知道是否有redis,不需要知道是否有用到MQ,不需要知道資料庫是否分片,隻關心根據提供的接口是否正确處理了資料。
  • 不關心用了什麼資料庫,詳細的不多說,ORM去了解下吧。
  • redis,上面提到了redis,天然适合讀多寫少的應用,這種場景不管傳統行業或者網際網路都不少,例如:賬戶庫,使用者庫,session庫等等,性能超強,減少直接請求資料庫的利器。
  • 上面柔性事務已講到MQ應用場景,這裡不再啰嗦。
  • 資料庫分片,嗯,這是個有意思的問題。

直接最主要的問題,資料庫分庫分表如何處理。先想一想,所有的表都能拆分麼?答案是理論上是的,到實際業務上,有些還真拆不了,一個個來。

都知道有垂直拆分和水準拆分,垂直拆分根據列拆分,水準拆分就是把多行的拆分,理論上都是可以拆的。

實際情況要看原表設計方案:

垂直拆分并非必須,如設計不合理的列給拆開,一些長的文本資訊等等,讓text内容和id去關聯。

水準拆分一般3種:

  1. 根據業務情況拆了,比如把交易成功的和交易失敗的給拆成2個表。
  2. 根據時間序列拆分,比如根據時間按天,小時等拆開。
  3. 取模拆分,根據唯一辨別,這個唯一辨別需要是數值,按需要分成的資料庫取模拆開,例如:把使用者表拆成4張表,user_id%4,讓使用者id分布在4張表上,這個一般應該都知道,資料結構中有很多這種,比如hashmap。

1和2的場景不再啰嗦,這裡主要說3,這裡這個唯一辨別如何處理,這裡也有幾種情況,資料庫自增主鍵ID,這個性能很好,但在實際業務場景中很少用自增ID做主鍵的,因為意義不大,一般都是用有意義的自定義id做主鍵,比如user表中的user_id或者手機号,或者身份證号做主鍵,那麼問題來了,如果按照user_id取模分表,而我生産業務中需要根據手機号處理業務,如何處理?比如你的使用者很多,好幾個億的使用者。掃描全部的使用者表?肯定是不行的,而這時候要做的是建立映射表,把手機号和user_id給映射起來,根據手機号去找user_id,再去處理業務,而映射表也是需要分庫分表的,那根據身份證号處理業務也需要映射,也要分庫分表,那更複雜的業務場景呢?

而好的業務處理系統,需要有一個全局唯一ID去處理業務,比如:訂單号,根據這個唯一訂單号去處理業務問題,而傳統的業務系統大多複雜就複雜在這個設計問題,并沒有一個唯一ID去處理業務,而需要根據訂單,金額,賬戶等等,各種索引群組合索引去處理各個業務需求,你去拆分看看。關鍵在于沒有一個全局唯一ID。

如何處理全局唯一ID?

  1. 類似于手機号,身份證号碼等提前設計方案。
  2. 分布式ID,類似于訂單編号生成,包括前面說到的分布式鎖中的key,分布式唯一ID生成方案最有名的就是snowflake,共64bit,由41bit的時間戳,10bit的機器id,12bit的序号id組合,整個ID趨勢遞增,能達到每秒26萬ID,以服務方式部署。具體的自己查去吧。但分布式ID保持時間的準确度很重要,不然時間差可能導緻重複ID。
是以支付系統能否也去解決這個問題呢?這裡隻從技術角度去說:
  1. 設計和規範請求方的流水号,讓流水号不重複,畢竟請求方的數量是有限的,這個流水号規則是可控的可以做到不唯一,相同的就認為業務重複。
  2. 把1次支付給它拆解,先prepare再commit,prepare的過程生成全局唯一ID,并且把這個ID傳回給請求方,請求方确認後commit。

這些拆分完就ok了?如果原先4張表,現在不夠,要擴充到8張表,你的取模都不對了,而你的業務系統是24小時運作的,你如何處理這些資料?淩晨來個停機公告?詳細的可能以後空了會寫篇"資料處理篇"。這裡就不寫了。

有人可能會說用一緻性Hash,一緻性Hash雖好,但它帶來了複雜性,同時某些情況下,它有點緻命,比如某個或者某些節點突然的網絡時壞的情況下,你仔細想想。

好了,分庫分表可并不好玩,盡量能不分就不分,别人家分庫分表是被迫的,你還主動往上湊?當然現在情況好多了,直接用分布式DB去解決問題,而不錯的就有PingCAP的TiDB。

9. DB,Cache,存儲

越接近資料的地方,就越難處理,而我認為首先需要去了解資料庫,而非會很多指令操作很多資料庫,這些需要肌肉記憶的東西相對是能較快上手的,而思想更重要。比如:你了解資料庫的索引麼,B+樹呢?LRU是什麼?mysql的LRU又有什麼不同?為什麼說資料庫存資料到一定量級要拆分?而有些人說單表達到300W要拆,有人說500W,而又有人說5KW要拆?mysql鎖的4種方式有何差別?資料庫資料的存儲方式?為何要有redo和undo日志?redo和undo的日志為何反而比存資料更快?好了,這些基礎問題在這裡不讨論。而現在因那些大廠處理資料庫的方式徹底改變了,為什麼叫資料庫?根本的想法就是讓資料庫好好的去快速處理資料的存取問題,而非一站式解決資料處理的問題,計算和存取盡量分開,把資料計算問題上移到上遊,而你如果還沉浸在寫複雜的表關聯,存儲過程,用觸發器,臨時表,内置函數等等也許你得改變思想了。

分布式存儲和分布式資料庫,既然都是存儲資料的,那肯定要保證強一緻性的,不是麼,在業務場景下,資料存取都不一緻,可能也就失去了它最原本的作用;而誰家産品在這個基礎上盡量做到高性能,穩定,可靠,那就是本事的問題。而強一緻性的問題必然會導緻可用性的降低,這也是分布式存儲和資料庫要解決的難點,而我認為處理思路有如下3點:1. 增加健壯性,從網絡層面下手,讓強一緻性不容易出現不可用狀态。2. 類似于mysql主從或者增加MQ發送到消費端一樣,再增加一套,組合成可用性,盡量降低損失。3. 讓不可用控制在一定範圍内,防止整個不可用。 這些都涉及到大量的基礎研發,難度和成本不是一般的公司能處理的了的。

9.1. 分布式存儲

存儲分為3大類:塊存儲,檔案存儲,對象存儲。而分布式存儲開源的其實并不多,ceph可能是最出名的一個,但個人認為它太複雜了,複雜的算法,複雜的設計,複雜的代碼,又有幾人可以拍胸脯保證他能搞定ceph,反正我個人了解有限,paxos算法保證一緻性,一緻性hash去解決節點增加删除,各種平衡各個節點的資料,龐大的C++的代碼量,還都支援3種存儲,如何保證可靠安全是個極大的挑戰,而從回報來看,ceph塊存儲rbd相對穩定。ceph的檔案存儲和對象存儲呵呵。當然某些大廠還是能搞定的,但他們的ceph還是那個ceph麼?

而現在都在雲原生時代了,很多的資料存儲問題可以通過對象存儲解決,比如虛拟機鏡像,資料備份,docker鏡像,大小圖檔檔案等等。而個人比較看好Minio(go語言寫的),github上star已經達到恐怖的19K,而它的作者就是原來寫glusterFS分布式檔案存儲的作者,minio寫了簡化版的raft協定保證一緻性,進而保證N/2的情況下仍然可讀,大于N/2的情況下可讀寫。但不能叢集添加節點擴充,是以要在一開始就設計好節點和容量,這樣屏蔽了添加節點帶來的複雜性,資料要擴容那就再增加叢集。思路上和K8S的聯合類似,同時在安全性上是以對象為機關的,把不可用隔離到對象級别。雖然使用的回報的不是很多,但上次看到篇文章,生産使用效果回報不錯。

9.2. 分布式資料庫

目前情況有回報很好的TiDB(也是go寫的), 其中就用了etcd保證一緻性,關鍵它相容大部分的mysql文法,同時提供事務和分析的支援,從各個大廠回報,最新版的性能和穩定性很不錯,github star高達21k,完整的文檔和易于了解的架構。其實在TiDB之前,codis也是他們的開源作品,很好的一個redis叢集,有興趣的可以了解了解。

至于騰訊開源的分布式資料庫(TBase)和華為開源的分布式資料庫(GaussDB)太新了,了解有限。

10. 微服務的基礎設施

到次,微服務就結束了麼?顯然不夠,以上部分是微服務核心必要問題。還有配置管理問題,部署問題,監控問題,日志問題,業務鍊路追蹤問題。但個人覺得這些問題在相對傳統基礎設施下的解決方案都有點“牽強”,有些方案可能已經過時了,特别是上了docker和k8s後,這些方案再硬套上去是不行的。也不知道為啥有人把K8S也放入微服務架構的比較中,雖然它們也有相似之處,但是要處理的問題和方向個人覺得是完全不同的。是以,這些問題會更簡略方式說吧。

其實不管是Minio,TiDB,codis,還是上面提到的Nacos,Etcd,consul,RocketMQ,ElasticSearch,snowflake,api gateway都是優秀的開源産品,官網都有較完整的架構圖和說明文檔和手冊,在這裡放入架構圖和列出它們提供的功能等等并沒多大意義,至于更多的基于Docker,K8S,Service Mesh雲原生開源應用就更多,而我這裡簡單引出部分隻是想說明優秀的東西很多,将來會更多,知識是永遠學不完的,而分布式系統你怎麼去了解它,它有什麼優缺點及以上一緻性的方案和CAP定理,以及基于這些選擇适合自己的我認為才更重要的,一點點個人見解,見笑。

10.1. 配置管理

為何需要配置管理?你那麼多服務,配置怎麼解決,生産配置,測試配置,狀态配置等等,不可能微服務一台台手工配置,再重新開機服務吧?那是要死人的。是以需要一個集中式的配置管理中心,同時需要滿足配置的修改不能讓服務重新開機。寫過代碼的都知道套路,在main入口都需要一次性先加載日志檔案,資料庫等等各種配置,如果要修改配置,隻能到相應的配置檔案中修改配置再重新開機程式生效。而要解決不能重新開機服務的情況下動态加載配置生效,你需要主動通知觸發修改記憶體中該值或者watch這個配置,有變動就修改記憶體中該配置的值,進而達到動态生效。而一般這個配置都是儲存在KV存儲中的。是以etcd,consul,nacos,包括K8S中的configMap 都有配置中心功能,而專用的配置中心還有攜程開源的apollo,但它可能更适合java和基于Eureka注冊中心的生态中,也較完整和重量。

10.2 CICD

這個屬于DevOps的範疇,這裡在微服務1.0中的典型應用有Gitlab的CI/CD,以及Jinkins,但個人覺得在雲原生中,Jinkins雖好,但還是太複雜太重了,流程也并非很适合雲原生應用。持續內建和持續部署的問題是個複雜的問題,目前CI相對成熟。

10.3 監控系統

傳統的選擇可能是zabbix,但小米開源的Open-falcon和雲原生的promtheus(普羅米修斯,包括上面的阿波羅,啥時候出個雅典娜應用啊_)分布式監控系統更合适,而K8S中的HPA彈性排程現在就是基于promtheus監控門檻值實作的。

10.4 日志系統

大家可能都知道建構ELK(elasticsearch,logstash,kibana)日志系統,但現在的ELK已經并非以上這3樣開源應用就能建構日志系統了,個人覺得它更是個日志系統解決方案概念,如果隻是這3樣應用軟體部署在K8S系統上你可以試試,把整個系統拖垮了都有可能,想想java的logstash如果部署在每個docker或者pod中,它會把你整個資源給吸光。

10.5 鍊路追蹤

我覺得鍊路追蹤很重要,而在微服務1.0中,不了解鍊路追蹤可能會遇上不少的坑,想想你怎麼才能快速追蹤異常業務,定位服務處理時間,延遲,性能問題,追蹤必不可少,分區後如何理順服務之間的關系,必然少不了資料傳輸中的埋點,是以資料封包中的traceid,spanid,parentid等埋點必不可少。不管是在封包頭或者封包體中。常用的開源鍊路追蹤有zipkin,pinpoint,skywalking。

11. service mesh

為什麼把service mesh叫微服務2.0,本質上它還是微服務,那和微服務1.0有和差別?

11.1 微服務1.0痛點

在微服務中,監控,日志,鍊路追蹤必不可少。沒有這些,你的微服務可能随時會分崩離析。到此,你的微服務在以上這些基礎下,已經可以跑起來了,但這就完美了麼?你将很有可能面對一系列坑,首先是你可能支援不了跨語言的整合,各開發人員水準能力的問題,想想開發人員需要了解多少的包?處理服務注冊和發現,配置中心,負載均衡,限流熔斷,服務重試,鍊路追蹤等等,這些應用包和你的業務耦合在了一起。如果還碰到某個或某些包需要更新或者變更的情況,這些是微服務1.0最大的痛點,是以service mesh來了。

11.2 service mesh

如果直接看service mesh其實是很難了解的,那service mesh如何解決微服務1.0的痛點,本質上是一個複雜的耦合性問題,個人的了解就是類似于微服務劃分邊界的處理方案,再“下沉一層”,把那些服務注冊,服務重試,配置中心,負載均衡,熔斷限流,日志收集,鍊路追蹤,監控等等再下沉,和業務代碼解耦,讓代碼層幹類似于聲明式的活,而下沉的一層幹類似于排程器的活。再畫張簡圖對比了解下吧:

微服務思想篇

讓service專注于業務代碼的事情,其它的事情交給sidecar去完成,類似于讓服務當老闆的感覺,我隻需要知道我調用其它哪個服務,其它就交給sidecar去做吧。這個sidecar是标準叫法,有解釋為mesh的,有proxy的,在istio中叫envoy。而一個個sidecar組成了一個資料平面。而服務的注冊中心,配置中心,監控,日志,鍊路追蹤,安全認證,灰階釋出等等認為在另一個平面,叫控制平面。是否霍然開朗?_

這裡其中可能唯一的疑惑是service和sidecar分開後,那不又涉及到網絡通訊問題,分區問題,一緻性問題等等,又回到了起點,其實service和sidecar的要求是一起部署在本地,這樣就不涉及到分區問題。在K8S和istio中,這個envoy就是通過注入(inject)的方式塞進pod中,而pod就是k8s的最小機關。而pilot就是服務注冊中心的角色,還有灰階釋出,逾時重試等管理,mixer就是收集envoy各種資料,citadel用于各種認證安全,galley用于擷取k8s各種配置,校驗,分發的元件,這4大元件構成了控制平面。整個istio和k8s完整的配合。

如果業務系統上線K8S和service mesh後,一次調用變成3次,目前有很大的介意在性能的影響,但其中2次是本地調用,總體和帶來的收益是值得的,有關更多可以看看大佬的分享,像美團,閑魚,B站等等,其實無狀态的應用上K8S和service mesh難度并非很大,難點都在于有狀态的服務,而大廠的資料量很大,這些有狀态的應用以目前情況下,很少有看到全部上K8S和service mesh(當然也許我孤陋寡聞)。有狀态服務部署到K8S上是有相當難度的,遠比直接部署難,雖然現在有helm和operator。

service mesh簡單到此結束,目前最流行的service mesh就是istio,官網有個很好的bookist的舉例,可以去看看,當然,istio雖說可以支援各種方式接入,但目前結合最好的就是K8S,畢竟是"一窩"的。當然還有sofamesh也不錯,它解決了一些istio的單點和複雜性問題,至于mesos等等其它的,我也不知道。

12. 秒殺,搜尋,推薦

秒殺,搜尋,推薦這3大業務是網際網路企業應用的的3大核心系統,你可以打開你手機各類APP,仔細觀察這3大業務比比皆是,再聯系微服務,無處不在。

這裡的“秒殺”并不僅僅是秒殺商品,想想12306,春節紅包,它們本質上都是秒殺,突然的大流量進入請求有限的資源,這種并發需求和大量的不限定資源的并發場景難度是不一樣的,前幾天看到朋友圈和公衆号上一篇go語言寫的12306的秒殺架構文章,會golang的有興趣的可以看看,它是否可行?是否解決了一緻性的問題?

秒殺架構,從業務角度看根本沒解決根本問題,比如:秒殺商品就那麼多件,春節火車票就那麼多張,春節紅包就那麼多個,滿足不了那麼多人的需求。從技術角度上講,突然大流量的請求這些有限資源,我的目的是讓系統不崩。如何去解決系統不崩,有個很形象的模型去解釋:“漏鬥”。通過微服務一層層不斷的去削去這些請求,最終隻能讓漏鬥下面的有限數量的請求通過到達資料庫層面請求到這些資源。因為上遊的處理能力相對總是大于下遊的處理能力的,雖然可以通過彈性水準擴張性能。架構思想如下:通過API網關層的隊列抛去大部分的請求,再通過MQ削峰填谷到達業務邏輯層,去掉那些不合格的請求,黑名單,不滿足要求的新使用者,不合規的IP位址等等,再去随機或者網絡先後等再抛去一部分請求,最終隻有有限的請求到達DB請求到資料,同時要保證不超賣問題必然需要DB去保持強一緻性。但讀請求檢視剩餘數量可不一定是實時準确的,資料庫是相對脆弱的,讀和寫必然要去分開來滿足并發需求。而讀請求一般都是從redis中讀取的,db中最新的剩餘數量異步方式寫入redis中,是以讀請求的資料極大機率是不準确的,但不影響最終的業務。難點就在于不讓這個漏鬥的上口破了,那就直接造成雪崩了。是以你可以看到12306變态的驗證,支付寶和微信等分流。春節搶紅包時一大堆的卡片,這些卡片你以為真的請求到了後端,可能壓根都還沒到達API網關層,在CDN層面甚至APP用戶端上就預先給你生成好了,這裡的架構圖就不畫了。

是以那篇文章看到試圖通過redis AP最終一緻性問題要去解決CP問題,是比較困難的,很多人在說最終一緻性,那想想這個最終一緻性到底最終到什麼時間才一緻?這個時間差是相對不确定的,在某些業務場景下,這個時間差是有要求的,比如火車票的最終一緻性不可能等火車都開過了才解決一緻吧?又比如,支付清算,這個清算是有場次和時間限定的,某些清算必須當天5點前結束的,在這之前必須解決一緻性的問題。是以,大機率的解決最終一緻性必須有完整的監控和核對及線下處理方式去保障。

搜尋系統(這裡的搜尋是垂直搜尋,非通用搜尋)和推薦系統這2個系統是相對最複雜的了,遠不隻是微服務架構的問題,這裡就水幾句話吧,從微服務架構角度上,簡單點就是召回層對應于資料通路層,排序層對應業務邏輯層,現代搜尋和推薦應用了大量的大資料和人工智能技術,打開你的APP首頁,直接各種推薦撲面而來,其實衡量一個推薦的牛逼看它是否越來越懂你,本質問題是你這個userid在不斷的被打标簽。你再仔細搜搜商品,往下拉取試試,你也會收到驚喜。好了,搜尋和推薦就水到這裡,其中個人覺得elasticsearch很有意思和值得去應用的,這裡就不聊了。其實各種技術一直都在我們身邊,不管大資料,人工智能,雲計算,微服務等等,打開你的APP直接觀察,看看他們的業務劃分,業務場景,業務聚合,購買,支付流程,技術人員别被各種商品和視訊内容給蒙住雙眼。

13. 收尾

好了,微服務到此結束,不知能否稱為篇“土味”微服務思想篇,其中盡量用簡單語句和例子把那些可能比較難懂的給解釋清楚,其中包含了個人的一點點了解,但畢竟用相對白話去描述那些詞語,限于個人能力,可能有些地方未必準确或描述不到位,那就将就了;至于要說能不能稱為“幹貨”,那就隻能在于你的了解。其中微服務還有很多内容沒涉及或内容很少,比如安全授權問題,DevOps,監控,日志,鍊路追蹤,各種測試問題,這裡不涉及或者簡單帶過主要是這些每一個都包含大量的知識,不是一點點文字能給說完的,而在微服務1.0中有些不好解決和不夠優雅,而大廠都是基于他們自己的需求和業務開發。啰哩啰嗦的原來寫了這麼多字,超出了原來的用時預期,以後也不會寫這種“又長又臭”的思想篇了,但寫寫k8s,istio,golang等也不錯。最後,這世界變化太快,各種浮躁和焦慮,知識和技術是永遠學不完的,有時我們需要的是停下來好好的想一想,人生不也一樣如此麼?

繼續閱讀