來想象這樣一個場景:你的垂直電商系統(tǒng)部署的 IDC 機房,在某一天發(fā)布了公告說,機房會在第二天凌晨做一次網(wǎng)絡設備的割接,在割接過程中會不定時出現(xiàn)瞬間或短時間網(wǎng)絡中斷。
機房網(wǎng)絡的中斷肯定會對業(yè)務造成不利的影響,即使割接的時間在凌晨(業(yè)務的低峰期),作為技術負責人的你,也要盡量思考方案來規(guī)避隔離的影響。然而不幸的是,在現(xiàn)有的技術架構下,電商業(yè)務全都部署在一個 IDC 機房中,你并沒有好的解決辦法。
而 IDC 機房的可用性問題是整個系統(tǒng)的阿喀琉斯之踵,一旦 IDC 機房像一些大廠一樣出現(xiàn)很嚴重的問題,就會對整體服務的可用性造成嚴重的影響。比如:
而目前,單一機房部署的架構特點決定了你的系統(tǒng)可用性受制于機房的可用性,也就是機房掌控了系統(tǒng)的生命線。所以,你開始思考如何通過架構的改造進一步提升系統(tǒng)的可用性。在網(wǎng)上搜索解決方案和學習一些大廠的經(jīng)驗后,你發(fā)現(xiàn)“多機房部署”可以解決這個問題。
多機房部署的難點是什么
多機房部署的含義是:在不同的 IDC 機房中部署多套服務,這些服務共享同一份業(yè)務數(shù)據(jù),并且都可以承接來自用戶的流量。
這樣,當其中某一個機房出現(xiàn)網(wǎng)絡故障、火災,甚至整個城市發(fā)生地震、洪水等大的不可抗的災難時,你可以隨時將用戶的流量切換到其它地域的機房中,從而保證系統(tǒng)可以不間斷地持續(xù)運行。這種架構聽起來非常美好,但是在實現(xiàn)上卻是非常復雜和困難的,那么它復雜在哪兒呢?
假如我們有兩個機房 A 和 B 都部署了應用服務,數(shù)據(jù)庫的主庫和從庫部署在 A 機房,那么機房 B 的應用如何訪問到數(shù)據(jù)呢?有兩種思路。
一個思路是直接跨機房讀取 A 機房的從庫:

另一個思路是在機房 B 部署一個從庫,跨機房同步主庫的數(shù)據(jù),然后機房 B 的應用就可以讀取這個從庫的數(shù)據(jù)了:

無論是哪一種思路,都涉及到跨機房的數(shù)據(jù)傳輸,這就對機房之間延遲情況有比較高的要求了。而機房之間的延遲和機房之間的距離息息相關,你可以記住幾個數(shù)字。
- 北京同地雙機房之間的專線延遲一般在 1ms~3ms。
這個延遲會造成怎樣的影響呢?要知道,我們的接口響應時間需要控制在 200ms 之內(nèi),而一個接口可能會調用幾次第三方 HTTP 服務或者 RPC 服務。如果這些服務部署在異地機房,那么接口響應時間就會增加幾毫秒,是可以接受的。
一次接口可能會涉及幾次的數(shù)據(jù)庫寫入,那么如果數(shù)據(jù)庫主庫在異地機房,那么接口的響應時間也會因為寫入異地機房的主庫,增加幾毫秒到十幾毫秒,也是可以接受的。
但是,接口讀取緩存和數(shù)據(jù)庫的數(shù)量可能會達到十幾次甚至幾十次,那么這就會增加幾十毫秒甚至上百毫秒的延遲,就不能接受了。
- 國內(nèi)異地雙機房之間的專線延遲會在 50ms 之內(nèi)。
具體的延遲數(shù)據(jù)依據(jù)距離的不同而不同。比如,北京到天津的專線延遲會在 10ms 之內(nèi);而北京到上海的延遲就會提高到接近 30ms;如果想要在北京和廣州部署雙機房,那么延遲就會到達 50ms 了。在這個延遲數(shù)據(jù)下,要想保證接口的響應時間在 200ms 之內(nèi),就要盡量減少跨機房的服務調用,更要避免跨機房的數(shù)據(jù)庫和緩存操作了。
- 如果你的業(yè)務是國際化的服務,需要部署跨國的雙機房,那么機房之間的延遲就更高了,依據(jù)各大云廠商的數(shù)據(jù)來看,比如,從國內(nèi)想要訪問部署在美國西海岸的服務,這個延遲會在 100ms~200ms 左右。在這個延遲下,就要避免數(shù)據(jù)跨機房同步調用,而只做異步的數(shù)據(jù)同步。
如果你正在考慮多機房部署的架構,那么這些數(shù)字都是至關重要的基礎數(shù)據(jù),你需要牢牢記住,避免出現(xiàn)跨機房訪問數(shù)據(jù)造成性能衰減問題。
機房之間的數(shù)據(jù)延遲在客觀上是存在的,你沒有辦法改變。你可以做的,就是盡量避免數(shù)據(jù)延遲對于接口響應時間的影響。那么在數(shù)據(jù)延遲下,你要如何設計多機房部署的方案呢?
逐步迭代多機房部署方案
-
同城雙活
制定多機房部署的方案不是一蹴而就的,而是不斷迭代發(fā)展的。我在上面提到,同城機房之間的延時在 1ms~3ms 左右,對于跨機房調用的容忍度比較高,所以,這種同城雙活的方案復雜度會比較低。
但是,它只能做到機房級別的容災,無法做到城市級別的容災。不過,相比于城市發(fā)生地震、洪水等自然災害來說,機房網(wǎng)絡故障、掉電出現(xiàn)的概率要大得多。所以,如果你的系統(tǒng)不需要考慮城市級別的容災,一般做到同城雙活就足夠了。那么,同城雙活的方案要如何設計呢?
假設這樣的場景:你在北京有 A 和 B 兩個機房,A 是聯(lián)通的機房,B 是電信的機房,機房之間以專線連接,方案設計時,核心思想是盡量避免跨機房的調用。具體方案如下。
- 首先,數(shù)據(jù)庫的主庫可以部署在一個機房中,比如部署在 A 機房中,那么 A 和 B 機房數(shù)據(jù)都會被寫入到 A 機房中。然后,在 A、B 兩個機房中各部署一個從庫,通過主從復制的方式,從主庫中同步數(shù)據(jù),這樣雙機房的查詢請求可以查詢本機房的從庫。一旦 A 機房發(fā)生故障,可以通過主從切換的方式將 B 機房的從庫提升為主庫,達到容災的目的
- 緩存也可以部署在兩個機房中,查詢請求也讀取本機房的緩存,如果緩存中數(shù)據(jù)不存在,就穿透到本機房的從庫中加載數(shù)據(jù)。數(shù)據(jù)的更新可以更新雙機房中的數(shù)據(jù),保證數(shù)據(jù)的一致性。
- 不同機房的 RPC 服務會向注冊中心注冊不同的服務組,而不同機房的 RPC 客戶端,也就是 Web 服務,只訂閱同機房的 RPC 服務組,這樣就可以實現(xiàn) RPC 調用盡量發(fā)生在本機房內(nèi),避免跨機房的 RPC 調用。

你的系統(tǒng)肯定會依賴公司內(nèi)的其他服務,比如審核、搜索等服務,如果這些服務也是雙機房部署的,那么也需要盡量保證只調用本機房的服務,降低調用的延遲。
使用了同城雙活架構之后,可以實現(xiàn)機房級別的容災,服務的部署也能夠突破單一機房的限制。但是,還是會存在跨機房寫數(shù)據(jù)的問題,不過由于寫數(shù)據(jù)的請求量不高,所以在性能上是可以容忍的。
-
異地多活
上面這個方案足夠應對你目前的需要,但是,你的業(yè)務是不斷發(fā)展的,如果有朝一日,你的電商系統(tǒng)的流量達到了京東或者淘寶的級別,那么你就要考慮即使機房所在的城市發(fā)生重大的自然災害,也要保證系統(tǒng)的可用性。而這時,你需要采用異地多活的方案(據(jù)我所知,阿里和餓了么采用的都是異地多活的方案)。
所以,在數(shù)據(jù)寫入時,你要保證只寫本機房的數(shù)據(jù)存儲服務再采取數(shù)據(jù)同步的方案,將數(shù)據(jù)同步到異地機房中。一般來說,數(shù)據(jù)同步的方案有兩種:
一種基于存儲系統(tǒng)的主從復制,比如 MySQL 和 Redis。也就是在一個機房部署主庫,在異地機房部署從庫,兩者同步主從復制實現(xiàn)數(shù)據(jù)的同步。
另一種是基于消息隊列的方式。一個機房產(chǎn)生寫入請求后,會寫一條消息到消息隊列,另一個機房的應用消費這條消息后再執(zhí)行業(yè)務處理邏輯,寫入到存儲服務中。

總結
不同機房的數(shù)據(jù)傳輸延遲是造成多機房部署的主要原因,你需要知道,同城多級房的延遲一般 在 1-3ms,異地機房的延遲一般在 50ms 以下 ,而跨國機房一般在200ms以下。
同城多機房可以允許有數(shù)據(jù)跨機房寫入的情況,但是數(shù)據(jù)的讀取和服務的調用盡量保證在同一個機房。
異地多活方案 應該盡量避免跨機房同步數(shù)據(jù)的讀取和寫入操作,而是采用異步的方式,將數(shù)據(jù)從一個機房同步到另一個機房。
多級房部署是業(yè)務發(fā)展到一定階段考慮采用的方案,對于機房容災有需求時才考慮的方案,能不做盡量不要去做。一旦決定采用跨機房,首先考慮的就是同城跨機房的部署,這種方案 會簡單的多,而異地多機房部署相對來說就要復雜的多了。
盲目的追求架構的先進性 只能給系統(tǒng)帶來更加的復雜,造成運維成本的上升。從而帶來維護的不便。