微服務落地實踐 - 經驗分享

前言

隨着架構設計的發展,微服務架構可以說是目前架構領域炙手可熱的設計理念。在公司,筆者也一直在負責系統的服務化設計和開發工作。

今天就來談談微服務落地實踐中的一些問題。希望對微服務設計無從下手的朋友,起到一些參考作用;另外也希望把自己的觀點分享出來,期待與大家一起交流,能夠認識到不足之處。

一、服務拆分

在落地微服務之前,我們遇到的第一個問題就是:應該如何拆分服務?

大家知道,關於如何拆分服務,並沒有一個完全通用的法則。不過有以下幾點要素,我們可以參考。

1、業務獨立性

一般情況下,我們第一時間都會考慮到這點。將系統中的業務模塊,按照職責標識出來,每個單獨的模塊拆分成一個獨立的服務。

這樣拆分之後,我們的服務要滿足一個原則:高內聚,低耦合。

比如,我們把一個系統拆分爲商品服務、訂單服務、物流服務。一般情況下,我們修改物流服務的時候,並不會影響商品服務,這就是低耦合的體現;那麼訂單服務裏面的功能和邏輯,都是圍繞着訂單這個核心業務流程的,就可以說它是高內聚的。

2、業務穩定性

我們可以把系統中的業務按照穩定性進行區分。比如,用戶註冊、登錄部分,在我司的系統中,這一塊的代碼只要寫完,基本就不再會發生變動,那麼我就將他們拆爲用戶服務。

同理,還有日誌服務、監控服務,這些模塊基本上也都是很穩定的業務,可以考慮單獨拆分。

3、業務可靠性

這裏講究的是,將可靠性要求高的核心服務和可靠性要求低的非核心服務拆分開來,然後重點保證核心服務的高可用。

避免由於非核心服務故障,而影響核心服務。

4、業務性能

基於業務性能拆分,考慮的是將性能壓力大的模塊拆分出來。對於這一點,筆者有兩點想法:

  • 避免性能壓力大的服務影響其他服務。
  • 將高流量的業務獨立出來,扛不住的情況下,方便水平擴展。

比如,在筆者參與的一個系統中,曾通過 RocketMQ 對接來自多家廠商的大量數據。

當時,就獨立出來一個消息服務,專門用來消費消息。然後有的是在本地處理,有的通過 RPC 接口轉發到其他服務處理,有的直接通過 WebSocket 推送到前端展示。

這樣的話,即便流量激增,考慮給消息服務增加機器,提高消費能力就好了。

當了解到上面這幾種拆分方式之後,我們就可以根據自己的業務範圍和技術團隊規模,來考慮自己系統的服務拆分了。

不過在這裏,尤爲值得注意的是,微服務切忌拆分的過細,一定要結合業務規模和技術團隊的規模。

筆者以前就遇到一個項目,在做服務化的過程中,當時的技術負責人按照業務獨立性來拆分服務,結果拆出了10來個服務;然後更狠的是,每個業務服務又把Service層和Controller層拆分開來,每個業務方法都需要遠程調用。據當事人描述,這樣做是爲了方便後期提升擴展能力。

不過說實話,有些系統業務量並沒有那麼大,一味的迎合微服務中的微字,無疑是給自己增加難度,破壞整體系統的穩定性。所以,在重新梳理了業務流程後,筆者對這個系統進行了重構,縮減服務數量。

在這裏,筆者想說明一點。Service層和Controller層是可以拆分成多個模塊的,這個沒關係。不過,它只應該是模塊的分離,而不是服務的拆分。比如我們可以在開發階段把它們拆分成多個模塊,然後通過 Maven modules 聚合到一塊,在部署運行階段,它們還都是一個服務。

二、技術選型

當完成了服務拆分之後,選用什麼框架進行開發呢,應該選擇 Dubbo 還是 SpringCloud ?

筆者不想單純討論它們的優劣之處,在這裏可以就着這個問題分享下筆者的心路歷程。

最初進行選型時,筆者選擇了 SpringCloud,畢竟它號稱是微服務一站式解決方案。然後就搭建框架,集成各種組件,完成了一些 demo 的開發和測試。

不過,在完成這部分工作後,重新審視整個系統,看到SpringCloud中,涉及到的Eureka、Ribbon、Feign、Hystrix、Zuul、Config這些組件。

這時候,我會產生兩個疑問:

  • 這些組件是不是非用不可 ? 有沒有更輕便的系統方案 ?
  • 這麼多東西,任一環節出了問題,我們是否能hold的住?

筆者對於一站式這個詞有兩層理解。第一,它簡化了分佈式系統基礎設施的開發,易於開發;第二,簡化的同時,它一定是屏蔽了複雜的配置和實現原理,不易於深入理解它的原理。

作爲架構師或者團隊技術負責人,我們必須對自己系統涉及到的技術點做到知根知底,或者說,最起碼要懂得它們的原理。這樣,即便遇到了問題,在 Baidu / Google 不出來結果時,也不會慌。

基於這樣一個思路,筆者把目光又轉向了Dubbo。對於筆者來說,對Dubbo就比較熟悉了,從它的框架本身來說,已經實現了負載均衡和集羣容錯,調用方式也是基於接口的。相較SpringCloud而言,不需要再額外引入像Ribbon/Feign這樣的組件。

還有一點,Dubbo得益於強大的SPI機制,我們可以非常方便的擴展它。如果業務上有需要,在很多地方都可以對它進行擴展,比如 RPC 協議、集羣、註冊中心、序列化方式、線程池等。

不過話說回來,Dubbo只是一個高性能的 RPC 框架,拿它和SpringCloud相比,更多的還是比較REST和RPC。不過關於這一點,就一般的項目而已,這點性能差異還不足以一錘定音,最多隻是錦上添花而已。

至於其他的組件,比如Hystrix/Zuul/Config/zipkin等,筆者的觀點,還是看業務規模。微服務只是一種設計思想,架構理念,而不是說用到了很多分佈式框架才叫微服務。這些框架的產生,只是爲了解決微服務系統遇到的問題的。

需知一口喫不成個胖子,在做技術選型時,切記直接對標像阿里、京東、美團這樣的大廠經驗,一來,也許我們遇不到那樣的業務場景;再者,一般公司也沒有人家那樣的人才儲備。畢竟如果線上出了問題,是沒人跟你分享損失的。

總的來說,還是結合自己的實際情況,以最穩妥的技術方案,完成業務上的需求。

三、節外生枝

拆分了服務,也完成了技術方案的選型,那就萬事大吉,開始擼代碼了嗎 ? 如果單純作爲開發人員,那確實開擼就行了。如果你是一個系統的負責人,只滿足於高屋建瓴,不考慮細節問題,那必然會節外生枝。

1、超時和容錯

服務化之後,不同服務之間的調用就是遠程調用。遠程調用有個最基本的設置,即超時時間。

比如,在Dubbo中,默認的超時時間是1秒。我們不能單純的使用默認值或者統一設置成另外的值,爲Dubbo設置超時時間最好是有針對性的。

比如,比較簡單的業務可以設置的短一些;但對於複雜業務而言,則需要適當的加長這個時間。因爲,這裏還涉及到一個集羣容錯的問題。

Dubbo中,集羣容錯的默認策略是失敗重試,次數爲2。假如有的業務本身就需要耗費較長的時間來執行,因爲超時時間太短就會觸發容錯機制,來重試。大量的併發重試請求,很可能會佔滿Dubbo的線程池,甚至影響後端數據庫系統,導致連接被耗盡。

2、容錯和冪等性

我們上面說,如果超時時間設置太短,有可能會導致大量請求會不斷重試,而導致異常。

這裏還隱瞞着另外一個細節,即讀請求和寫請求。如果是讀請求,那麼重試無所謂;如果是寫請求,我們的接口是否也支持自動重試呢 ? 這就會涉及到接口冪等性問題。

如果寫請求的接口,不支持冪等性,那麼集羣容錯就得改爲 failfast,即快速失敗。

3、分佈式事務

筆者感覺,分佈式事務在業界是一個沒有徹底解決的技術難題。 沒有通用的解決方案,也沒有既高效又簡便的手段。

雖然如此,但我們也得事先考慮到這一點,不然數據肯定會變成髒亂差。

在考慮解決方案之前,我們需要先看看自己的系統是不是真的追求強一致性;按照BASE理論,在分佈式系統中,允許不同節點在同步的過程存在延時,但是經過一段時間的修復後,能夠達到數據的最終一致性。

基於這兩個思路,我們纔好制定自己的分佈式事務方案。

對於要求強一致性的場景,或許可以考慮XA協議,通過二階段提交或者三階段提交來保證。

對於要求最終一致性的場景,可以考慮採用 TCC 模式,補償模式,或者基於消息隊列的模式。

比如基於消息隊列模式,可以採用 RocketMQ。它支持事務消息,那麼這時候整個流程大概是這樣的:

  • 通過 RocketMQ 發送事務消息到消息隊列;
  • 消息發送成功,則執行本地事務;
  • 如果本地事務執行成功,則提交RocketMQ事務消息,提交後對消費者可見;
  • 如果本地事務執行失敗,則刪除RocketMQ事務消息,消費者不會看到這條消息。

另外,在這裏安利下阿里開源的Seata。目前最新版本是1.1.0,支持多種事務模式,比如 AT、TCC、SAGA 和 XA 事務模式。

筆者有篇文章是基於 Seata 0.7版本的寫的,有興趣的朋友可以瞭解下。

SpringBoot+Dubbo+Seata分佈式事務實戰

4、消息隊列

在分佈式系統架構中,爲了系統間的解耦和異步處理,應對高併發和大流量,消息隊列絕對是一大利器。

在使用這一利器前,我們也得考慮下有可能因爲消息隊列帶來的煩惱。

首先需要考慮的就是可用性,如果消息隊列不可用,會不會對系統本身造成大量的不可用;

然後,消息會不會丟失呢 ? 如何保證消息可靠性傳輸呢?比如要考慮消息隊列本身的刷盤機制、同步機制;數據發送時的確認和消費後的提交;

然後就是重複消費,如果保證了消息不會丟失,多多少少都可能會有重複消息的問題,這時候就要考慮重複消費有沒有問題,即消息冪等性;

還有,消息順序性問題,你們的業務場景裏,是否有消息順序性問題,如果有這個問題,要麼在設計時規避它,要麼在消費時保證它的順序。

5、統一日誌

隨着微服務的拆分,日誌系統也可能會演變爲獨立的模塊。爲了查看日誌,我們可能需要登錄到不同的服務器去一個個查看。

因此,搭建統一的日誌處理平臺是必然的。我們可以採用 ELK 的解決方案進行日誌聚合。

在這裏,還需要鏈路追蹤問題。在微服務複雜的鏈式調用中,會比單體應用更難以定位和追蹤問題。

對於這個問題,我們考慮引入分佈式調用鏈,生成一個全局唯一的 TraceID ,通過它把整個調用鏈串聯起來。結合Dubbo框架的話,我們實現自己的Filter,用來透傳這個TraceID

具體思路可以參考:SpringBoot+Dubbo集成ELK實戰

當然,我們也可以選用一些成熟的開源框架來解決。

總結

本文簡單總結了微服務設計和開發過程中,可能會涉及到的一些問題。

以上觀點只是一家之言,僅僅是筆者在過去時間裏的經驗總結。

如果對您有幫助,請點贊鼓勵~ 如果您有不同觀點,請積極發言,共同交流~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章