新東方APP技術架構演進

閃念基因 發佈 2023-02-06T14:29:10.275983+00:00

前言古代東西方的思想家都產生過一個終極的追問,世界的本源到底是什麼?老子說,道生一,一生二,二生三,三生萬物,天道有常不以堯存不為桀亡。孔子說朝聞道,夕死可矣,孔子把對道的研究從,對人與自然關係的天道,轉移到了研究君君臣臣父父子子的人道方向上。

前言

古代東西方的思想家都產生過一個終極的追問,世界的本源到底是什麼?老子說,道生一,一生二,二生三,三生萬物,天道有常不以堯存不為桀亡。孔子說朝聞道,夕死可矣,孔子把對道的研究從,對人與自然關係的天道,轉移到了研究君君臣臣父父子子的人道方向上。古希臘第一個哲學家泰勒斯說世界的本元是水,後來畢達哥拉斯回答說世界的本元是數字,古希臘哲人對道的研究始終聚焦在人與自然關係的天道方向上。對終極追問的不同思考和回答,衍生出了,東西方在文化價值觀,思考邏輯和做事方法等方面的巨大差異。

我們在企業里工作,實際上也存在一個終極追問,就是你的終極用戶是誰?怎麼服務好終極用戶。我曾經在2B技術公司IBM工作過12年,在2C技術公司滴滴工作過三年。我深切感受到,在這個問題上的不同回答,導致了傳統的IT技術公司和網際網路技術公司,在工作價值觀,和工作方法等方面的不同。

通常,B端技術重功能不重體驗,軟體客戶每年續簽一次合同,軟體開發商可以通過各種手段增加用戶遷移到其他競爭對手的成本,深度綁架用戶,即使客戶對軟體有一些不滿意也會續簽合同。而網際網路的C端個人用戶是典型的用腳投票,拋棄一個產品的時候,一聲再見都不會說。因此網際網路產品技術的競爭是典型的贏者通吃,產品體驗稍有不同,短時間內用戶就會流失到競爭對手那裡。

所以網際網路產品技術極度關注用戶體驗,和用戶客訴。IBM解決客戶的客訴流程冗長,有所謂的一線二線三線技術支持,解決一個客訴動輒數月之久。但是網際網路C端技術需要時刻關注用戶客訴和線上事故,爭分奪秒的解決問題,甚至要求幾分鐘內就把問題解決掉。所以如何避免和減少客訴,如何快速處理線上故障,就成為區別2B,2C技術公司不同點之一,對價值導向問題的不同回答,也產生了完全不同的價值評估系,最簡單的就是如何去判斷一項具體工作的輕重緩急和重要程度。

我剛來新東方的時候,我的團隊裡沒人關心客訴問題,沒有技術值班,上線流程很草率。網際網路公司里重要的技術方法到了IBM那裡,就可能變成過度設計,變成不重要的工作,但其實沒有毛病,因為倆者的業務場景和最終用戶是完全不同的。所以一定要認識清楚,我們工作的end user是哪些人,以及如何服務好最終用戶。當最終用戶滿意時,我們的工作自然就會被公司認可。所以我認為很多事情不是技術問題,而是價值導向問題,價值導向對了,我們的技術規劃,架構選型就自然做對了。我是被IBM培養出來的,但到了滴滴後,只要願意改變,一樣也能做好網際網路的技術工作。

在網際網路產生之前,最大的計算機系統就是銀行櫃員系統,用戶必須去銀行網點排隊辦業務,銀行網點人滿為患,所有的交易都通過櫃員處理,大家想想看,這實際上就是通過人工排隊的消息隊列進行了削峰限流,最後所有交易都集中在一台價值幾億人民幣的 IBM大型計算機里,在集中式的DB2或者Oracle資料庫里完成交易。今天網際網路電商的交易量要比銀行大的多,所有的系統都是分布式系統了,與以前的集中式系統相比,分布式系統最顯著的特點就是容易擴容,今天銀行的IT系統也都網際網路化了,大多數的銀行業務都可以足不出戶在家自助辦理了,所以我們必須看一下什麼是分布式系統。


分布式系統

CAP原則

分布式系統是由許多計算機集群組成的,但對用戶而言,就像一台計算機一樣,用戶感知不到背後的邏輯。分布式系統最重要的理論是2002年被科學家證明的CAP理論。C是數據一致性,A是可用性,P是分區容錯性。CAP三者之間是互相矛盾,互相影響的關係。

其中,最好理解的是A,A是可用性。可用性存在兩個關鍵指標。首先是「有限的時間內」,其次是「返回正常的結果」,如果用戶的操作請求返回的是400或者500錯誤,或者規定時間內沒有返回結果發生了超時,那就算是發生了一次不可用。所以並不是說,只有發生了線上大規模事故,全部服務不可用才是不可用,發生400,500錯誤或者請求超時都屬於不可用。

P是分區容錯性:當系統發生消息丟失或局部故障時,仍然可以運行。在分布式系統中,當一項數據只在一個節點中保存時,萬一發生故障,訪問不到該節點的數據,整個系統就是無法容忍的。如果這項數據複製到多個節點上,某個節點數據訪問不了時,系統仍然可以從其他節點上讀到數據,系統的容忍性就提高了。但是多個數據副本又會帶來一致性問題。要保證一致性,每次寫操作就都要等待全部數據副本寫成功,而這種等待會損害系統的可用性。總的來說就是,數據副本越多,分區容忍性就越高,但要複製更新的數據就越多,一致性就越差。所以CAP就是一種按下葫蘆,就起了瓢的感覺。

C是數據一致性,分布式系統的數據一致,是指所有伺服器節點在同一時間看到的數據是完全一樣的。如果成功更新一個數據項後,所有的伺服器節點都能讀取到最新值,那麼這樣的系統就被認為是強一致性的。系統如果不能在規定時間內達成數據一致,就必須在C和A之間做出選擇。注意規定時間內是很重要的。

現在的系統都是分布式系統了,P是不可能放棄的,但一般來講,架構設計要儘量減少依賴,系統依賴的基礎架構組件和第三方系統越多,如果P太強了,整個系統的C和A,可能都會受到損害。架構取捨更多的是在C和A之間做出選擇。而沒有分布式的CA系統就是關係型資料庫,就是放棄了分布式,放棄了分布式的擴容能力。

有些場景要求數據強一致,例如與錢有關的與訂單有關的系統,這種場景下,或者捨棄A,或者捨棄P。關係型資料庫是強一致的CA系統, 但如果mysql引入了從庫,就會在一定程度上損害數據一致性,但同時換來了A可用性或者讀寫性能的提升。

而TIDB採用raft協議,數據保存在至少三個副本里,同時它要兼容mySQL,實現數據的強一致性,所以既然,TIDB引入了P,就只能在一定程度上要捨棄A,讀寫性能會相應降低。但是增強了分布式的擴容能力,包括了存儲和算力的擴容。所以CAP理論告訴我們,所有便宜都想占到是不可能的,只能根據實際業務需求和價值判斷體系,做出取捨。

BASE理論

BASE理論是對CAP理論的延伸,是指基本可用、軟狀態、和最終一致性。基本可用,比如在規定的1秒內熔斷了,沒有返回結果,那我們又重試了兩次,結果返回了結果,這個就是基本可用。假如我重試了兩次後,還是失敗了,我們做了一個降級處理,讓用戶仍然可以繼續使用服務,這個也叫做基本可用。

最終一致性是指系統中的所有數據副本經過一定時間後,最終能夠達到一致的狀態。最終一致性是弱一致性的特殊情況。

新東方不同業務系統對數據一致性的要求都不一樣,本質區別就是具體的業務可以容忍在多長的時間限制內,達到最終的一致性。

FLP不可能原理

FLP不可能原理,請自行查找解釋,此定理告訴我們,不要試圖設計一個能夠容忍各種故障並保持一致的分布式系統,這是不可能的. 分布式事務也永遠無法實現單體應用級別的一致性。即使如paxos協議和raft協議也會出現無法達成共識的情況,只不過出現的可能性很低。所以說paxos是有效的一致性算法,但也不可能做到完美無缺。

如果剝離掉具體的業務目標和產品目標,那我們就會發現,我們C端技術工作的目標,其實正好就是分別對應前面講的CAP。但是同時滿足三個要求是非常難的,只能根據具體業務場景進行取捨。Tidb的開發商的名字叫PingCap就是一個很有雄心壯志的名字,意在無限的同時逼近這三個目標。

現在C端系統都是分布式的,捨棄P是不可能的,而且業務又會要求我們必須保證系統的可用性,因此事實上我們C端技術能捨棄的也只有規定時間內的數據一致性。

通常只要在數秒鐘甚至數分鐘,甚至1天後,可以達到最終一致就是可以接受的。我們犧牲了一致性,換來的就是A的大幅度提升,和性能的提升。

新東方App中的C端技術

接下來我們,結合APP的工作實踐分別講一下分布式系統常用的基礎架構組件

緩存

首先講一下緩存。剛才講過了,數據拷貝越多,數據一致性就越差,引入緩存必然會損害規定時間內的數據一致性,因此數據緩存時間通常不宜太久,持久化的緩存數據更是荒唐。redis對外提供集中式的緩存服務,只增加了一份臨時數據副本。但Guava是基於JAVA的本地緩存,每台JAVA伺服器在本地保存一份數據副本,會顯著損害整個分布式系統的數據一致性,並且造成相關客訴。

除了數據一致性,使用redis也會涉及到系統可用性問題。Redis實際上是一個CP系統。對於Redis,value越大,讀寫訪問的延遲就越大。使用緩存時,要避免使用大Value和大Key,否則會降低可用性。大值可以採用序列化後壓縮的方法。與大值相反的另一個應用極端是,大Key小Value,這種Key可以通過MD5壓縮來避免。通常來講redis的平均get請求應該是數十微秒級別的,這種高速緩存部署時要和應用服務在統一的IDC,避免跨機房部署,因為跨IDC通常會有毫秒級的延遲,從而損害系統可用性,使用緩存的意義就不大了。

此外,Redis可以使用pipeline提高批量訪問性能,可以想見網絡請求數量會因此大幅度減少。

使用緩存時要思考如何提高緩存命中率,對應的就是如何減少緩存穿透率,減少對第三方系統或者對資料庫的壓力。

理想狀態下,系統中的熱數據應該都存在緩存里。一個極端是壓測,不好的壓測設計是,不停地,頻繁地,重複地,請求相同的測試用例,其實第一次就是預取,數據變熱後,壓測快的飛起,但是一到線上真實環境,可能就不行了,因為線上沒有那麼多重複請求。但在真實業務場景下,可以根據用戶的真實的操作序列,預測出來哪些數據將會很快會被用戶訪問到,此時,我們就可以採用預先計算和預取數據的方法,在用戶後續操作發生時直接從緩存里讀取數據。

此外,剛剛發生讀寫操作的數據,可以認為都是熱數據,都可以考慮預取到緩存。

下面這張圖是在剝離掉具體業務後的,APP使用緩存的架構方法。

首先講主動更新緩存。

首先第一個操作是需要更新數據的事務操作,可以考慮主動更新緩存。對一致性要求高的業務,事務主動更新緩存時必須從資料庫主庫讀取數據,否則會因為資料庫主從延遲導致緩存數據和資料庫里的不一致。還有一種情況,緩存的值是一個集合set,需要一次更新很多數據項,這種操作沒有原子性保障,就必須用事務保障,需要加鎖。所以主動更新緩存的方案是不夠嚴謹的,容易導致數據不一致。還有就是發數據更新消息,這時候數據變更MQ必須把所有信息都帶上,典型的如binlog。如果只是通知一個變更事件,可能就會導致緩存數據和資料庫不一致。所以binglog可能是最不容易導致一致性問題的方案。但有時候我就是拿不到binlog怎麼辦,而且嚴謹的方案顯然在技術上會更複雜,落地成本會更高。

我想說,不嚴謹的方式不一定就不能用,時刻記住業務場景可以彌補技術上的不足,架構設計時實用主義比完美主義更適用,更能多快好省地解決業務問題。採用什麼方案,首先是要考慮業務場景是啥,其次是要考慮,萬一出問題了後果是否可控,不要追求完美。特別強調,報名續班優惠的同學,千萬不要這麼玩,如果非要這麼玩,出問題後果自負。

第6個操作是主動更新緩存,緩存快過期時,發生讀操作,可以考慮要主動讀取源數據,更新緩存,當第三方故障,導致數據源無法讀取時,可以主動延長緩存時間。做一個降級處理,提高系統的可用性。

再講一下本地緩存Guava。比起集中式的redis緩存,本地緩存的副本更多了,數據一致性也就更差了。如果網關不採用特別的分發策略,本地緩存時間建議要設置的很短,為的是縮短各節點數據最終一致達成時間。具體怎麼取捨,還是要看業務的實際需求。很多資料庫調優的工作,最終變成了玄學。就是因為系統參數太多了。基於CAP定理,你不可能什麼好處都得到,顧此必然失彼。

其他使用緩存的小竅門還有設置緩存時間時加一個隨機值,避免緩存集中過期帶來的問題。剩下幾個前面都涉及到了,就不再講了。

消息隊列

接下來,快速講一下消息隊列。消息隊列可以應用到分布式事務,分段式提交里。也可以幫助系統架構解耦。可以用來做削峰限流,既不超過系統承載能力,同時又不丟失用戶消息。消息隊列服務的SLA是保證消息不丟,但犧牲掉了消息不重。會重複發送同一個消息。所以就要求消費者保證操作的冪等性。

消費者的拉消息模型,好處是消費者不會過載,超過最大處理能力。壞處是消費者需要專門引入一個單獨的服務進程或者線程。

消費者的推模型,消費者構型簡單,跟開發一個用戶接口一樣。壞處是可能會過載。

常見消息隊列如kafka和rabbitMQ的特點,我就不多講了。

資料庫

下面這張圖,我快速講一下資料庫。

資料庫的主庫從庫讀寫分離,一般會造成規定時間內的數據不一致現象,因此在需要強一致的情況下,可以先讀從庫,如果讀不到,再去讀主庫,一定程度上可以減少主庫的壓力。犧牲了一點兒數據一致性,提升了系統的可用性。

分庫或者分表可以顯著提升資料庫的讀寫性能。定時歸檔和分表一樣,是減少單表的數據量,提升讀寫性能。減少不必要的索引,任何資料庫的讀寫操作,即使沒有使用事務,對資料庫而言也都是一個事務。為了保障資料庫的ACID屬性,Insert操作必須在所有索引建立完成後才能返回,因此單表建立過多的索引會拖累資料庫的性能。

分布式系統里,要儘量避免耗時的資料庫操作,比如資料庫事務操作,聯表查詢等操作,因為這些耗時操作,就是讓系統在規定時間內的A達不到了。根據業務特點可以採取最終一致性的概念,結合消息隊列,使用分段事務提交的方法,適當延長達到一致性的時間,來換取系統可用性的提升。

在新東方APP的作業系統里,發作業就是一個典型的分布式事務。此外,由於學生的報、轉、退,和插班動作,產生了很多學生收不到作業,作業錯亂的客訴。因此結合消息隊列,分段提交。並且用定時任務掃表,做各種補償操作,解決了大部分的客訴問題。

結合CAP定理,再說一句,MYSQL是CA系統,如果mysql增加了從庫,也就是引入了CAP里的P,但是mysql主從分離的分布式屬性,比起tidb的分布式屬性要差一些,因為這樣只是擴容了算力,而不是擴容了存儲。資料庫主從分離,是引入P,犧牲了C,增強了A,SQL查詢變快了。但是對於很短的,規定時間裡,要達到數據強一致的業務場景,讀寫必須都走主庫,但還好,這種業務場景是比較少的,新東方的報名續班業務是一個。

結合CAP定理,Tidb是一個典型的CP系統,至少有三個數據副本,通過raft協議實現三副本數據的強一致。因為引入了P,TIDB的分布式擴展能力比mysql好,無論是存儲擴容還是算力擴容都比mysql好。而且tidb兼容mysql,保證數據的強一致性,所以tidb對於訂單交易業務,是一個很好的選擇。這些年銀行大大小小的不可用事故,發生了不下10起,但沒聽說誰的錢丟了。所以涉及到錢和優惠券的場景,寧可發生服務不可用事故,也不能發生數據一致性事故,因此CP系統保證了錢不丟,又保證了分布式擴容能力,是一個網際網路時代下的很不錯的選擇,但規定時間內的可用性就會差一點兒。

熔斷降級限流

快速說一下,熔斷降級限流,前面的PPT都提到了。

這裡再說一個APP熔斷重試的bad case,APP團隊以前的系統里,把Redis配成默認1秒熔斷,20次重試。結果有一次,redis發生故障時,拖累整個APP服務故障,全都不可用。大家記住redis是CP系統。

關於限流,有一次,因為外部的爬蟲導致APP對教務的訪問增加,流量激增,最後為了系統穩定,在nginx上增加了限流處理,保護了內部ERP系統的穩定。

JVM優化

快速過一下JVM優化。

這一頁是APP做JVM優化的實際案例。我抽象總結一下,就是要儘量避免,一次申請非常大的內存,例如打開一個10幾兆的圖片,例如,不分頁,一次查詢10幾萬條資料庫記錄。

當年輕代內存不夠用的時候,這些內存會直接進入老年代,如果這些操作是比較頻繁的,就會導致頻繁的Full GC,損害整個系統的可用性。優化之後,full GC頻率就明顯下降了。


新東方App口語配音作業重構

新東方APP最核心的功能是學生做作業。這張圖是以前,新東方APP里趣配音的作業架構,這個架構發生過很多次服務不可用事故。

大家還記得前面的定理描述,可用性是在規定的時間內要返回正常的應答。而400應答,500應答增多就是系統服務的可用性降低了。不是非要等整個系統已經ping不到才算不可用。

由於作業,無論是上傳還是下載,都占用連接池的時間過長,所以導致其他服務都受到了影響,降低了整個系統的可用性。

用戶中心頭像服務超時嚴重,分析原因也都是因為NFS存儲導致的。所以為了保障作業穩定性,提升學生做作業的體驗,必須拆除掉對NFS存儲的嚴重依賴。

下面這張圖是目前新東方APP的趣味配音作業的架構,這個架構比較複雜,第一次分享,沒有潤色,稍微有點亂。

這個圖裡的綠色文字步驟都是降級操作。新的架構設計應用了BASE定理,通過基本可用,最終一致等概念,顯著提升了整個作業系統的可用性。

和舊的架構相比,新的架構拆除了整個用戶交互過程對NFS存儲的的依賴,使用騰訊對象存儲作業主力存儲,NFS降級為備份。這個方案還抵擋住了好幾次騰訊雲的線上事故。

當騰訊雲對象存儲發生了故障時,NFS會被啟用,短時間內會有一定的數據不一致,但由於NFS被設置成對象存儲的回源地址,因此兩邊存儲最終還是會一致的。

騰訊雲CDN和對象存儲發生過多次故障,持續時間從數分鐘到10分鐘左右不等,我們的新架構都抗過去了,沒有發生任何穩定性問題和客訴,曾經有一次,新東方到騰訊雲的專線流量激增,就是因為騰訊雲的CDN出了問題,無法訪問,APP端發生大面積降級,帶附件上傳作業的比例顯著增加,查看作業結果視頻的降級也同時顯著增加。

除了混合雲存儲,我們做了自動彈性到騰訊雲的混合雲計算。當我們本地的視頻合成服務過載時,計算任務會自動彈性到騰訊雲的FAAS計算。沒有發生彈性時,我們不需要給騰訊雲付錢,發生彈性時,每個月100萬次以下的計算是免費的。所以可以說,我們以這種方式,基本沒花錢,就加強整個系統的可用性。

作業是APP最重要的功能,從我們的價值導向判斷,作業穩定性投入再多時間和精力,都是值得的。目前新東方APP的作業系統已經被改造成一個打不死的小強。

作者:張建鑫

來源:微信公眾號:新東方技術

出處:https://mp.weixin.qq.com/s/xREZmvnMtFI2P0Mx5DeJQQ

關鍵字: