億級使用者中心的設計與實踐
使用者中心是網際網路最為基礎的核心繫統,隨著業務和使用者的增長,勢必會帶來不斷的挑戰。如何在億級的情況下保證系統的高可用,高效能以及高安全,本文能夠給你一套實踐方案。
注1:本文討論的是微服務框架下的使用者中心,不涉及授權等功能;
注2:本文所涉及的使用者中心設計與vivo自身業務無關。
使用者中心,顧名思義就是管理使用者的地方,幾乎是所有網際網路公司最為核心的子系統之一。它的核心功能是登入與註冊,主要功能是修改密碼、換綁手機號碼、獲取使用者資訊、修改使用者資訊和一些延伸服務,同時還有登入之後生成Token以及校驗Token的功能。下面我們從幾個維度來拆解使用者中心。
一、服務架構
使用者中心既需要為使用者提供服務,也會承擔其他業務的頻繁呼叫;既然需要為使用者提供服務,它就會自帶一些業務邏輯,比如使用者在登入過程中需要風控或簡訊的校驗,那麼就會存在不可用的風險。而比如獲取使用者資訊的介面,則沒有那麼多的依賴,可能只需要呼叫資料庫或者快取就可以。獲取使用者資訊介面要求穩定,而核心的登入註冊介面也需要穩定,但是當我們在介面層面加一些策略或者修改的時候,不希望因為上線問題導致整個服務不可用,而且上線後,需要對整個服務功能做全量的迴歸,導致資源嚴重浪費。
因此,基於業務特性,我們可以將使用者中心拆成3個獨立的微服務: 閘道器服務,核心服務,非同步消費者服務。閘道器服務,提供http服務,聚合了各種業務邏輯和服務呼叫,比如登入時候需要校驗的風控或者簡訊;核心服務,處理簡單的業務邏輯以及資料儲存,核心服務處在呼叫鏈路的終端,幾乎不依賴呼叫其他服務,比如校驗Token或者獲取使用者資訊,他們就只依賴於redis或者資料庫;而非同步消費者服務,則處理並消費非同步訊息。下文會詳細介紹。
這樣的設計之後,當有新功能上線時,核心服務和非同步消費服務幾乎不需要重新發布,只需要釋出閘道器服務,依賴我們核心服務的第三方非常放心,層級也非常的清晰。當然,這樣做的代價就是服務的呼叫鏈路變長了。由於涉及到閘道器和核心服務,就需要釋出兩個服務,而且要做相容性測試。
二、介面設計
使用者中心的介面涉及到使用者的核心資訊,安全性要求高;同時,承接了較多第三方的呼叫,可用性要求也高。因此,對使用者中心的介面做以下設計:
首先,介面可以拆分為面向Web和麵向App的介面。Web介面需要做到跨域情況下的單點登入,加密、驗籤和token校驗的方式也同App端的不一樣。
其次,對核心介面做特殊處理。比如登入介面,在邏輯和鏈路上做了一些最佳化。為什麼要對這些介面做特殊處理呢?假如使用者不能登入,使用者會非常恐慌,客訴量會立馬上來。
那怎麼做呢?一方面,我們將使用者核心資訊表做簡單。使用者的資訊當中會包含userId、手機號碼、密碼、頭像、暱稱等欄位,假如把使用者的這些所有資訊都儲存在一張表中,那麼這張表將會異常龐大,變更欄位變得異常困難。因此,需要將使用者表拆分,將核心的資訊儲存在使用者表中,比如userId、username、手機號碼、密碼、鹽值(隨機生成)等;而一些如性別,頭像,暱稱等資訊儲存在使用者資料表中。
另一方面,我們需要將登入的核心鏈路做短,短到只依賴於讀庫。一般情況下,使用者登入後,需要記錄使用者登入資訊,呼叫風控或者簡訊等服務。對於登入鏈路來說,任何一個環節出現問題都有可能導致使用者無法登入,那麼怎麼樣才能做到最短的鏈路呢?方法就是依賴的服務可自動降級。比如說反欺詐校驗出問題了,那麼它自動降級後使用它的預設策略,極端情況下只做密碼校驗,主庫掛了之後還能到從庫讀取使用者資訊。
最後就是介面的安全性校驗。對App介面我們需要做防重放和驗籤。驗籤可能大家比較熟悉,但是對防重放這個概念可能相對陌生。防重放,顧名思義就是防止請求重複傳送。使用者請求在特定時間段內只能請求一次。即使使用者請求被攻擊者挾持,在一段時間內也無法重複請求。如果攻擊者想要篡改使用者請求再傳送,對不起,請求不會透過。得益於大資料的支援,結合終端,我們還可以把每個使用者行為畫像儲存在系統中(或者呼叫第三方服務)。使用者發起請求後,我們的介面會根據使用者畫像對使用者進行諸如手機號碼校驗、實名認證、人臉或者活體校驗。
三、分庫分表
隨著使用者的增長,資料超過了1億,怎麼辦?常見的辦法就是分庫分表。我們來分析一下使用者中心常見的一些表結構:使用者資訊表,第三方登入關聯表,使用者事件表。從上述表中可以看出來,使用者相關的資料表增長相對緩慢,因為使用者增長是有天花板的。使用者事件表的增長是呈指數級增長,因為每個使用者登入、變更等密碼及變更手機號碼等操作是不限次數。
因此,首先我們可以先把使用者資訊表垂直切分。正如上面說的,將使用者ID、密碼、手機號、鹽值等常見欄位從使用者資訊表中拆分,其他使用者相關的資訊用單獨一張表。另外,把使用者事件表遷移至其他庫中。相比於水平切分,垂直切分的代價相對較少,操作起來相對簡單。使用者核心資訊表由於資料量相對較少,即使是億級別的資料,利用資料庫快取的機制,也能夠解決效能問題。
其次,我們可以利用前後臺業務的特性採用不同的方式來區別對待。對於使用者側前臺訪問:使用者透過username/mobile登入或者透過uid來查詢使用者資訊。使用者側資訊的訪問通常是單條資料的查詢,我們可以透過索引多次查詢來解決一致性和高可用問題。對於運營側後臺訪問:根據年齡、性別、登入時間段、註冊時間段等來進行查詢,基本上都是批次分頁查詢。但是由於是內部系統,查詢量低,對一致性要求低。如果使用者側和運營側的查詢採用同一個資料庫,那麼運營側的排序查詢會導致整個庫的CPU上升,查詢效率下降,影響到使用者側。因此,運營側使用的資料庫可以是和使用者側同樣的MySQL離線庫,如果想要增加運營側的查詢效率,可以採用ES非關係型資料庫。ES支援分片與複製,方便水平分割和擴充套件,複製保證了ES的高可用與高吞吐,同時能夠滿足運營側的查詢需求。
最後,如果還是要水平切分來保證系統的效能,那麼我們採取什麼樣的切分方式呢?常見的方法有索引表法和基因法。索引表法的思路主要是UID能夠直接定位到庫,但是手機號碼或者username是無法直接定位到庫的,需要建立一個索引表來記錄mobile與UID或者username與UID的對映關係的方式來解決這個問題。通常這類資料比較少,可以不用分庫分表,但是相比直接查詢,多了一次資料庫查詢的同時,在新增資料的時候還多了一次對映關係的插入,事務變大。基因法的思路是我們將username或者mobile融入到UID中。具體做法如下:
- 使用者註冊時,根據使用者的手機號碼,利用函式生成N bit的基因mobile_gen,使得mobile_gen=f(mobile);
- 生成M bit全域性唯一的id,作為使用者標識;
- 拼接M和N,作為UID賦給使用者;
- 根據N bit來取餘來插入到特定資料庫;
- 查詢使用者資料的時候,將使用者UID的後N bit取餘來落到最終的庫中。
從上述過程中看,基因法只適用於某類經常查詢的場景,比如用手機號碼登入,如果使用者使用username登入就比較麻煩了。因此大家以根據自己的業務場景來選擇不同的方式水平切分。
四、Token之柔性降級
使用者登入之後,另一個重要的事情就是Token的生成與校驗。使用者的Token分為兩類, 一類是web端登陸生成的Token, 這個Token可以和Cookie結合, 達到單點登陸的效果,在此不細說了。另外一類就是APP端登入生成的Token。使用者在我們的APP輸入使用者名稱密碼之後,服務端會對使用者的使用者名稱密碼進行校驗,成功之後從系統配置中心獲取加密演算法的版本以及秘鑰,並按照一定的格式排列使用者ID,手機號、隨機碼以及過期時間,經過一系列的加密之後,生成了Token之後並將其存入Redis快取。而Token的校驗就是把使用者ID和Token組合並校驗是否在Redis中存在。那麼假如Redis不可用了怎麼辦呢?這裡有一個高可用和自動降級的設計。當Redis不可用的時候, 服務端會生成一個特殊格式的Token。當校驗Token的時候,會對Token的格式進行一個判斷。
假如判斷為Redis不可用時生成的Token,那麼服務端會對Token進行解密,而Token的生成是由使用者ID,手機號、隨機碼和過期時間等資料按照特定順序排列並加密而來的, 那麼解密出來的資料中也包含了ID,手機號碼,隨機碼和過期時間。服務端會根據獲取到的資料查詢資料庫, 比對之後告訴使用者是否登入成功。由於記憶體快取redis和資料庫快取效能的差距,在redis不可用的情況下,降級有可能會導致資料庫無法及時響應,因此需要在降級的方法上加入限流。
五、資料安全
資料安全對使用者中心來說非常重要。敏感資料需要脫敏處理,對密碼更是要做多重的加密處理。應用雖然有自己的安全策略,但如果把駭客限制在登入之前,那應用的安全性將得到大幅度的提升。網際網路上使用者明文資料遭到洩露的案件屢屢發生,因此各大企業對資料安全的認識也提到了前所未有的高度。而即使使用了MD5和salt的加密方式,依然可以使用彩虹表的方式來破解。那麼使用者中心對使用者資訊是怎麼儲存的呢?
首先,正如上文中提到的使用者密碼、手機號等登入資訊和其他的資訊分離,而且在不同的資料庫中。其次,對使用者設定的密碼進行了黑名單校驗,只要符合條件的弱密碼,都會拒絕提交,因為不管使用了什麼加密方式的弱密碼,都極其容易破解。為什麼呢?因為人的記性很差,大部分人總是最傾向於選擇生日,單詞等來當密碼。6位純數字可以生成100萬個不同的密碼,8位小寫字母和數字的組合大概可以生成2.8萬億個不同的密碼。一個規模為7.8萬億的密碼庫足以覆蓋大部分使用者的密碼,對於不同的加密演算法都可以擁有這樣一個密碼庫,這也就是為什麼大部分網站都建議使用者使用8位以上數字加字母密碼的原因。當然,如果一方面加了鹽值,另一方面對金鑰分開保管,破解難度會指數級增加。
最後,可以用bcrypt/scrypt的方式來加密。bcrypt演算法是基於Blowfish塊金鑰演算法來實現的,bcrypt內部實現了隨機加鹽處理,使用bcrypt之後每次加密後的密文都不一樣,同時還會使用記憶體初始化hash過程。由於使用記憶體,雖然在CPU上執行很快,但是在GPU上並行運算並不快。隨著新的FPGA整合了大型RAM,解決了記憶體密集IO的問題,但是破解難度依然不小。而scrypt演算法彌補了bcrypt演算法的不足,它將CPU計算與記憶體使用開銷都指數級提升了。bcrypt和scrypt演算法能夠有效抵禦彩虹表,但是安全性的提升帶來了使用者登入效能的下降。使用者登入註冊並不是一個高併發的介面,所以影響並不會特別大。因此在安全和效能方面需要依據業務型別和大小來做平衡,並不是所有的應用都需要使用這種加密方式來保護使用者密碼。
六、非同步消費設計
此處的非同步消費,就是上文提到的非同步消費服務。使用者在做完登入註冊等操作後,需要記錄使用者的操作日誌。同時,使用者註冊登入完畢後,下游業務需要對使用者增加積分,贈送禮券等獎勵操作。這些系統如果都同步依賴於使用者中心,那麼整個使用者中心將異常龐大,鏈路非常冗長,也不符合業內的“大系統做小“的原則。依賴的服務不可用之後將會造成使用者無法登入註冊。因此,使用者中心在使用者操作完之後,將使用者事件入庫後傳送至MQ,第三方業務監聽使用者事件。使用者中心和下游業務解耦,同時使用者操作事件入庫後,在MQ不可用或者訊息丟失的時候可做補償處理。使用者的畫像資料也在很大程度上來源於此處的資料。
七、靈活多樣的監控
使用者中心涉及到使用者的登入註冊更改密碼等核心功能,能否及時發現系統的問題成為關鍵指標,因此對業務的監控顯得尤為重要。需要對使用者中心重要介面的QPS、機器的記憶體使用量、垃圾回收的時間、服務的呼叫時間等做詳細的監控。當某個介面的呼叫量下降的時候,監控會及時發出報警。除了這些監控之外,還有對資料庫Binlog的寫入,前端元件,以及基於ZipKin全鏈路呼叫時間的監控,實現從使用者發起端到結束端的全面監控,哪怕出現一點問題,監控隨時會告訴你哪裡出問題了。比如運營互動推廣註冊量下降的時候,使用者中心就會發出報警,可以及時通知業務方改正問題,挽回損失。
八、總結
本文從服務架構設計,介面設計,token降級,資料安全和監控等方面介紹了億級使用者中心的設計,當然使用者中心的設計遠不止這些,還會包含使用者資料的分庫分表,熔斷限流,第三方登入等,在本文中就不一一贅述。儘管本文中設計的使用者中心能夠滿足大部分公司的需求,但是還存在一些比較大的挑戰:在鑑權服務增長的情況下,如何平滑的從使用者中心剝離;監控的侵入性以及監控的粒度的完善;另外服務的安全性、可用性、效能的提升永遠都沒有盡頭,也是我們孜孜追求的目標。在未來的日子裡,希望能夠透過大家的努力,使使用者中心的技術體系更上一層樓。
作者:vivo 遊戲技術團隊
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69912579/viewspace-2761461/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- HBase+Elasticsearch,百億級資料中心架構設計實踐Elasticsearch架構
- 億級流量實驗平臺設計與實現
- 使用者中心,1億資料,架構如何設計?架構
- 塗鴉智慧 dubbo-go 億級流量的實踐與探索Go
- 鬥魚 Juno 監控中心的設計與實現
- 實驗 2 Scala 程式設計初級實踐程式設計
- 微前端的設計理念與實踐初探前端
- 聊聊訊息中心的設計與實現邏輯
- react 設計模式與最佳實踐React設計模式
- 頂級程式設計師的10條最佳實踐程式設計師
- Web 使用者體驗設計提升實踐Web
- B站萬億級資料庫選型與架構設計實踐資料庫架構
- 仿微博訊息中心的系統設計與實現
- 騰訊燈塔融合引擎的設計與實踐
- 資料中心基礎設施高可用提升研究與實踐
- JavaScript設計模式與實踐--代理模式JavaScript設計模式
- 端遊HUD設計實踐與策略
- Django RESTful API設計與實踐指南DjangoRESTAPI
- 從0到10億,微信後臺架構及基礎設施設計與實踐!架構
- 如何設計電商行業億級使用者秒殺系統行業
- SAP Spartacus module 層級結構設計的一種實踐
- vivo 敏感詞匹配系統的設計與實踐
- 設計出色API的最佳實踐與原則 - JamesAPI
- 58同城敏捷BI系統的設計與實踐敏捷
- 億級流量架構實戰之秒殺設計架構
- udp程式設計的那些事與golang udp的實踐UDP程式設計Golang
- JavaScript設計模式與實踐–工廠模式JavaScript設計模式
- JavaScript設計模式與實踐--工廠模式JavaScript設計模式
- RocketMQ Flink Catalog 設計與實踐MQ
- 服務API版本控制設計與實踐API
- 移動設計使用者體驗的7個最佳實踐
- Java中的多級快取設計與實現Java快取
- Java的API設計實踐JavaAPI
- RocketMQ 多級儲存設計與實現MQ
- 最佳實踐:路徑路由匹配規則的設計與實現路由
- 京東毫秒級熱key探測框架設計與實踐,已實戰於618大促框架
- 專案中多級快取設計實踐總結快取
- 移動端手勢庫設計與實踐