
釘釘單元化從2018年開始到今年已經是第五個年頭了,五年的時間,釘釘單元化迭代了三個版本,從最初的毛頭小子,到達今年已經小有成就,今天想借這個場來和大家分享我們單元化的心路歷程和一些最佳實踐,本文要分享的內容只涉及部分內容,無法做到面面俱到,主要是想在同路人中形成共鳴,進而能復用一些架構或者系統,在我們單元化建設程序中,除了網上僅有的文章外,其可以直接使用的系統乏善可陳,使我們不得不從最基礎的系統開始,極大的影響建設效率,幸運最近幾年云原生技術的興起,讓我們能復用很多基礎設施,進而快速的提升我們單元化能力,助力釘釘的發展,
2018年,部分大客戶出于法律政策、商業機密資料存盤的要求,要求釘釘的資料存盤、訪問接入、服務部署需要在其信任的區域內,既需要滿足其資料存盤私有化要求,同時需要滿足跨地區網路的rt性能要求,結合阿里云機房部署位置、物理距離、用戶資料安全等方面出發,釘釘在客戶的阿里云機房內建設了一個單元,將通訊錄、IM資訊等企業資料單獨存盤在客戶機房,
我們通過一條專線,將兩個機房邏輯串聯到一起,內部通過DMB/DMR系統,實作了請求互通,釘釘單元化1.0比較簡單,純粹是業務驅動,和支付寶單元化建設的初衷:容災驅動有較大區別,兩個站點通過UID分段,將用戶劃分為中心用戶和專有用戶,上圖只是一個簡化的邏輯結構,內部實作遠比上圖復雜,但是1.0建設主要是從0到1,和大多數異地多活的系統相似,簡單和大家分享,
2020年是一個特殊的年份,由于疫情的原因,帶給大家非常多的改變,其中也包括釘釘,由于在線辦公與教育流量的突增,開年第一天上班就給釘釘一個下馬威,平峰的流量已經和除夕跨年的持平,但是和除夕不同的是這個流量是持續的,即使節前準備了三倍容量,也抵擋不住流量對系統的沖擊,只能借助阿里云的能力,不斷的擴容,
但是每天將近30%的流量增幅,單純的擴容也能難保障服務的連續性,最終也遇到了擴無可擴的場景,張北機房沒有機位了,有機器資源但是沒有機位讓我們有力無處使,我們不得不不斷進行系統優化,同時借助限流、降級、雙推等措施,勉強抗住了流量的最高峰,
疫情之前,我們一直在做高可用,但是這個高可用主要集中在容災機制上,比如搭建容災單元,如同支付寶一樣,是因為當時光纖被挖斷;又比如銀行的兩地三中心架構,是擔心某一個地域由于天災或者戰爭導致資料丟失,疫情的流量給我們上了一課,僅僅關注容災是不夠的,特別是釘釘的DAU從千萬走向億級別之后,更需要在容量上做出提前規劃,正因如此,我們認為“容量架構不是設計出來而是真真切切被逼出來的”,所以容量架構就成為我們單元化核心要素之一,
容量架構是將流量劃分到不同單元,每個單元承載各自的流量;容災架構是單元例外時,能保障核心的能力可用,也可以將流量動態調度到別的單元,實作服務的快速恢復,因此釘釘單元化進入了2.0時代,專注于容量和容災的建設,
要實作流量的劃分,必然要基于一個維度進行劃分,一部分到A單元,一部分到B單元,釘釘單元化架構也是參考了淘系和支付寶的單元化架構,前兩者都是基于UID劃分,釘釘單元化的第一個版本其實也是一樣的,基于UID做拆分,但是當我們設計容量架構時,發現基于UID劃分無法解決我們的容量問題,以IM為例,一條訊息其實屬于聊天雙方的,群聊亦是如此,用戶能和任意一個人聊天,這樣我們根本無法找到一個切入點來劃分流量,強行按照UID拆分,必然導致一個用戶的訊息出現在N個單元,單元的自封閉就無法做了,也有同學會說,為什么訊息不按照每個人存盤,這不就能按照UID劃分了嗎?結論是不行,首先這個訊息變成了寫擴散,持久化的時候會變成多單元寫,其次是成本翻倍,在DTIM這種過億規模的場景這條路走不通,這里可以多說一點,因為這個觀點來之不易,大家都知道,人是有慣性的,既然淘寶、支付寶甚至是微信都是UID劃分,為什么釘釘要特立獨行,當時我們團隊受到了絕大部分釘釘技術團隊的挑戰,持續長達將近一個月的技術選型的“爭吵”,最侄訓是達成了一致意見,
DTIM有主要的3個維度,分別是UID、會話(CID)、訊息,其中會話和訊息是系結的,而系統中最大量的是訊息,按照第一性原則來看,一定要將訊息劃分開來,才能做到將容量劃分開來的效果,我們再來看看音視頻,是按照房間維度組織流量和資料的,和IM又完全不同;同樣,檔案其實更適合按照企業維度來劃分,不同的業務擁有不同的維度,因此我們認為單元化最重要的找到自身“最大”的業務維度,將這個維護拆分,才能實作單元的橫向擴展,我們稱之為“業務路由”,回頭來看,我們之前其實是進入了思考誤區,以為淘系和支付寶都是UID維度,我們也要這個維度,其實UID正是前者的業務維度,比如訂單,也是圍繞用戶,并不會有交集的情況,會話就是IM的劃分維度,因此做單元化之前要先找到屬于自己的業務維度,
UID路由有個最大的好處,就是可以按照UID分段,能實作高效的靜態路由,也不用擔心多單元之間的一致性問題,但是這種分段路由局限性也比較明顯,需要預先分配,單元之間動態調度流量和資料成本極高,而且只能支持這種數值+順序的場景,在釘釘的場景,有會話維度、房間維度、企業維度等等,想簡單采用這種預分段機制難以滿足業務需求,因此我們構建一個業務路由系統(RoutingService),實作流量的精確路由,
以IM為例,每次訊息的發送,在單元化框架層面,會通過訊息的會話(CID),查詢路由資訊,如果是本單元,流量下行并持久化;如果是非本單元,路由到對應的單元中,下圖是三個會話,分別是cid:1001、cid:1002、cid:1003,三個會話隸屬不同單元,不管用戶從哪個單元發送訊息,都會路由到會話所在的單元,比如用戶在Unit B的cid:1001 中發送訊息,當訊息進入Receiver之后,會先查詢此cid:1001所在的單元,發現是Unit A,路由框架將請求轉到A單元,訊息在A單元持久化并通過A單元的同步協議,將資料推送到客戶端,
從上圖可知,每次訊息發送,都要查詢路由服務,DTIM百萬的峰值,對路由必然會帶來超大的壓力,同時我們能發現,路由資料在多單元實作一致性是一個巨大的挑戰,
邊緣計算:端到端路由
在DTIM的場景中,會話的路由資訊幾乎不會變更,只有當我們決定將某些超大的會話或者企業騰挪到新單元時,才會發起路由的變更,因此會話的路由資訊幾乎可以認為是恒定不變的,那么每次查詢路由服務端,效費比太低,是極大的浪費,既然路由資訊幾乎不可變,是否將路由資訊快取呢?最常見的是使用一個集中式的Cache系統,快取Hot的會話,我們也是這么做的,但是這么做還是不夠,一旦Cache系統失效,DTIM還是會出現大面積故障,而且這個百萬級的請求對Cache也是一個極大的壓力,
考慮到釘釘有強大的客戶端,借用邊緣計算的思路,我們將用戶的會話資料快取到客戶端,對于客戶端來說,也只用快取用戶自身最熱的N會話路由資料,訊息發送時,通過Header將路由資料攜帶到服務端,服務端路由SDK只要做合法性和續約即可,將路由流量降低了95%以上,當路由服務出現例外時,還可以繼續使用客戶端路由,將路由的可用性提升到一個新的高度,
SDK本地會依據上行請求的回傳中是否有新的路由資訊,進而更新客戶端路由,同時可以借助釘釘有主動下推的能力,通過同步協議將新的路由資訊主動推送給客戶端,使會話遷移做到更平順,
計算下沉:多單元一致性
對于新會話,比如小明要創建一個群聊,是應該創建在那個單元呢?如果在A單元創建了,當會話訊息來到B單元,系統怎么能第一時間知道會話已經在被系結到A單元,這里一般的方式有兩種,單元之間的存盤系統采用類似DTS的機制進行異步同步,這種機制有秒級延遲;或者第二種方式是在應用層主動同步,比如接入訊息佇列,這兩種方式由于都是異步的原因,都會出現不一致的問題,如果會話同時被系結在兩個單元,邏輯上會導致用戶的歷史訊息丟失,這個是不能接受的,
多地域(Region)資料同步其實是通用的技術挑戰,我們認為存盤系統提供是最好的方式,正如Google的Spanner一樣,這樣對我們上層才是最友好的方式,因此我們找到了存盤的OTS、Nuwa團隊一起共建了GlobalTable,GlobalTable的核心原理還是借助Nuwa的一致性組,組分布在多個地域,采用多數派寫入成功即回傳的原理,做到20ms以內的一致性寫,
釘釘單元化的容災能力是深度結合釘釘的業務層場景落地的,和淘系支付寶等有明確的區別,以DTIM為例,最大的特點是當服務單元例外時,服務側仍能提供最核心的服務,保障最基本的能力,本質上是由于DTIM是最終一致性系統,可以短暫允許部分環節失敗,可以看一下DTIM發送訊息的容災場景,當某個單元完全不可用的情況下,用戶訊息發送鏈路通過降級為local模式,在本地校驗非本單元會話資料通過之后直接做訊息發送,processor遇到非本單元的會話訊息資料可以做單元間投遞做資料回放,本地是否落庫可選,同步協議推送不必區分是否為本單元會話訊息資料直接通過本單元的topic推送給客戶端,配合用戶無狀態快速遷移能力,單元間可以實作真正的分鐘級別容災切換能力,
|
|
|
|
|
|
|
和支付寶一樣,在釘釘做單元化有了頂層設計和最佳實踐,應用能快速進行業務改造,
|
|
|
基于業務路由(L2),每個業務由于自身的特點,在容量和容災場景有會有獨立的選擇,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
分鐘級容災切換,DTIM能做到容災場景下核心功能依舊可用,
|
|
|
路由服務作為整個單元化的核心,提供99.995%的可用性,但是對于DTIM這種超大場景,僅有的0.005%例外也會帶來極大的影響,所以DTIM和單元化做了降級兜底,
|
|
|
|
以上是釘釘單元化2.0提供給應用的核心能力,在滿足容災和容量設計需求之后,釘釘單元化給應用帶來了更多的能力和想象空間,比如:快速遷移:當某一地域資源不足時,釘釘單元化可以將業務快速的從A單元遷移到B單元,常態化切流:比如新建的教育會話,可以放到獨立的單元,熱點治理:當前某一個會話過熱,特殊時期可以遷移到獨立集群,SLA:滿足不同的VIP客戶需求,基于不同的SLA和售賣價格,將VIP客戶放到對應地單元,
核心還是我們擁有單元化能力之后,實作了多單元流量的快速調度,為業務解決了后顧之憂,
魚和熊掌不可兼得
2022年對釘釘來說是成本之年,成本的壓力不光落到了團隊,還落到了每個人身上,正如存盤的CAP理論是一樣的,我們同時只能滿足兩個維度,對于流量(性能P)、成本(C)、體驗(E)也是一樣,在流量不可預知和干預的情況下,選擇成本必然導致體驗受損,反之選擇體驗,必然導致成本升高,進入下半年,疫情反復帶來流量的反復,為了實作可控的教育成本,只能在高峰期降級部分能力,這又導致體驗受損,這段時間的工單量可以窺見一斑,
流量是用戶側觸發的,我們無法干預,只能在成本和體驗之間尋求平衡,和前面提及的一樣,為了減小成本的消耗這就導致我們在擴容和縮容之間疲于奔命,反應不及時甚至有故障的危險,這種機制不可取也不可持續,到底是要流量與成本,還是要流量與體驗,給我們技術團隊帶來了巨大的挑戰和矛盾,
商業化路在何方
當前釘釘為支持大客戶提供了多種解決方案,專業釘釘、專屬存盤與打包、專有釘釘,專屬釘釘通過APP專屬化以及部分專屬功能,比如為一個企業定制一個擁有獨立Logo的APP,能滿足一般的中大型客戶的業務訴求,對于大型以及超大型客戶,我們提供專有釘釘,提供專有化輸出,完全隔離的方案,比如浙政釘,伴隨著釘釘的商業化進入深水區,客戶對釘釘提出了新的訴求,特別是資料安全與歸屬、互聯互通、完整的能力堆疊等訴求,當前釘釘輸出產品形態都無法同時地滿足以上需求,
前幾年互聯網上出現的幾起資料安全事件,資料丟失與泄露,未經客戶授權私自訪問客戶資料,讓大多數客戶不信任服務提供商,即使服務商的安全能力已經是業界一線能力,其實這個是可以理解的,資料即客戶的生命線,資料無法在自身可控范圍內,特別是對于很多特殊行業,這是無法接受的,自身性命豈能假手于人,專屬釘釘在面臨這種客戶時,前線售賣同學是無能為力,
那么很多同學肯定會提“如果專屬釘釘滿足不了需求,我們專有釘釘不是能解決這些問題嗎?”,其實單單從訴求來看,專有釘釘場景是切合客戶的業務訴求,提供完全獨立運行環境、可控的資料安全,但是專有釘釘由于其獨特的架構帶來高昂的售價以及后期的運維代價,對于超大型的客戶來說也難以承擔如此高的成本,對于釘釘自身來說,從研發到后續運維,維護一套獨立體系也難以在客戶側大面積推廣,
釘釘單元化經過四年的發展,在容災和容量上做出一定的積淀,同時完成了一些核心技術的積累,當整體架構成熟之后,我們也在思考,單元化能否從技術架構升級為業務架構,比如搭建獨立的高可用單元,按照售賣的SLA提供給VIP客戶,支持釘釘商業化的發展,同時我們在云原生逐步發力,將部分核心應用放到云上,經過這一年多的運行,遇到了新的挑戰,但更獲得云下無法獲得的計算彈性能力,云上的彈性對云下是一個降維打擊,從一個新的方向解決計算問題,
如上文提到的兩個核心挑戰,釘釘單元化同樣面臨這個問題,在持續的發展中找到了一個合適的架構方向,基本思路是云下作為基本盤,保障核心流量的問題,畢竟云下經過集團多年的打磨,不管是穩定性還是流程的合理性都有保障;云上應對高漲例外的流量,比如和疫情正相關的教育流量,既保證了服務的穩定性,又能充分利用云上彈性能力,在提供完整能力的前提下做到一個相對較低的成本,其次是升級Geo概念,將Geo作為一個獨立的業務域,實作Geo級別完全獨立部署,分布式云模式;同時Geo之間按需互通,從研發體系上能做到一套代碼,
因此,釘釘單元化來到了3.0版本,我們稱之為釘釘單元化混合云架構,混合云主要是從兩個維度來看:第一是云上云下,我們認為云上云下并不是取代的關系,而是相互補充的關系,是一個長期的狀態,正如很多大客戶隨著規模的持續擴張,最終依賴的部分核心能力必然走向自研道理一樣,這能做成本的進一步降低,所以架構是一個混合云架構,第二,業務架構上也是混合云架構,通過不同的Geo,將不同的業務邏輯上聚合到一起,構建起一張釘釘的大網,不同Geo按需互通,實作了業務架構的混合,3.0從系統架構上相對于2.0,最大的區別就是云原生技術的運用和互通網關的建立,
云原生技術 :抵抗系統架構熵增的有效手段
近幾年,互聯網圈最火的技術莫過于以Docker為代表的云原生技術最為火熱,各大云廠商也都在不遺余力的推廣云原生技術以及對應的產品;同時釘釘服務過億DAU的客戶,面對各種可靠性、服務連續性、并發、容災等技術挑戰,也都走到了現有技術的邊界,所以我們也在不斷吸收新的技術和架構,希望從體系與架構上降低我們的技術復雜度,以抵抗熵增,我們在2021年底啟動了我們的云原生升級戰略,我們升級云原生技術并不是為了技術而升級,而是切實面臨巨大的技術挑戰,
首先我們面臨多語言的挑戰,我們以IM為例,IM的核心邏輯都是使用C++構建,但是我們常用的中間件三大件:存盤、快取、異步佇列,其中快取和異步佇列在C++客戶端上長期建設不足,導致IM長期在使用低版本,低版本由于長時間缺乏維護,經常會出現例外,比如佇列假死、消費不均等,導致我們自己不得不親自上陣修改SDK的代碼;最后難以使用到產品的新能力,阻礙IM服務能力的提升,
其次是多產品多云的挑戰,我們以阿里云為例,資料庫類目下的產品,從類別上就有關系資料庫、NoSQL資料庫、數倉等等,還有存盤也是一樣,對于我們上層業務,其實絕大部分服務都只依賴了底層的CURD,這么多產品,每次對接一個產品都要開發一輪,配置系統也是一樣,彈內有Diamond,云上有Nacos、Mse,K8s有自己的Configmap等,而且這些配置系統不像資料庫有標準,而是百花齊放,但是這樣卻苦了我們使用者,這些內容不是我們的核心路徑,浪費大把時間在各種產品介面的適配上,明顯拖累了釘釘的發展,
最后就是通用的流量治理挑戰,釘釘很多系統都是最終一致的系統,IM就是典型的最終一致系統,這類系統和強同步系統在架構設計有一個明顯的區別,強一致系統如果遇到失敗,必須要持續重試直到成功,所以一般編程上都是重試+退避,但是最終一致系統不是,這類系統允許部分節點失敗,不要阻礙其他流程,失敗的流量通過一個異步回旋的佇列,將資料逐步回放回來即可,這種回旋需要借助異步佇列,而且要設計各種消費機制,比如限速、比如丟棄等等,這是一個通用的邏輯,但是每個業務方或多或少都在實作自己的回旋系統,重復的造輪子,又比如各種故障注入,單元化路由流量等等,要想擁有這個能力,團隊不得不投入人力研發,
在對付架構復雜度上,我們主要從兩個維度來屏蔽復雜度,首先代碼層面我們選擇了DDD模式,我們使用DDD分層核心是把對外系統的依賴全部收攏到Infrastructure這一層,全部采用純虛函式(Interface)對外提供介面,屏蔽底層中間件差異和細節,在架構上采用Sidecar的模式,類似于Dapr的思想,通過標準的GRPC和PB實作應用與中間件解耦,Sidecar中集成了各種中間件、配置系統、灰度系統等,等價實作了應用和中間件的解耦,上文中提到的不管是多語言挑戰、多云多產品的挑戰、重復造輪子等問題,都能很好的解決,
互通網關 :混合架構的基石
云上云下互通,或者說多個云賬戶VPC之間的互通,我們常見的有兩種方案,其一就是VPC直接打通,讓多個VPC之間形成一個大的局域網,RealServer實作點對點互通;其二是中間搭建一個負載均衡器,通過暴露EIP實作互通,兩個方案都有自己的優缺點,比如方案一,打通的VPC涉及到IP規劃,如果前期沒有合理規劃,后續很難打通;還有這種方案有水桶短板安全問題,一旦一個VPC被攻破,這張網也被攻破;但是對于內部的應用來說架構就比較簡單,可以僅僅借助K8s DNS service就能做到服務發現,對于方案二,最大的缺點就是中間有一個集中式的負載均衡,需要申請獨立的LB才可訪問;但是這種方案隔離性好,
對于釘釘單元化來說,涉及N個業務方,N * M個應用,對應X個VPC,要想VPC之間打通,幾乎沒有可能性,而且VPC打通,還面臨應用之間的安全性問題,要實作Geo之間互通,環境之間的隔離性是基本要求,與此同時,我們也要考慮到系統的可擴展性,所以我們必須要構建一套獨立的流量網關,實作流量加密、尋址、轉發等通用能力,
釘釘互通網關是構建在Envoy之上的系統,雙向Ingress和Egress,支持GRPC和釘釘自研協議,具備流量管理、傳輸加密、單元尋址等能力,釘釘單元化借助互通網關的能力,再配合全域流控系統,我們可以在多單元之間實作精確的流量控制和調度,
伴隨著專屬集群的持續輸出,客戶對專屬的場景需求會越來越多,需要我們投入更多的人力持續的建設,比如架構側的加強,首先是Sidecar持續強化,支持更多的中間件和環境,提供不同維度的安全能力,滿足客戶和應用的安全需求,在運維側,我們需要構建多Geo管理能力,完善Geo和單元之間流量快速調度能力,提供自動化的自檢系統等,在交付側,如果實作快速交付,比如是否能做到新應用一周完成單元化改造,新Geo一天部署完成,這些挑戰都是接下來我們要重點投入的方向,
對于標準釘來說,這個是我們的基本盤,一個穩定可靠且低成本的釘釘是我們持之以恒的目標,接下來我們會加大云上流量的占比,充分的借助云上彈性能力,實作可控的成本,
今天我們只是站在釘釘的角度上拋了一個“磚”,希望在異地多活這個領域激起一層浪花,歡迎大家一起討論,
-
Geo:釘釘專有化部署單位,解決資料合規需求,Geo間資料按需互通,并且互通資料在Geo內部做鏡像拷貝,解決兩化問題
-
Unit: Geo內部資源物理磁區隔離的最小單位,解決Geo內的容災和容量的問題
-
L0:客戶端路由,決定了用戶客戶端接入釘釘服務器的所屬單元,用戶長連接所在的邏輯單元,起到連接加速作用,用戶接入單元
-
L1:接入層路由,以用戶為維度進行調度,即用戶操作發生的單元,用戶歸屬單元
-
L2:業務層路由,以業務資源為維度進行調度,大部分的業務資源所在單元應該和用戶調度單元一致,但一些業務無法按照用戶劃分單元,如IM的會話,音視頻的會議, 業務歸屬單元
-
DMB:負責釘釘應用跨單元RPC呼叫的轉發,可以認為是釘釘單元化RPC路由中間件,
-
DMR:負責釘釘應用跨單元MQ訊息的轉發,可以認為是釘釘單元化MQ路由中間件,
作者|嘯臺、萬泓
本文來自博客園,作者:古道輕風,轉載請注明原文鏈接:https://www.cnblogs.com/88223100/p/How-to-carry-the-traffic-peak-through-the-modularization.html
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/547033.html
標籤:架構設計
上一篇:幾種常用三維模型幾何精糾正方法,可以糾正三維模型精度
下一篇:對于多任務多連接多執行緒實作限速的實作方法及思考