異地雙活實踐筆記

最近恰好在搞異地雙活,以下是一個梳理:

基本概念

1、異地容災。這僅僅是一個冷備的概念。也就是在平時正常的時候,另外一個機房只是當做備份。

2、異地雙(多)活。而異地雙(多)活,卻是指有兩個或者多個可以同時對外服務的節點,任意一個點掛了,也可以迅速切換到其他節點對外服務,節點之間的數據做到準實時同步。

分類

根據是否需要數據同步大體分爲三類:

1、必須同步型。(比如數據庫)

2、無須同步型。比如緩存,僅僅是當做緩存,就可以這樣做(這個有待商榷,其實緩存也需要同步的,嚴格來說的話)。

3、只能單活(對全局原子要求較高),不接受有一定時延的“不一致”窗口。

核心問題

     數據同步、網絡時延。

切換方式

1、自動切換。自動切換表現爲當災難來臨時,程序內部可以自動識別出問題然後切換至可用機房。

2、手動切換。通過簡單的配置,在幾分鐘或者一兩小時內切換到另外的機房。

異地多活面臨的挑戰

1、切換問題。

切換問題不僅僅是災難發生自動切換到好的機房,還有另外一個問題,就是災難機房恢復能力後,如何再切換回去,切換回去的數據同步問題又是需要解決的。

2、跨機房流量問題。

跨機房的流量是花錢的。所以不是無限大的。控制跨機房消息體大小,越小越好。然而,很多時候要想保證數據同步是一件很耗費流量的事情。但跨機房流量真的是一座山。

既然跨機房流量有限制,而且不穩定。所以有一種解決方案就是不跨機房。既然不跨機房就要做用戶分區,確保每個用戶只能訪問自己所在的區,這樣至少能保證該用戶自己的數據的完整。

3、所有的業務都適合做異地雙活嗎?

異地多活效果看起來很誘人,但如果不假思索貪大求全的要求所有業務都實現異地多活的話,就會把自己帶到坑裏去。

第一個原因是異地多活是有成本的,包括開發成本和維護成本。需要實現異地多活的業務越多,方案越複雜,投入的設計開發時間越多;同時維護成本也會越高,需要更多的機器,需要更多的帶寬。

第二個原因是有的業務理論上就無法實現異地多活。典型的有“餘額”和“庫存”這兩個業務。

以餘額爲例,假設我們實現了餘額的異地多活業務,用戶小明有10000塊錢,在A機房給女友轉賬了5000塊,還剩餘5000塊;如果此時A機房異常且數據還沒同步到B機房,小明登錄到B機房發現自己又有10000塊了,小明感覺中彩票了,趕緊又轉了10000塊給女友,最後出現了小明只有10000塊卻轉賬了15000塊的問題,對於和資金相關的業務,這樣的問題是絕對無法容忍的,哪怕一個用戶有問題都不行。

所以,異地多活也不能保證所有業務都異地多活,在設計異地多活方案的時候,需要從業務和用戶的角度出發,識別出核心和關鍵業務,明確哪些業務是必須實現異地多活,哪些是可以不實現異地多活,哪些是不能實現異地多活的。比如“登錄”必須實現異地多活、“註冊”和“修改用戶信息”不一定要實現異地多活。

4、冷備還是熱備。冷備了以後,一直冷備,當真正出現問題,你還有勇氣去切換到那個一直冷的機房嗎?恐怕需要點勇氣。

5、數據一致性問題。

解決方案:

(1)、守護進程同步。

(2)、客戶端雙寫。

(3)、不去解決。不需要解決的前提是用戶分區。分區後,從本質上說,其實是沒有做到雙活的。只是看起來一個業務確實被分到了多個機房而已。

6、讀取問題。

這個相對來說要好解決一些,就是就近讀取。

業務以及基礎組件異地雙活方案

業務實例異地雙活

業務實例的異地雙活。這個相對來說要簡單一些,只要做到無狀態,再如果通過docker這些容器結束,基本上是相對來說容易一些。

消息隊列的異地雙活

rabbitmq 每個機房部署一套server,然後每個機房的業務使用各自機房的mq。通過haproxy自動映射到本機房的broker。topic同步,通過rest api讀取到

配置文件,然後同步到另外一個機房的rabbitmq下。

具體restapi:

http://17X.XXX.X.XXX:15672/api/definitions

以上只是同步了rabbitmq的元數據,而且是全量同步。

消息同步問題:如果不同步會導致消息丟失。所以mq消息其實也是需要同步的。

同步可以通過客戶端雙寫,或者服務端複製。雙寫更加容易。

Redis的異地雙活

Redis 的異地雙活。就是分別在每個機房搭建一套Redis集羣。 然後每個機房的業務都去訪問各自機房的Redis集羣。

但這樣只是能保證基本的緩存能力。如果業務中涉及到一些全局的原子操作,那麼這樣的做法顯然不能滿足需求。

原因很簡單,比如你使用redis incr自增命令:

a 機房 加1 後變爲了1,b機房的業務也加1, 本來應該是2。結果由於各自都是訪問了自己的redis,所以全局計數顯然是有問題的。

怎麼解決呢?就需要涉及到全局操作的業務,去單獨的連接 一個全局的唯一的那個 redis集羣,這個redis集羣專門用於 業務的全局操作。

但這樣的後果,就是會涉及到跨機房流量問題,因爲這個全局的redis集羣無論放在哪個機房,另外一個機房的業務要想訪問都得跨機房。

 

讀取流程:

 

寫入流程:

 

全局操作:

數據庫的異地雙活
canal:

早期,阿里巴巴B2B公司因爲存在杭州和美國雙機房部署,存在跨機房同步的業務需求。不過早期的數據庫同步業務,主要是基於trigger的方式獲取增量變更,不過從2010年開始,阿里系公司開始逐步的嘗試基於數據庫的日誌解析,獲取增量變更進行同步,由此衍生出了增量訂閱&消費的業務,從此開啓了一段新紀元。

基於日誌增量訂閱&消費支持的業務:

  1. 數據庫鏡像
  2. 數據庫實時備份
  3. 多級索引 (賣家和買家各自分庫索引)
  4. search build
  5. 業務cache刷新
  6. 價格變化等重要業務消息

otter:

阿里巴巴B2B公司,因爲業務的特性,賣家主要集中在國內,買家主要集中在國外,所以衍生出了杭州和美國異地機房的需求,同時爲了提升用戶體驗,整個機房的架構爲雙A,兩邊均可寫,由此誕生了otter這樣一個產品。

otter第一版本可追溯到04~05年,此次外部開源的版本爲第4版,開發時間從2011年7月份一直持續到現在,目前阿里巴巴B2B內部的本地/異地機房的同步需求基本全上了otter4。

目前同步規模:

  1. 同步數據量6億
  2. 文件同步1.5TB(2000w張圖片)
  3. 涉及200+個數據庫實例之間的同步
  4. 80+臺機器的集羣規模

 

說明:

a. 數據涉及網絡傳輸,S/E/T/L幾個階段會分散在2個或者更多Node節點上,多個Node之間通過zookeeper進行協同工作 (一般是Select和Extract在一個機房的Node,Transform/Load落在另一個機房的Node)

b. node節點可以有failover / loadBalancer. (每個機房的Node節點,都可以是集羣,一臺或者多臺機器)

Zookeeper異地雙活

先來點背景知識:

1、zookeeper中的server機器之間會組成leader/follower集羣,1:n的關係。採用了paxos一致性算法保證了數據的一致性,就是leader/follower會採用通訊的方式進行投票來實現paxos。

2、zookeeper還支持一種observer模式,提供只讀服務不參與投票,提升系統。

異地多活決定了我們需要進行跨機房操作,比如杭州,美國,中國香港,青島等多個機房之間進行數據交互。

跨機房之間對應的網絡延遲都比較大,比如中美機房走海底光纜有ping操作200ms的延遲,杭州和青島機房有70ms的延遲。

爲了提升系統的網絡性能,在部署zookeeper網絡時會在每個機房部署節點,多個機房之間再組成一個大的網絡保證數據一致性。(zookeeper千萬別再搞多個集羣)。

最後的部署結構就會是:

  • 杭州機房 >=3臺 (構建leader/follower的zk集羣)
  • 青島機房 >=1臺 (構建observer的zk集羣)
  • 美國機房 >=1臺 (構建observer的zk集羣)
  • 中國香港機房 >=1臺 (構建observer的zk集羣)

 

一句話概括就是: 在單個機房內組成一個投票集羣,外圍的機房都會是一個observer集羣和投票集羣進行數據交互。 這樣部署的一些好處,大家可以細細體會一下。

針對這樣的部署結構,我們就可以實現就近訪問: 比如在美國機房的機器就去優先訪問本機房的zk集羣,訪問不到後纔去訪問杭州機房。

默認在zookeeper3.3.3的實現中,認爲所有的節點都是對等的。並沒有對應的優先集羣的概念,單個機器也沒有對應的優先級的概念。

擴展代碼:(比較暴力,採用反射的方式改變了zk client的集羣列表)

  • 先使用美國機房的集羣ip初始化一次zk client
  • 通過反射方式,強制在初始化後的zk client中的server列表中又加入杭州機房的機器列表

Java代碼

ZooKeeper zk = null;  
        try {
            zk = new ZooKeeper(cluster1, sessionTimeout, new AsyncWatcher() {

public void asyncProcess(WatchedEvent event) {
      //do nothing   
      }

      });
      if (serveraddrs.size() > 1) {
      // 強制的聲明accessible  
      ReflectionUtils.makeAccessible(clientCnxnField);
      ReflectionUtils.makeAccessible(serverAddrsField);
      // 添加第二組集羣列表  
      for (int i = 1; i < serveraddrs.size(); i++) {
      String cluster = serveraddrs.get(i);
      // 強制獲取zk中的地址信息  
      ClientCnxn cnxn = (ClientCnxn) ReflectionUtils.getField(clientCnxnField, zk);
      List<InetSocketAddress> serverAddrs = (List<InetSocketAddress>) ReflectionUtils
      .getField(serverAddrsField, cnxn);
      // 添加第二組集羣列表  
      serverAddrs.addAll(buildServerAddrs(cluster));
      }
      }
      }  

異地雙活思維誤區

異步雙活思維誤區:

1、所有業務異地多活!

2、實時一致性。我要所有數據都同步!

3、只使用存儲系統的同步功能!

4、我要保證業務100%可用!

5、所有用戶異地多活

一句話談“異地多活”

綜合前面的分析,異地多活設計的理念可以總結爲一句話:採用多種手段,保證絕大部分用戶的核心業務異地多活

選型時需要考慮的維度

以下是方案選型時需要考慮的一些維度:

  • 能否整業務遷移:如果機器資源不足,建議優先將一些體系獨立的服務整體遷移,這樣可以爲核心服務節省出大量的機架資源。如果這樣之後,機架資源仍然不足,再做異地多活部署。
  • 服務關聯是否複雜:如果服務關聯比較簡單,則單元化、基於跨機房消息同步的解決方案都可以採用。不管哪種方式,關聯的服務也都要做異地多活部署,以確保各個機房對關聯業務的請求都落在本機房內。
  • 是否方便對用戶分區:比如很多遊戲類、郵箱類服務,由於用戶可以很方便地分區,就非常適合單元化,而SNS類的產品因爲關係公用等問題不太適合單元化。
  • 謹慎挑選第二機房:儘量挑選離主機房較近(網絡延時在10ms以內)且專線質量好的機房做第二中心。這樣大多數的小服務依賴問題都可以簡化掉,可以集中精力處理核心業務的異地多活問題。同時,專線的成本佔比也比較小。以北京爲例,做異地多活建議選擇天津、內蒙古、山西等地的機房。
  • 控制部署規模:在數據層自身支持跨機房服務之前,不建議部署超過兩個的機房。因爲異地兩個機房,異地容災的目的已經達成,且服務器規模足夠大,各種配套的設施也會比較健全,運維成本也相對可控。當擴展到三個點之後,新機房基礎設施磨合、運維決策的成本等都會大幅增加。
  • 消息同步服務化:建議擴展各自的消息服務,從中間件或者服務層面直接支持跨機房消息同步,將消息體大小控制在10k以下,跨機房消息同步的性能和成本都比較可控。機房間的數據一致性只通過消息同步服務解決,機房內部解決緩存等與消息的一致性問題。跨機房消息同步的核心點在於消息不能丟,微博由於使用的是MCQ,通過本地寫遠程讀的方式,可以很方便的實現高效穩定的跨機房消息同步。

人們的總結以及經驗

1 、如果業務量不大,沒必要做異地多活,因爲異地多活需要的運維資源成本、開發成本都非常高; 2 、注意機房間的延時問題,延時大的可能達到100ms以上,如果業務需要多次跨機房請求應用的話,延遲的問題會徹底放大; 3 、跨機房的專線很大概率會出問題,要做好運維或者程序層面的容錯; 4 、不能依賴MySQL雙寫,必須有適應自身業務的跨機房消息同步方案; 5 、MySQL或者其他存儲的數據同步問題,在高延時和較差的網絡質量的情況下,考慮如何保證同步質量; 6、 考慮使用docker等容器虛擬化技術,提高動態調度能力;7、Devops流程配套。devops要支持推送到兩個機房。

真實的異地雙活案例:

5.27日下午17時許,支付寶被反映故障;18時許,支付寶通過官方微博給出迴應,解釋是因爲電信運營商光纖被挖斷。19時許,支付寶服務恢復正常。22時許,支付寶官方微博正式迴應復原了整個事件。   圍繞整個事件有很多討論,討論的中心最主要的有兩點:“爲什麼光纖被挖斷,會造成整個機房癱瘓”、“爲什麼支付寶的業務恢復用了兩個小時”。   其中,第一個問題,應該是電信運營商的光纖災備出現問題。第二個焦點問題“爲什麼支付寶用了2個小時恢復了業務”,一堆所謂“業內人士”衆說紛紜。其實,這應該是中國金融史上,首次完全意義的災難成功切換案例。   在此之前,中國金融行業投入重金建設的災備系統基本上有這麼兩類用武之地(一般來說,增建一個災備數據中心的建設成本是單數據中心成本的1.1-1.2倍):   1,計劃內災備切換演習,全副武裝、如臨大敵、不開一槍、全身而退。   2,因系統升級造成的被動災備切換。   例如2013年鬧得沸沸揚揚的某行DB2升級造成的系統回滾切換。萬幸的是,這是發生在凌晨的系統升級故障,當時沒有實時交易發生;某行也準備了各種應急預案,只是恢復的時間超出了計劃,網點推遲了一個小時開業而已;而另一家西部的區域銀行就沒有這麼強的科技實力了,同樣是DB2升級失敗,系統恢復時間用了37小時40分鐘(37小時啊,吼吼,坐火車都到莫斯科了)   像支付寶這種突發情形下的災備切換還真是頭一遭,而且居然成功了。支付寶雖然運氣差了點,但技術能力還真不是一般金融機構能拼的。   在支付寶微博答覆中,有一個新名詞——“異地多活”。在傳統了災備方案中,一般提的都是同城災備、異地災備、兩地三中心。與傳統的災備技術相比,異地多活的特點是:在不同地點的數據中心都可以同時支持業務,而且每個地點發生的交易都是真實業務流量,而不是常見的一主一備,如果主中心沒有問題,備份中心永遠都是“備胎”。   這種多活數據中心的好處是:因爲所有的數據中心都在支持交易,所以能節約IT成本;另外傳統方式中備份系統都不在真實的交易活動狀態,所以很難判斷它的狀態到底怎麼樣,在出現問題時,都不一定敢切過去。   大規模的“異地多活”,據說目前全球除了阿里能做到,也就Google和Facebook實現了,還是非金融類的業務。中國銀行業,只有某國有大行在去年6月份實現了上海同城兩個數據中心的雙活,是“同城雙活”,還沒有實現“異地多活”,而且在災難真正發生時,切換效果如何,還有待驗證。   昨天是支付寶“異地雙活”第一次真刀實槍的上戰場,支付寶因爲要滿足金融行業的很多要求,特別是對交易一致性、數據完整性等方面的要求,目前還處於小範圍試用階段,沒有全體上線,例如昨天杭州機房癱瘓後,有一部分流量跑在支付寶異地機房。因此,在昨天支付寶2小時整體恢復之前,並不是所有交易都停止的,並且基於“異地多活”技術,實現了這部分用戶的無感知切換。   對另外沒有通過“異地多活”技術切換的交易流量,支付寶選擇了最穩妥的做法:首先進行了完整的數據校驗,保證所有客戶的客戶信息、賬戶信息、資金信息、交易信息都是正確的,一切確認完成後,才重新“開門迎客”。這個過程耗時了一個多小時,不過相比較支付寶數億客戶所對應的校對數據量,這個時間還是可以接受的。   側面印證切換效果的是:被挖斷的光纖修到半夜才恢復,而支付寶的業務在晚間19點多恢復正常。   客觀來講,支付寶的這次表現,是一次說不上完美、但很成功的真實災難切換,也是中國金融史上第一次在完全突發情形下,成功完成切換的真實案例。整個切換過程中,沒有一條客戶數據丟失,也體現了金融級的數據高可用要求,雖然切換的時間對用戶來說長了點,但“就像是一次跳水,整體完成的質量很高,只是落水時水花沒有壓好,水花稍微大了點。”   估計經過這次折騰,支付寶全盤推進“異地多活”的速度會加快,可能在今年七八月份實現。

文章來源:https://cloud.tencent.com/developer/article/1082855

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