客服系統微服務架構的演化

来源:http://www.cnblogs.com/wintersun/archive/2017/03/11/6534627.html
-Advertisement-
Play Games

微服務要求 服務協作 服務治理 服務治理 1 懷疑第三方 堅持一條信念:“所有第三方服務都不可靠”,不管第三方什麼天花亂墜的承諾。基於這樣的信念,我們需要有以下行動。 1.1 有兜底,制定好業務降級方案 如果第三方服務掛掉怎麼辦?我們業務也跟著掛掉?顯然這不是我們希望看到的結果,如... ...


 

image

微服務要求
image

image
image
image

服務協作


image

服務治理

image

服務治理
image
image
image

1 懷疑第三方

堅持一條信念:“所有第三方服務都不可靠”,不管第三方什麼天花亂墜的承諾。基於這樣的信念,我們需要有以下行動。

1.1 有兜底,制定好業務降級方案

如果第三方服務掛掉怎麼辦?我們業務也跟著掛掉?顯然這不是我們希望看到的結果,如果能制定好降級方案,那將大大提高服務的可靠性。舉幾個例子以便大家更好的理解。

比如我們做個性化推薦服務時,需要從用戶中心獲取用戶的個性化數據,以便代入到模型里進行打分排序,但如果用戶中心服務掛掉,我們獲取不到數據了,那麼就不推薦了?顯然不行,我們可以在cache里放置一份熱門商品以便兜底;

又比如做一個數據同步的服務,這個服務需要從第三方獲取最新的數據並更新到mysql中,恰好第三方提供了兩種方式:1)一種是消息通知服務,只發送變更後的數據;2)一種是http服務,需要我們自己主動調用獲取數據。我們一開始選擇消息同步的方式,因為實時性更高,但是之後就遭遇到消息遲遲發送不過來的問題,而且也沒什麼異常,等我們發現一天時間已過去,問題已然升級為故障。合理的方式應該兩個同步方案都使用,消息方式用於實時更新,http主動同步方式定時觸發(比如1小時)用於兜底,即使消息出了問題,通過主動同步也能保證一小時一更新。

有些時候第三方服務錶面看起來正常,但是返回的數據是被污染的,這時還有什麼方法兜底嗎?有人說這個時候除了通知第三方快速恢複數據,基本只能幹等了。舉個例子,我們做移動端的檢索服務,其中需要調用第三方介面獲取數據來構建倒排索引,如果第三方數據出錯,我們的索引也將出錯,繼而導致我們的檢索服務篩選出錯誤的內容。第三方服務恢複數據最快要半小時,我們構建索引也需要半小時,即可能有超過1個多小時的時間檢索服務將不能正常使用,這是不可接受的。如何兜底呢?我們採取的方法是每隔一段時間保存全量索引文件快照,一旦第三方數據源出現數據污染問題,我們先按下停止索引構建的開關,並快速回滾到早期正常的索引文件快照,這樣儘管數據不是很新(可能1小時之前),但是至少能保證檢索有結果,不至於對交易產生特別大的影響。

1.2 遵循快速失敗原則,一定要設置超時時間

某服務調用的一個第三方介面正常響應時間是50ms,某天該第三方介面出現問題,大約有15%的請求響應時間超過2s,沒過多久服務load飆高到10以上,響應時間也非常緩慢,即第三方服務將我們服務拖垮了。

為什麼會被拖垮?沒設置超時!我們採用的是同步調用方式,使用了一個線程池,該線程池裡最大線程數設置了50,如果所有線程都在忙,多餘的請求就放置在隊列里中。如果第三方介面響應時間都是50ms左右,那麼線程都能很快處理完自己手中的活,並接著處理下一個請求,但是不幸的是如果有一定比例的第三方介面響應時間為2s,那麼最後這50個線程都將被拖住,隊列將會堆積大量的請求,從而導致整體服務能力極大下降。正確的做法是和第三方商量確定個較短的超時時間比如200ms,這樣即使他們服務出現問題也不會對我們服務產生很大影響。

1.3 適當保護第三方,慎重選擇重試機制

需要結合自己的業務以及異常來仔細斟酌是否使用重試機制。又如有些異常是介面處理超時異常,這個時候就需要結合業務來判斷了,有些時候重試往往會給後方服務造成更大壓力,啟到雪上加霜的效果。

2 防備使用方

這裡又要堅持一條信念:“所有的使用方都不靠譜”,不管使用方什麼天花亂墜的保證。基於這樣的信念,我們需要有以下行動。     

2.1 設計一個好的api(RPC、Restful),避免誤用

如果你的介面讓很多人誤用,那要好好反思自己的介面設計了,介面設計雖然看著簡單,但是學問很深,建議大家好好看看Joshua Bloch的演講《How to Design a Good API & Why it Matters(如何設計一個好的API及為什麼這很重要)》以及《Java API 設計清單》。

a) 遵循介面最少暴露原則

使用方用多少介面我們就提供多少,因為提供的介面越多越容易出現亂用現象,言多必失嘛。此外介面暴露越多自己維護成本就越高。

b) 不要讓使用方做介面可以做的事情

如果使用方需要調用我們介面多次才能進行一個完整的操作,那麼這個介面設計就可能有問題。比如獲取數據的介面,如果僅僅提供getData(int id);介面,那麼使用方如果要一次性獲取20個數據,它就需要迴圈遍歷調用我們介面20次,不僅使用方性能很差,也無端增加了我們服務的壓力,這時提供getDataList(List<Integer> idList);介面顯然是必要的。

c避免長時間執行的介面

還是以獲取數據方法為例:getDataList(List<Integer> idList); 假設一個用戶一次傳1w個id進來,我們的服務估計沒個幾秒出不來結果,而且往往是超時的結果,用戶怎麼調用結果都是超時異常,那怎麼辦?限制長度,比如限制長度為100,即每次最多只能傳100個id,這樣就能避免長時間執行,如果用戶傳的id列表長度超過100就報異常。

加了這樣限制後,必須要讓使用方清晰地知道這個方法有此限制。之前就遇到誤用的情況,某用戶一個訂單買了超過100個商品,該訂單服務需要調用商品中心介面獲取該訂單下所有商品的信息,但是怎麼調用都失敗,而且異常也沒打出什麼有價值的信息,後來排查好久才得知是商品中心介面做了長度限制。

怎麼才能做到加了限制,又不讓用戶誤用呢?

兩種思路:1)介面幫用戶做了分割調用操作,比如用戶傳了1w個id,介面內部分割成100個id列表(每個長度100),然後迴圈調用,這樣對使用方屏蔽了內部機制,對使用方透明;2)讓用戶自己做分割,自己寫迴圈顯示調用,這樣需要讓用戶知道我們方法做了限制,具體方法有:1)改變方法名,比如getDataListWithLimitLength(List<Integer> idList); ;2)增加註釋;3)如果長度超過 100,很明確地拋出異常,很直白地進行告知。

d)參數易用原則

避免參數長度太長,一般超過3個後就較難使用,那有人說了我參數就是這麼多,那怎麼辦?寫個參數類嘛!

此外避免連續的同類型的參數,不然很容易誤用。能用其它類型如int等的儘量不要用String類型,這也是避免誤用的方法。

e)異常

介面應當最真實的反應出執行中的問題,更不能用聰明的代碼做某些特別處理。

2.2 流量控制,按服務分配流量,避免濫用

相信很多做過高併發服務的同學都碰到類似事件:某天A君突然發現自己的介面請求量突然漲到之前的10倍,沒多久該介面幾乎不可使用,並引發連鎖反應導致整個系統崩潰。

如何應對這種情況?生活給了我們答案:比如老式電閘都安裝了保險絲,一旦有人使用超大功率的設備,保險絲就會燒斷以保護各個電器不被強電流給燒壞。同理我們的介面也需要安裝上“保險絲”,以防止非預期的請求對系統壓力過大而引起的系統癱瘓,當流量過大時,可以採取拒絕或者引流等機制。具體限流演算法參見《介面限流實踐》一文。

3 做好自己

從需求分析、架構設計 、代碼編寫、測試、code review、上線、線上服務運維等階段都可以重點展開介紹,這次簡單分享下架構設計、代碼編寫上的幾條經驗原則。

3.1 單一職責原則

單一職責原則,在我們的需求分析、架構設計、編碼等各個階段都非常有指導意義。在需求分析階段,單一職責原則可以界定我們服務的邊界,如果服務邊界如果沒界定清楚,各種合理的不合理的需求都接,最後導致服務出現不可維護、不可擴展、故障不斷的悲哀結局。

對於架構來講,單一職責也非常重要。比如讀寫模塊放置在一起,導致讀服務抖動非常厲害,如果讀寫分離那將大大提高讀服務的穩定性(讀寫分離);比如一個服務上同時包含了訂單、搜索、推薦的介面,那麼如果推薦出了問題可能影響訂單的功能,那這個時候就可以將不同介面拆分為獨立服務,並獨立部署,這樣一個出問題也不會影響其他服務(資源隔離);又比如我們的圖片服務使用獨立功能變數名稱、並放置到cdn上,與其它服務獨立(動靜分離)。

從代碼角度上講,一個類只乾一件事情,如果你的類幹了多個事情,就要考慮將他分開。這樣做的好處是非常清晰,以後修改起來非常方便,對其它代碼的影響就很小。再細粒度看類里的方法,一個方法也只乾一個事情,即只有一個功能,如果乾兩件事情,那就把它分開,因為修改一個功能可能會影響到另一個功能。

3.2 控制資源的使用

寫代碼腦子一定要繃緊一根弦,認知到我們所在的機器資源是有限的。機器資源有哪些?cpu、記憶體、網路、磁碟等,如果不做好保護控制工作,一旦某一資源滿負荷,很容易導致出現線上問題。

3.2.1 CPU資源怎麼限制?

a)計算演算法優化

如果服務需要進行大量的計算,比如推薦排序服務,那麼務必對你的計算演算法進行優化,比如筆者曾經對地理空間距離計算這一重度使用的演算法進行了優化,取得了較好的效果,詳見《地理空間距離計算優化》一文。

b)鎖

對於很多服務而言,沒有那麼多耗費計算資源的演算法,但cpu使用率也很高,這個時候需要看看鎖的使用情況,我的建議是如無必要,儘量不用顯式使用鎖。

c) 習慣問題

比如寫迴圈的時候,千萬要檢查看看是否能正確退出,有些時候一不小心,在某些條件下就成為死迴圈,很著名的案例就是《多線程下HashMap的死迴圈問題》。比如集合遍歷時候使用性能較差的遍歷方式、String +檢查,如果有超過多個String相加,是否使用StringBuffer.append?

d)儘量使用線程池

通過線程池來限制線程的數目,避免線程過多造成的線程上下文切換的開銷。

e)jvm參數調優

jvm參數也會影響cpu的使用,如《發佈或重啟線上服務時抖動問題解決方案》。

3.2.2 記憶體資源怎麼限制?

a)Jvm參數設置

通過JVM參數的設置來限制記憶體使用,jvm參數調優比較靠經驗,有一篇朋友寫的好文可以參考《Linux與JVM的記憶體關係分析》。

b)初始化java集合類大小

使用java集合類的時候儘量初始化大小,在長連接服務等耗費記憶體資源的服務中這種優化非常重要;

c)使用記憶體池/對象池

d)使用線程池的時候一定要設置隊列的最大長度

之前看過好多起故障都是由於隊列最大長度沒有限制最後導致記憶體溢出。

e)如果數據較大避免使用本地緩存

     如果數據量較大,可以考慮放置到分散式緩存如redis、tair等,不然gc都可能把自己服務卡死;

f)對緩存數據進行壓縮

比如之前做推薦相關服務時,需要保存用戶偏好數據,如果直接保存可能有12G,後來採用短文本壓縮演算法直接壓縮到6G,不過這時一定要考慮好壓縮解壓縮演算法的cpu使用率、效率與壓縮率的平衡,一些壓縮率很高但是性能很差的演算法,也不適合線上實時調用。

有些時候直接使用probuf來序列化之後保存,這樣也能節省記憶體空間。

g)清楚第三方軟體實現細節,精確調優

在使用第三方軟體時,只有清楚細節後才知道怎麼節約記憶體,這點我在實際工作中深有體會,比如之前在閱讀過lucene的源碼後發現我們的索引文件原來是可以壓縮的,而這在說明文檔中都找不到,具體參考《lucene索引文件大小優化小結》一文。

3.2.3 網路資源怎麼限制?

a)減少調用的次數

減少調用的次數?經常看到有同學在迴圈里用redis/tair的get,如果意識到這裡面的網路開銷的話就應該使用批量處理;又如在推薦服務中經常遇到要去多個地方去取數據,一般採用多線程並行去取數據,這個時候不僅耗費cpu資源,也耗費網路資源,一種在實際中常常採用的方法就是先將很多數據離線存儲到一塊 ,這時候線上服務只要一個請求就能將所有數據獲取。

b)減少傳輸的數據量

一種方法是壓縮後傳輸,還有一種就是按需傳輸,比如經常遇到的getData(int id),如果我們返回該id對應的Data所有信息,一來人家不需要,二來數據量傳輸太大,這個時候可以改為getData(int id, List<String> fields),使用方傳輸相應的欄位過來,服務端只返回使用方需要的欄位即可。

3.2.4 磁碟資源怎麼限制?

     打日誌要控制量,並定期清理。1)只列印關鍵的異常日誌;2)對日誌大小進行監控報警。我有一次就遇到了第三方服務掛了,然後我這邊就不斷列印調用該第三方服務異常的日誌,本來我的服務有降級方案,如果第三方服務掛了會自動使用其它服務,但是突然收到報警說我服務掛了,登上機器一看才知道是磁碟不夠導致的崩潰;3)定期對日誌進行清理,比如用crontab,每隔幾天對日誌進行清理;4)列印日誌到遠端,對於一些比較重要的日誌可以直接將日誌列印到遠端HDFS文件系統里;

3.3 避免單點

不要把雞蛋放在一個籃子上!從大層次上講服務可以多機房部署、異地多活;從自己設計角度上講,服務應該能做到水平擴展。

對於很多無狀態的服務,通過nginx、zookeeper能輕鬆實現水平擴展;

對一些job類型的服務,怎麼避免單點呢,畢竟只能在一個節點上運行,可以參考《Quartz應用與集群原理分析》一文;

對數據服務來說,怎麼避免單點呢?簡而言之、可以通過分片、分層等方式來實現。


image
image
全棧小團隊

image
image
image
image
智能化平臺
image

image

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

希望對您系統架構,軟體項目開發,運維管理,系統架構與研發管理體系, 信息安全, 企業信息化等有幫助。 其它您可能感興趣的文章:
DevOps的基本原則與介紹
Docker與CI持續集成/CD
持續交付中高效率與高質量
持續集成CI與自動化測試
軟體研發工程基礎設施
容器化實踐金融業案例一
雲計算參考架構幾例
微服務與Docker介紹
互聯網直播平臺架構案例一
高可用架構案例一
某互聯網公司廣告平臺技術架構
某大型電商雲平臺實踐
雲計算參考架構幾例
移動應用App測試與質量管理一
全面的軟體測試
著名ERP廠商的SSO單點登錄解決方案介紹一
軟體項目風險管理介紹
企業項目化管理介紹
智能企業與信息化之一
由企業家基本素質想到的
敏捷軟體質量保證的方法與實踐
構建高效的研發與自動化運維
IT運維監控解決方案介紹
IT持續集成之質量管理
人才公司環境與企業文化
企業績效管理系統之平衡記分卡
企業文化、團隊文化與知識共用
高效能的團隊建設
餐飲連鎖公司IT信息化解決方案一

如有想瞭解更多軟體研發 , 系統 IT集成 , 企業信息化,項目管理,企業管理 等資訊,請關註我的微信訂閱號:

MegadotnetMicroMsg_thumb1_thumb1_thu[1]

 


作者:Petter Liu
出處:http://www.cnblogs.com/wintersun/
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。
該文章也同時發佈在我的獨立博客中-Petter Liu Blog


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 閉包是一個比較抽象的概念,尤其是對js新手來說.書上的解釋實在是比較晦澀,對我來說也是一樣. 但是他也是js能力提升中無法繞過的一環,幾乎每次面試必問的問題,因為在回答的時候.你的答案的深度,對術語的理解以及js內部解釋器的運作方式的描述,都是可以看出你js實際水平的.即使你沒答對,也能讓考官對你的 ...
  • 世界上本來沒有設計模式。用的人多了,也就成了設計模式。所以,我們不是嚴格按照它的定義去執行,可以根據自己的實際場景、需求去變通。領悟了其中的思想,實現屬於自己的設計模式。 你肯定有過這樣的體會。某某時候,聽人說起**模式。這麼牛逼,回去得看看。結果仔細一看原來自己早就是這麼用了,只是不知道它還有個這... ...
  • 一、Java的起源 最初是為家用電器設計的,因為其特點適合於internet, 於是通過internet成為一種計算語言,一個平臺,一個網路計算的架構。 二、Java平臺分類 ①JavaSE適用於普通PC及筆記本電腦,為其他JAVA程式的開發和運行提供了最基本的技術支持。 ②JAVAEE適用於企業級 ...
  • 武俠小說練功講究打通任督二脈。程式設計練到一定程度也講究打通任督二脈。好奇心強的同學可以搜搜“打通任督二脈有什麼感覺”。 spring的任督二脈ApplicationContext 最經典的任督二脈莫過於java中spring中的ApplicationContext。用慣spring的都會覺得,這裡 ...
  • 當我們在系統範圍內部署大量的微服務時,一個新的挑戰產生了,單體應用部署時不會發生。這篇文章將針對這些新的挑戰,在系統範圍內部署大量微服務時定義一套操作模型(operations model)。這篇文章分為如下幾個部分: 前提條件;擴展;問題;需要的組件;參考模型;下一步; ...
  • 什麼是白話? 今天我不陳述白話的又由來,也不陳述白話的概念,我們主要說白話到底是一個什麼東西,我認為的白話的呢,通俗的講就是大家都能聽的懂得,不會產生歧義的。此話怎麼講呢?比如“你吃了麽?”你絕對不會回答"我從北京來"。這是明顯的所問非所答。我所謂的白話,淺顯通俗,在語言上,生動、潑辣、粗獷、生活氣 ...
  • 序言 清風萬里的季節,周末本該和親人朋友一起消遣這爛漫的花花草草,或是懶洋洋的曬個太陽聽聽風聲鳥鳴。無奈工作使然,理想使然,我回到啦公司,敲起啦鍵盤,擼起啦代碼,程式狗的世界一片黯然,一片黯然,願天下所有努力的程式狗都夢想成真吧!! 回到正題,為什麼搭建rabbitmq集群?rabbitmq集群有那 ...
  • 一、為什麼需要線程模型? 記得幾年前,自己寫高精度演算法時,因為需要一個線程安全的後臺(用來保存一些信息),便手動寫了一個線程本地存儲(TLS)(雖然,後來因為改了計算模型,棄用了);再後來,因為記憶體池的需要,亦手動再寫了一個線程本地存儲(TLS);很好,這樣一來同一個庫里,竟然有兩套相同的TLS;於 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...