漫談企業級SaaS的多租戶設計

企業級SaaS市場近幾年在每一個細分領域都涌現出了一批玩家。從技術角度看,不一樣的領域、不一樣的SaaS產品,一定有着一樣的架構內核,其中最關鍵的即是對於多租戶(Multi-Tenancy)的支持。對廣大企業來講,引入SaaS產品本質上就是對互聯網服務的租賃,於是多租戶便必然是SaaS的自然屬性之一,也是其與傳統互聯網應用架構設計的重要差別之一。在SaaS架構的成熟度演進過程當中,其核心路線即是如何實現多租戶,也就是說,SaaS成熟度的高低,很大程度上取決於如何實現多租戶的支持。數據庫

一多租戶技術的核心關注點

多租戶在技術實現層面目前並無既定的規範,不只細節多,每處細節的實現方式也多種多樣。如何落地,一方面取決於當前研發團隊現有的技術儲備、技術選型、團隊資本實力、所處行業或客戶特色(好比金融行業對數據安全會有更高要求),另外一方面也與當前的技術發展息息相關,雲廠商的崛起和雲原生時代的到來,也深入影響着包括SaaS在內的軟件構建的方法。
圖片1.png安全

但常規來講,真正的SaaS應用每每須要知足如下兩點網絡

· 單實架構

· 多租併發

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

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

二多租戶的實現

從資源共享的層面看,從share nothing到share everything,在天平的任何一個點上均可以支撐多租戶。但正如咱們前文所說,SaaS架構首要考慮的目標即是單實例,只有單實例才能將成本儘量下降,產品纔會有規模效應。因此所謂共享和隔離,在經典架構下又會聚焦爲一點,即如何對不一樣租戶進行資源層面的隔離高併發

三關於資源

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

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

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

四存儲資源的隔離

隔離存儲資源歸納來講能夠用一個詞來解決:命名空間。以數據庫爲例,咱們只須要在每條租戶的記錄上,記下對應租戶的標識便可。

通常來講,不考慮分庫分表的狀況下,咱們邏輯上會在同一個Schema中,存儲全部租戶的數據。這就要求每張表都會有一個tenant_id字段,也即每條記錄都攜帶了它的「命名空間」——租戶標識。

圖片2.png

再以經常使用的NoSQL方案Redis爲例,通常來講也是在同一個分佈式集羣中存儲全部租戶數據,那麼很明顯在key上攜帶租戶標識便可。

因此不管何種存儲,思路都是相通的,並且處理起來相對簡單粗暴。但這裏我想着重強調的是,在工程層面咱們應當將這種約定在底層框架裏作統一處理

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

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

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

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

五計算資源的隔離

隔離計算資源的方法也能夠用一個詞來歸納,那就是親和性,簡單來講就是租戶與集羣計算資源的親和性設計。
圖片3.png

計算與存儲除了「狀態」方面的差別外,還有一個很是重要的區別,計算的財務成本每每遠高於存儲,好比咱們一臺虛擬主機上可能只容許數百個線程同時處理請求。

正由於如此,寶貴的計算資源在非必要的狀況下通常不會再進行細粒度的隔離,例如咱們通常不會在運行時只容許某租戶的請求只提交給指定工做線程處理。

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

但特定場景下較粗粒度的隔離,有時候仍是很是必要的。好比爲了減小系統故障時租戶的影響範圍,咱們可能會將租戶的請求哈希後提交給不一樣的線程池處理,由於這種狀況下,反壓將會產生全局的影響。

另外咱們也可能在特定場景下進行進程、集羣層面的隔離。總的來講,對計算資源進行隔離,沒有既定的模式與套路,並且每每須要高超的資源操做水平,通常不到萬不得已不建議實施。

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

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

圖片4.png

在這裏用一個表格就一些要點對兩種手段作個簡單的對比,便於你們更直觀地理解。
圖片5.png

六單實例架構的擴展

面向企業的SaaS服務每每還有一些特色可能會引出一些高階需求,而獨立的單實例架構有時候並不能徹底知足這些高階需求。此時就須要對原有架構進行擴展,以實例級別的總體隔離,配合租戶級的請求分流手段,爲SaaS帶來資源、軟件版本等多方面的隔離。

但須要注意的是,對單實例架構的擴展,並無下降其架構成熟度,與咱們文中一直在強調的單實例架構理念並不衝突

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

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

若是有個別客戶體量遠超其餘客戶,那麼在成本容許的狀況下,咱們甚至能夠考慮爲其建設專屬資源池,對其進行重點保障,這種級別的保護並不意味着犧牲了小體量客戶的體驗,相反,每每大致量客戶才更容易發生一些影響穩定性的突發事件,因此能夠認爲是一種多贏的操做。

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

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

七總結

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

好的架構則能將這些本質的特徵透明化,作到業務層無感,從而提升研發效率。在企業SaaS的多租戶架構設計環節,咱們沒法羅列或預判全部可能,在不一樣的技術選型下的多租戶實現也有很大差別,咱們應當着重去發掘其技術本質,從計算與存儲資源的隔離層面,系統地規劃與架構,作好基礎組件的建設與沉澱。

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

關於做者

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

相關文章
相關標籤/搜索