企業級SaaS的多租戶設計

 

 

導讀

      現有企業級SaaS市場在每個細分領域都涌現出了一批玩家,從技術角度看,在不同的領域不同的SaaS產品必定有着同樣的架構內核,其中最關鍵的就是多租戶的支持。簡而言之,SaaS的成熟度高低,很大程度取決於如何實現多租戶模式的支持。

 

多租戶技術的核心關注點

 

多租戶在技術實現層面目前並沒有既定的規範,不僅細節多,每處細節的實現方式也多種多樣。

 

如何落地,一方面取決於當前研發團隊現有的技術儲備、技術選型、團隊資本實力、所處行業或客戶特點(比如金融行業對數據安全會有更高要求),另一方面也與當前的技術發展息息相關,雲廠商的崛起和雲原生時代的到來,也深刻影響着包括SaaS在內的軟件構建的方法。

但常規來說,真正的SaaS應用往往需要滿足以下兩點

  • 單實例

  • 多租戶

單實例意味着系統資源層面的共享,多租戶意味着應用邏輯層面的隔離。所以如何平衡好這兩點,纔是SaaS應用多租戶設計的核心關注點。

 

經典的分佈式服務架構天然解決了互聯網應用的三高問題(高併發、高性能、高可用),這也是企業SaaS發展中後期即將面臨的問題,下面我們來分析下如何在該架構下去設計與實現多租戶SaaS應用。

 

多租戶的實現

 

從資源共享的層面看,從share nothing到share everything,在天平的任何一個點上都可以支撐多租戶。

 

但正如我們前文所說,SaaS架構首要考慮的目標便是單實例,只有單實例才能將成本儘可能降低,產品纔會有規模效應。所以所謂共享和隔離,在經典架構下又會聚焦爲一點,即如何對不同租戶進行資源層面的隔離。

 

 

關於資源

 

談到資源,我們可能會想到CPU、內存、磁盤、網絡帶寬等,但如此多類型的資源,從其特徵上又可以歸爲兩類,即存儲資源和計算資源。

 

換句話說,SaaS系統在技術本質上也可以認爲就是分佈式存儲和分佈式計算的融合

 

在多租戶的實現中,往往更關鍵的是對於存儲資源的處理,計算資源一般只在必要情況下才會考慮,我認爲這主要是和存儲的“有狀態性”有關。下面我們以一些典型場景爲例,具體分析一下多租戶的設計該如何着手。

 

 

存儲資源的隔離

 

隔離存儲資源概括來說可以用一個詞來解決:命名空間。以數據庫爲例,我們只需要在每條租戶的記錄上,記下對應租戶的標識即可。

 

一般來說,不考慮分庫分表的情況下,我們邏輯上會在同一個Schema中,存儲所有租戶的數據。這就要求每張表都會有一個tenant_id字段,也即每條記錄都攜帶了它的“命名空間”——租戶標識。

 

 

再以常用的NoSQL方案Redis爲例,一般來說也是在同一個分佈式集羣中存儲所有租戶數據,那麼很明顯在key上攜帶租戶標識即可。

 

所以無論何種存儲,思路都是相通的,而且處理起來相對簡單粗暴。但這裏我想着重強調的是,在工程層面我們應當將這種約定在底層框架裏做統一處理。

 

比如在租戶上下文中的所有SQL語句,應當都要攜帶where tenant_id=?這個條件,才能保證邏輯正確,我們很難想象在代碼從零到十萬、百萬行的過程中,所有人都自始至終都牢記這個規則。

 

那麼類似場景下,我們就可以通過AOP技術將多租戶相關的邏輯切出來進行統一處理,比如在Java中,我們可以定義@TenantContextAware註解,以聲明而非編碼的方式在需要的地方做對應的租戶信息獲取及傳遞處理。

 

那麼又如何保證開發者也牢記這個規則呢,由於多租戶是SaaS的天然屬性,我們可以反其道而行之,默認支持多租戶邏輯,同時定義@TenantContextUnaware註解,在不需要多租戶的地方進行例外聲明,這就大大降低了開發團隊的負擔。

 

同理,類似Redis Key的維護,也建議定義統一的KeyGeneratePolicy來維護。

 

計算資源的隔離

 

隔離計算資源的方法也可以用一個詞來概括,那就是親和性,簡單來說就是租戶與集羣計算資源的親和性設計。

 

計算與存儲除了“狀態”方面的差異外,還有一個非常重要的區別,計算的財務成本往往遠高於存儲,比如我們一臺虛擬主機上可能只允許數百個線程同時處理請求。

 

正因爲如此,寶貴的計算資源在非必要的情況下一般不會再進行細粒度的隔離,例如我們一般不會在運行時只允許某租戶的請求只提交給指定工作線程處理

 

另外一方面,計算資源發生傾斜的後果,往往比存儲要嚴重的多,如同木桶效應般,直接且顯著地影響整個集羣的服務能力。

 

但特定場景下較粗粒度的隔離,有時候還是非常必要的。比如爲了減少系統故障時租戶的影響範圍,我們可能會將租戶的請求哈希後提交給不同的線程池處理,因爲這種情況下,反壓將會產生全局的影響。

 

另外我們也可能在特定場景下進行進程、集羣層面的隔離。總的來說,對計算資源進行隔離,沒有既定的模式與套路,而且往往需要高超的資源操作水平,一般不到萬不得已不建議實施。

 

同樣地,如果一定要實施,那麼也應當以組件化的方式進行,保證業務邏輯的純粹性。

 

通過上述對存儲和計算資源的隔離處理,我們的SaaS架構整體看起來將會是下圖這個結構。

 

 

 

在這裏用一個表格就一些要點對兩種手段做個簡單的對比,便於大家更直觀地理解↓

 

 

單實例架構的擴展

 

面向企業的SaaS服務往往還有一些特點可能會引出一些高階需求,而獨立的單實例架構有時候並不能完全滿足這些高階需求。

 

此時就需要對原有架構進行擴展,以實例級別的整體隔離,配合租戶級的請求分流手段,爲SaaS帶來資源、軟件版本等多方面的隔離。

 

需要注意的是,對單實例架構的擴展,並沒有降低其架構成熟度,與我們文中一直在強調的單實例架構理念並不衝突

 

比如我們往往會根據企業客戶的規模和特點對其保障等級進行分級,那如何進一步合理地隔離資源,保障不同級別客戶的使用體驗,也是一個無法逃避的問題。

 

這種情況下,我們就可以考慮將這類客戶的某些資源實施特殊的保護性隔離,或者乾脆將單實例架構擴展成爲多實例架構,將客戶分流到不同保障級別的資源池。

 

如果有個別客戶體量遠超其他客戶,那麼在成本允許的情況下,我們甚至可以考慮爲其建設專屬資源池,對其進行重點保障,這種級別的保護並不意味着犧牲了小體量客戶的體驗,相反,往往大體量客戶才更容易發生一些影響穩定性的突發事件,所以可以認爲是一種多贏的操作。

 

另外,SaaS往往能給客戶帶來更快的特性交付,但這種快速交付很可能帶來不佳的使用體驗,比如嚴重BUG的存在。

 

那麼這個時候,如果我們的系統是多實例架構,那麼就可以很輕易地實現灰度發佈,從而使得特性交付的過程更加穩健,也是對品牌形象的一種保護

總結

 

在實際開發中,我們往往容易忽視早期對類似多租戶等基礎層面的系統性規劃與設計,導致後期研發、維護成本持續增加,甚至在面臨一些新的商業機會的時候,無法靈活應對。

 

好的架構則能將這些本質的特徵透明化,做到業務層無感,從而提高研發效率。

 

在企業SaaS的多租戶架構設計環節,我們無法羅列或預判所有可能,在不同的技術選型下的多租戶實現也有很大差異,我們應當着重去發掘其技術本質,從計算與存儲資源的隔離層面,系統地規劃與架構,做好基礎組件的建設與沉澱。

 

只有拋開現象去歸納總結相關本質方法,才能以不變應萬變。

 

關於作者

張晉。網易智慧企業架構師,負責旗下多款SaaS產品的架構、基礎設施建設等相關工作,有豐富的C端、B端產品研發經驗。目前主要關注企業級產品的技術架構、研發管理等方面

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