聊聊服務災備

2018年,有半年的時間在做服務災備,由於當時對這一塊的知識掌握得比較零碎,直接上手實踐,沒有較系統地學習,在後續的工作中,通過不斷實踐+學習補充這一塊的知識,以及反思當時的實踐,逐漸明白了要做災備的原因和這麼做的理由。在此寫下自己的小小總結。

爲什麼要做災備?

當時開始要做災備的原因,是因爲有一次機房A故障了,當時大部分的服務都不可以用:時長上漲、接口失敗,原因是:

1、很多服務都部署到A機房了,導致大部分服務不可用

2、服務依賴的數據服務(MySQL、Redis)是單點

出現的問題表現是:時長上漲和接口失敗,導致了頁面不可用、服務受損。

這個問題的根本原因是出現服務單點的情況,沒有備用的服務可以切換,導致請求/服務上游一直等待,等待一定時間後,就失敗了。

知道問題的根本原因後,解決問題的核心方向就是解決單點問題,解決單點問題的方案有:服務冗餘(多一份可用的服務),做災備。

什麼是災備

災備,簡單點說,就是生產環境上部署的服務,假如有一個服務(集羣)掛了,有另一個地方的同一個服務(集羣)可以繼續使用。

災備分主備和雙活兩種部署。假設有兩個機房A、B。

主備:大部分流量都會到主集羣A上,當A掛了,備點B能承擔主集羣的角色;

雙活:流量會平均分配到A、B兩個機房,兩個機房都能正常對外服務。v

如何做一個合理的災備

怎麼去做一個合理的災備呢?

筆者結合自己的工作經歷及理論知識,覺得做災備主要是以下的幾點,如果還有其他遺漏的,還望各位指正。

一、業務梳理

個人覺得,對於業務方來說,做一個應用的災備最重要的一點就是業務梳理。理由如下:

1、達到需要做災備的業務,通常都是存活了有一定時間的業務,這些業務都會由於各種因素而有一些在做災備時覺得不合理的設計,簡稱歷史原因。這些歷史原因有:依賴的服務單點;依賴的數據存儲系統單點;依賴的服務無法做災備等等。這些原因,如果沒有解決完,那麼業務方也無法完成災備。

2、不熟悉業務,對裏面的邏輯不清楚,就不知道如果服務異常會導致什麼問題發生,貿然去做災備,等到真正有異常時,可能會發現沒有達到預期的效果。

業務梳理,需要檢查以下幾個要點:

1、業務有多少個依賴服務?依賴服務是否還有其他的依賴?

2、依賴服務的災備情況如何?雙活還是單點?

3、依賴服務是否支持重試?重試失敗怎麼處理?

4、業務使用了什麼數據存儲系統?部署情況如何?純DB還是有Redis?主從還是多主?是否支持自動切換主庫?

5、業務用到的數據存儲系統的災備情況如何?是否滿足災備?是否支持分佈式?

6、依賴的服務是否可降級?降級是否可以返回默認值?返回默認值對業務是否有損?

7、依賴服務多次重試依然失敗,是否可以熔斷?熔斷對業務是否有損?

業務梳理完成之後,再根據對應不滿足的點去完成,直到所有情況都考慮完成了或者使用折中的方案來解決。

二、負載均衡

負載均衡的意思是將流量負載分佈到多臺服務器,從而提高程序的性能和可靠性。通過負載均衡技術,可以分發集中的流量,可以解決兩種情況:

1、流量暴漲,所有流量到一臺機器,將應用拖垮

2、其中一個集羣的所有應用掛了,可以將流量轉發到另一個集羣

注:在筆者實踐負載均衡的經歷中,使用到最多的就是nginx的負載均衡配置,將多個集羣的機器添加到nginx配置的upstream中,nginx會根據配置文件中指定的策略來分發流量。

三、服務降級

服務降級:簡單地說,就是如果服務異常,停掉不重要的服務,只返回部分數據。

比如說,一個用戶信息接口,包含以下三個字段:

{
    "id": 111,
    "nickName": "hhq",
    "userLogo": "https://www.test.com/test.jpg"
}

如果頭像暫時獲取失敗,如果返回默認頭像用戶可以接受,那麼就降級返回默認的頭像,這樣既不會使得整個接口失敗導致無法進行後續的操作,也不會影響用戶體驗。

四、服務熔斷

熔斷:這個概念參考電路的保險絲,如果電力負載過高,達到保險絲熔斷,保險絲就會自身熔斷切斷電源,保護電路安全運行。而互聯網中的服務熔斷,是指依賴服務由於各種因素變得不可用或者響應過慢,業務方爲了整個服務的穩定性,不再繼續調用目標服務,直接返回,如果依賴服務恢復了,則恢復調用。

注意,熔斷和降級看似很相似,但卻是不一樣的概念,應該理解爲從屬關係:

1、服務降級有多種降級方式,如限流降級、熔斷降級

2、熔斷是降級的其中一種方式

在熔斷降級的實踐中,筆者用到最多的是Hystrix。

五、服務發現

服務發現:自動檢測一個計算機網絡內的設備機器提供的服務。

服務發現有一個服務中間者維護服務與業務方之間的關係,服務將地址註冊到服務中間者,業務方從服務中介中查找需要調用的服務的地址。

上面提到,最初開始做災備是通過nginx的負載均衡來實現,這種方式在服務部署和擴容時需要修改配置文件,需要自己維護網絡中的機器,一旦不小心配置錯誤,整個服務就崩了。如果使用服務發現,由服務發現的中介維護服務地址,配置時只需要知道服務發現的域名和服務名稱即可,不需要關心具體的機器是哪一些。

實踐過程中,用到的服務發現組件有:Zuul和spring-cloud。

六、演練

如果以上的步驟都完成了,那麼就完成災備了嗎?並不是的。

現實情況下,很多時候是因爲出現了單點故障,纔會想到要去做災備。或者其他服務出現了故障,自身的服務也要檢查並完成災備。那麼怎麼檢查自己的服務已經完成災備了呢?總不可能等待下次的故障到來纔去驗證。這種情況下,需要多做服務的災備演練,根據已做的災備要點,逐步演練,如果發現遺漏點,重新梳理,繼續業務災備,重新演練。不斷循環,直到隨時演練都能快速恢復並最小地影響業務或者業務完全無感知纔算完成了災備。

踩過的坑

以上的這些理論是多次反覆實踐得出的總結,以筆者自己做災備的經歷,給大家分享我遇到過的兩個比較大的坑。

真的只是重試就完了嗎?流量暴漲,拖垮服務

接口A,依賴服務B,B依賴服務C,部署情況如下:

當時做了雙活+網關重試+負載均衡的部署,出現的情況是B->C超時,導致A接口響應太慢,這裏B->C有兩次重試,A->B也有兩次重試,接口超時時間太長,網關判斷接口失敗,於是也做了兩次重試,最終的結果是,同一個接口,有2*2*2=8倍的流量,導致服務C的請求量暴漲,於是將服務C的進程池耗盡,服務499了,最終接口A一直都是失敗,直到B->C之間的網絡恢復才正常。

這次的故障得出的結論是:

1、重試不能單純加上就完事了,需要看下游的依賴是否滿足重試

2、重試多次失敗後就需要加熔斷降級

3、重要的接口,除了重試以外,還可以做部分數據降級提高接口高可用性

機房服務“孤島”

有接口A,B、C兩個服務,A-B之間通過外網相連,B-C之間通過內網相連。異常情況是B-C之間網絡不通,外網流量通過接口A進入到B,B依賴C,但是B-C之間不通,B調用C會不斷重試,直到全部重試都失敗了,纔會返回網絡錯誤。這樣一來,接口A並不知道B服務失敗,用戶側體驗是一直等待,然後顯示失敗。理想的做法是希望能在B-C網絡不通的情況下將後續到來的流量拒絕掉,快速響應失敗的結果。

要做到這一點,就需要讓服務B“自殺”,如果應用側發現B-C之間的網絡出現異常,就讓B返回失敗錯誤碼,不再進行重試。

需要注意的點

不能脫離業務

衆所周知,開發大部分的時間都需要趕需求,一方面需求多到無法擠出時間完成災備的任務,另一方面災備工作如果不完成,出現故障之後就會影響業務了。因此通常會將這類需求當作技術需求來完成,業務開發人員沒有時間完成災備工作時,就會讓一些負責技術需求的開發來直接完成災備。上面提到,完成災備最重要的一點就是需要梳理業務,如果由一個完成不懂該業務的開發去完成災備,那麼至少需要花1-2天去閱讀代碼,理清業務邏輯和列出可能出現的坑才能完成這份工作。但是筆者覺得這樣做的效率是比較低的。首先,人無完人,雖然代碼大家都能看懂,但是由一個未參與過業務的開發重新梳理,難免會有遺漏的地方(即使問相應的業務開發,也有可能會遺漏);其次,重新熟悉業務也需要投入一定的時間。

所以,做災備是不能脫離業務的,應該給業務開發騰出相應的時間完成服務災備,提高服務穩定性,這對業務而言,出現故障時不影響用戶的使用,用戶無感知,就是提高用戶的體驗。

自動化運維

在筆者的災備經歷中,如果機器出現故障/機房故障/流量暴漲,都需要運維和相應的業務開發人工介入判斷是否需要擴容或摘除機器。但是人的判讀是主觀的,對是否需要擴容以及機器的選擇可能會有判斷出錯的時候,災備的工作,如果能結合現在較成熟k8s進行自動化運維,那麼將達到事半功倍的效果。

總結

災備,對於服務穩定性而言十分重要,但是也不是一朝一夕能完成的。個人覺得核心要點就是盡最大努力消除單點故障:服務單點、數據系統單點等等。

以上的文字理論僅僅是筆者經歷過的小小總結,也許仍未做到最好的災備級別,還需要日後不斷實踐來提升這部分的知識,如果有說錯的地方,還望各位指正。

原創文章,文筆有限,才疏學淺,文中若有不正之處,萬望告知。

如果本文對你有幫助,請點個贊吧,謝謝^_^

更多精彩內容,請關注個人公衆號。

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