探討篇(一):服務粒度的藝術 - 簡化架構與避免服務氾濫

一、背景

上週小組有個需求上線牽扯9個應用(小組目前維護了26個服務,由於團隊系統業務屬性特徵基於高可用、高性能原則拆分,有些是合理的,有些不是很合理的),同時上週OpsReview的一個微服務濫用典範案例(Promise服務A調用服務B,服務B只是讀個配置數據返回,無具體業務邏輯) ,OpsReview會上也看到過其他微服務氾濫等案例,這些事情引發了我對服務氾濫和服務粒度的深入思考,接下來在穩定性的前提下,基於成本收益維度思考,小組哪些應用(先從2級開始)應該合併治理。



在當今軟件開發的舞臺上,微服務架構因其所靈活性以及獨立部署等優勢而廣受推崇。然而,隨着服務的過度細分,我們面臨着服務數量激增所帶來的管理複雜性和分佈式通用問題挑戰性等。



本文通過Promise後端現有服務探討服務粒度的合理劃分與有效合併的關鍵因素。本文的觀點源自我在學習與實踐過程中的思考,尚處於不斷探索和驗證的階段。 希望能“拋磚引玉”,引發大家的思考與討論, 讓我們共同進步, 從而優化我們的系統設計。

目前團隊服務氾濫的影響如下: 1、維護成本增加:服務數量的增加直接導致了維護工作量的增加。這不僅包括服務器維護,還包括代碼維護依賴管理,比如Promise時效內核升級、中間件JSF等版本升級、安全漏洞工單log4j版本改造等都需求部署上線N個(最多26)應用。當服務數量過多時,即使是小的變更也可能需要協調多個服務,從而增加了工作量。 2、團隊協作難度:在服務氾濫的環境中,團隊成員可能需要跨多個服務進行協作,比如本次需求上線牽扯9個應用。這不僅需要更多的溝通和協調,而且還需要確保所有相關人員對服務的改動和更新都有清晰的認識。這種跨服務的依賴關係可能會導致團隊間的溝通混亂和效率低下。 3、資源利用效率低:服務氾濫可能導致資源分配不均,一些服務可能過度使用資源,而其他服務則可能閒置。這種不平衡的資源分配會導致整體成本的上升。





名詞解釋: 模塊化:是指將一個複雜的系統分解成若干個相互獨立且可集成的部分,這樣可以簡化系統的設計、開發和管理。模塊化的目的是通過創建高內聚、低耦合的模塊來提高軟件的可維護性、可複用性和可擴展性。在模塊化的過程中,每個模塊都是一個獨立的單元,可以單獨開發、測試和優化,同時通過定義良好的接口與其他模塊通信。 粒度: 在軟件架構中,粒度通常用來描述組件或模塊的大小和層次。較小的粒度意味着系統更加靈活,因爲它允許更精細的控制,但可能會帶來性能問題,如增加硬件資源和通信開銷。較大的粒度則可能提高效率,但會犧牲一定的靈活性。



二、Promise服務粒度問題

在微服務架構的設計理念中,最大陷阱之一在於,並非所有構成程序的組成部分都必須被設計爲微服務







系統交互圖

簡單描述下各應用作用:

1.A:訂單履約控制中心

2.B:promise時效服務,主要提供訂單下傳庫房時效、訂單妥投時效

3.C:promise產能服務,主要用於倉庫產能查詢、產能佔用

4.D:promise消息服務,主要用於訂單時效異步處理,其中包括髮送全程跟蹤,訂單時效緩存等。

5.E:promise訂單時效持久化服務

對應用戶在京東APP購買商品流程如下:

1.北京用戶4月1號22點購買了一件衣服,庫存中臺定位的是北京的倉。

2.A服務通過JSF調用Promise計算這個訂單下傳庫房時間是4月2號0點01分(對應B應用)。

3.JSF調用佔用4月2號的倉庫產能(對應C應用)。

4.然後通過MQ方式給用戶發送全程跟蹤話術,告知用戶訂單時效環節(對應D應用)。

5.最後把訂單時效數據進行保存持久化,用於業務進行數據分析和履約績效考覈(對應E-mysql持久化應用)

6.同時爲了方便排查歷史訂單數據持久化同時存在es(對應E-es持久化應用)。



那麼問題來了

1.上面這些應用劃分合理嗎?尤其持久化包含2個應用E-mysql和E-es。 這應該是1個服務還是2個服務更合理呢?

接下來針對這個問題,進行分析。



三、服務粒度

模塊化關心繫統分解成單獨的部分。而粒度則處理這些單獨部分的大小。粒度纔是分佈式各種挑戰的問題關鍵。那如何來度量粒度大小呢?看裏面java文件數、class類數量、代碼行數?其實並不是的,而是看服務的職責,服務範圍包含哪些,另外一個指標是看服務對外提供的公共API接口或者MQ的數量。雖然這2個指標也是存在變化和主觀因素,但這應該是目前最接近客觀度量和評估服務粒度的方法。



如下圖:當業務複雜度達到一定程度後,微服務架構消耗的成本纔會體現優勢,並不是所有的場景都適合採用微服務架構,服務的劃分應逐步進行,持續演進。





現在很多拆分粗粒度可能是主觀意識或者直覺,缺乏理論。我們需要使用【拆分因素】和【合併因素】來客觀綜合分析利弊,從而形成拆還是不拆服務的充分合理解釋。

1、拆分因素

粒度拆分因素解決的是什麼時候應該將服務拆分爲更小部分。爲服務拆分爲更小的部分提供了指導和依據。其中粒度拆分因素的主要因素如下:





1.1、服務職責&業務邊界

服務職責是拆分服務考慮的第一因素,確保服務與業務能力對應,領域治理,職責單一, 避免單個服務承擔過多不相關的功能。需要從兩個維度來考慮。

•第一個維度是內聚性,即服務內部的行爲關聯的緊密度,每個服務應該只負責一個業務能力或業務領域。

•第二個維度是組件的整體大小,服務的大小和職責範圍需要平衡,既不能過於龐大,也不能過於細碎。一般根據服務對外提供的入口數來度量。

比如上面說的Promise時效B應用、C產能應用、D消息全程跟蹤、持久化應用 這4個應用內聚性緊密型不大,產能域和時效域拆分。同理訂單時效E-mysql持久化服務和E-es持久化服務,這2個應用都是數據持久化,功能是一樣,即把訂單時效數據保存起來。內聚性相對較強,入口也都是消費對應MQ。強內聚意味着數據持久化這個服務是單一目的性的,這2個應用不應該拆分。

1.2、容錯性&穩定性&高可用

•容錯性和服務可用性也是很好的分解因素之一,分析如何通過服務拆分提高系統的容錯性和高可用性。

•將系統中的業務模塊按照業務優先級排序,可靠性要求高(0/1級應用)的核心服務和可靠性要求低(2級應用)的非核心服務拆分開來,重點保證核心服務的高可用。這樣可以避免非核心服務故障影響核心服務。

•服務的粒度越大,故障發生時帶來的影響面也越大,識別出系統中脆弱的部分將其拆分出去,可以有效的減少故障時帶來的連帶影響。拆分爲更小的服務,以便於在不同的節點上實現容錯和故障轉移。

比如案例中的物流配送時效和產能服務通常是關鍵服務,它們的可用性直接影響用戶體驗。拆分這些服務時,要特別考慮如何設計系統的高可用性和容錯機制。這也是爲什麼上面爲什麼要通過MQ異步方式解耦把時效核心服務和 全程跟蹤數據持久化應用拆分,優先保障訂單庫存生產(物流配送),再來處理全程跟蹤話術(用戶體驗)和數據持久化數據分析。因爲數據持久化mysql耗時並且可能故障概率大。

如果mysql服務和es服務容錯性不一樣,比如es老是崩潰,不穩定,進而影響了MYSQL服務,導致業務影響,則可拆分爲2個服務。

1.3、性能&吞吐量

•基於性能拆分和基於可靠性拆分類似,將性能要求高或者性能壓力大的模塊拆分出來,避免性能壓力大的服務影響其他服務。

比如持久化mysql服務100W/M 和持久化es服務80W/M 是一樣的吞吐量。大促期間es消費相對較慢,但性能在可接受範圍。比如自營訂單1W/S TP99位20MS,外單100/S TP99 爲3000MS可能更適合拆分

1.4、可擴展性

•服務拆分需要考慮可擴展性。其實很多設計方案都會多度設計擴展性。其實通常很難去猜測未來上下文功能是否可能會擴展(比如額外的數據持久化方式hbase、jdq等)。

•做過太多需求,說某種業務場景未來可能會用,但根據歷史經驗,業務基本不會有這種場景。

•建議是等待,直到持續可擴展性被確認,然後讓可擴展性這個因素成爲判斷粒度分解的主要因素。

1.5、團隊大小和穩定性

•考慮開發、維護團隊的能力和組織結構。服務拆分應便於團隊協作和溝通。



從拆分因素來看上面的promise服務粒度應用的劃分是否妥當? A: 對於B時效服務、C產能服務以及D消息服務,其應用拆分是根據業務的獨特需求來定製的。特別是在大型促銷活動期間,訂單的實時生產、倉庫產能實時管理情況是至關重要的。因此,我們基於時效性和產能管理的需求進行了服務劃分。此外,通過MQ(消息隊列)機制,我們實現了業務特徵的解耦,使D消息服務能夠消費相應的MQ,並處理相關的業務邏輯,例如提供用戶的全程跟蹤體驗和訂單時效性的緩存。爲了確保服務的穩定性和高可用性,我們特意將持久化應用獨立出來,從而定義了清晰的職責邊界。例如,在進行MySQL數據庫的主從切換時,我們只需暫停持久化應用,這樣就不會影響到訂單的時效處理和用戶的全程跟蹤邏輯。

2、合併因素

上面的拆分因素爲何時將服務分解爲更小的部分提供指導和依據,那粒度合併因素則相反,爲服務重新重合在一起提供指導和參考依據。粒度集成的幾個主要因素如下:





2.1、服務原子性

服務的原子性是指服務操作應該是一個不可分割的整體,要麼全部成功,要麼全部失敗。這有助於保證數據的一致性和系統的可靠性。

•考慮不同服務之間是否需要事務?需要原子操作或事務性的業務流程可能不適合拆分成多個服務,因爲分佈式事務管理通常是複雜且成本較高的。

•具有原子操作的單獨服務具有更好的安全訪問控制

•如果服務之間需要頻繁同步數據以保持一致性,這可能表明它們應該被集成合併爲單個服務。

•具有組合操作的單獨服務間不支持數據庫事務,微服務應該追求數據自治,每個服務擁有自己的數據庫,以避免分佈式事務的複雜性。

2.2、成本效益

拆分服務可能會增加運維成本,因此需要進行成本效益分析,確保服務的粒度調整能夠帶來正面的經濟效益

•硬件成本:如果拆分後,硬件成本更高,可能更傾向更粗粒度,減少資源消耗

•變更成本:如果服務經常一起變更(開發、測試、部署、上線),可能更傾向更粗粒度。

•維護成本:維護多個服務,比如服務通用治理版本升級等,成本較高

2.3、網絡開銷

•考慮服務之間是否有交互?服務間的通信會帶來額外的延遲。如果服務拆分導致大量的遠程調用,這可能會對性能造成影響。在某些情況下,集成合並服務以減少網絡通信可能更有利

2.4、共享代碼

•是否有共享代碼,在分佈式服務架構中處理共享代碼,事情會變得複雜,有時候會影響服務粗粒度。如果共享代碼太多,合併則可能更合適

2.5、數據關係

•數據庫表的關係影響服務粗粒度,服務之間的數據是否也可以拆分?

•假設服務底層數據並不共享,而是在每個服務內形成緊密的限界上下文,則適合拆解

•如果底層數據共享相同表,可能更適合一個服務

2.6、代碼變更&部署頻率

•代碼變更的頻率是服務分解的另一個考慮因素,如果服務之間變更頻率是一樣的,可能不適合拆分。

•如果一組服務總是一起更新,這可能表明它們是高度耦合的,應該作爲一個單獨的服務部署。

比如上面的【訂單時效E-mysql持久化】服務和【E-es持久化】服務變更頻率是一樣的,時效增加一個字段,用於下游考覈業績,es同樣也需要用於日常排查。

2.7、測試和監控

• 考慮服務劃分對測試策略和監控體系的影響。更細的服務粒度會帶來更復雜的鏈路監控和日誌管理需求。

•微服務的監控和日誌管理可能會變得複雜。如果服務拆分導致難以追蹤問題和性能監控,這可能是一個合併服務的理由。



從集成因素來看上面的promise服務粒度問題

Q: 關於持久化,目前包括兩個應用:E-mysql和E-es。是將它們設計爲一個服務更合理,還是作爲兩個獨立的服務? A: 綜合前述的分析,將這兩個應用設計成一個服務顯得更爲合理。這樣的設計可以充分利用兩者之間的協同效應,同時簡化系統架構,提高維護效率。通過統一的服務接口,我們可以更加靈活地管理數據持久化的過程,無論是對MySQL還是Elasticsearch的操作,都可以確保一致性和高效性。這種一體化的服務設計,既可以減少系統複雜性,也便於在需要時對服務進行擴展和優化,但需要做好es容錯性,即es有問題不能影響mysql業務。



四、總結

在微服務架構的世界裏,服務粒度的藝術不僅僅是技術上的劃分,更是對業務理解的體現,對系統複雜性的把控,對團隊協作和效率的考量。我們不能忽視服務粒度選擇對系統性能、可維護性和可擴展性的深遠影響。正如我們通過Promise系統的探索所見(Promise業務屬性是下單前商詳結算黃金鍊路、下單後訂單控制節奏,一切的拆分原則優先考慮高可用、高併發出發),恰當的服務粒度能夠帶來清晰的職責邊界,提升系統的響應性和可靠性,同時降低維護的複雜性和成本。



在決定服務如何拆分或合併時,我們不僅要考慮技術因素,還要考慮團隊結構、業務需求和未來的可擴展性。這需要我們深入理解業務邏輯,預見潛在的變化,並且勇於對現有架構進行調整。服務粒度的選擇不是一成不變的,而是隨着業務的發展和技術的進步而不斷演化的



我們不要簡單地追求服務數量的增加,而是追求每個服務能夠在正確的時間做正確的事情。通過精心設計服務粒度,構建出既強大又靈活的系統,這些系統能夠支持我們的業務成長,應對未來的挑戰。



如果您的團隊面臨服務氾濫,您會如何權衡服務的粒度呢?歡迎大家指正和完善,謝謝!




本文的觀點源自我在學習與實踐過程中的思考,尚處於不斷探索和驗證的階段。 希望能“拋磚引玉”,引發大家的思考與討論, 讓我們共同進步,從而優化我們的系統設計。

附:

1、軟件架構第一定律:軟件架構中的一切都是在權衡

作者:京東物流 馮志文

來源:京東雲開發者社區

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