|
返回總目錄
本篇目錄
什么是多租戶
維基百科:“軟件多租戶是指一種軟件架構(gòu),在這種軟件架構(gòu)中,軟件的一個實例運行在服務(wù)器上并且為多個租戶服務(wù)”。一個租戶是一組共享該軟件實例特定權(quán)限的用戶。有了多租戶架構(gòu),軟件應(yīng)用被設(shè)計成為每個租戶提供一個 專用的實例包括該實例的數(shù)據(jù)的共享,還可以共享配置,用戶管理,租戶自己的功能和非功能屬性。多租戶和多實例架構(gòu)相比,多租戶分離了代表不同的租戶操作的多個實例。
多租戶用于創(chuàng)建Saas(Software as-a service)應(yīng)用(云處理)。有幾種類型的多租戶:
多部署-多數(shù)據(jù)庫
這實際上不是多租戶。但是,如果我們?yōu)?strong>每個具有分開數(shù)據(jù)庫的客戶(租戶)運行該應(yīng)用的一個實例,那么我們可以在單個服務(wù)器上為多個租戶提供服務(wù)。我們可以確定該應(yīng)用的多個實例在相同的服務(wù)器環(huán)境不會相互沖突。
這個對于一個不是為多租戶設(shè)計的已存在應(yīng)用也是可能的。創(chuàng)建這么一個應(yīng)用更容易,因為該應(yīng)用不需要了解多租戶。但這種方式存在安裝,使用和維護問題。
單部署-多數(shù)據(jù)庫
在這種情況下,我們可以在一個服務(wù)器上運行應(yīng)用的單個實例。對于每個登錄用戶,我們從master database中檢測該用戶的租戶,并獲得該租戶的數(shù)據(jù)庫信息(連接字符串)。然后我們可以將連接字符串存儲到像session一樣的變量中,同時,使用這個租戶特定的連接字符串執(zhí)行所有的數(shù)據(jù)庫操作。
某種程度上,這樣的應(yīng)用應(yīng)該設(shè)計成多租戶。但是大多數(shù)的應(yīng)用都獨立于多租戶。這種方式也存在一些安裝,使用和維護問題。我們應(yīng)該為每個租戶創(chuàng)建并維護一個分離的數(shù)據(jù)庫。
單部署-單數(shù)據(jù)庫
這是最真實的多租戶架構(gòu):我們只將具有單個數(shù)據(jù)庫應(yīng)用的單個實例部署到單個服務(wù)器上。在(RDBMS)每個表中,都存在一個TenantId(或相似)字段,該字段用于分離每個租戶之間的數(shù)據(jù)。
這種方法安裝和維護都很簡單,但唯獨創(chuàng)建這么一個應(yīng)用很難,因為我們必須要阻止一個租戶讀取或?qū)懭肫渌鈶舻臄?shù)據(jù)。我們可以為每個數(shù)據(jù)庫的讀?。╯elect)操作添加一個TenantId過濾器。而且,我們可以在每次寫入的時候檢查一下該實體是否和當(dāng)前的租戶相關(guān)。這是乏味而易于出錯的,但ABP通過使用自動的數(shù)據(jù)過濾幫助我們處理這個事情。
如果我們有很多具有大量數(shù)據(jù)的租戶,那么這種方法可能會有性能問題。我們可以使用關(guān)系型數(shù)據(jù)庫的表分割特征或者將租戶按組分到不同的服務(wù)器上。
ABP中的多租戶
ABP提供了創(chuàng)建單部署,單數(shù)據(jù)庫,多租戶架構(gòu)的基礎(chǔ)設(shè)施。
開啟多租戶
多租戶默認(rèn)是關(guān)閉的。我們可以在模塊的PreInitialize方法中開啟,如下所示:
Configuration.MultiTenancy.IsEnabled = true;
租主vs租戶
首先,我們應(yīng)該定義多租戶系統(tǒng)中的兩個條目:
- 租主(Host):租主是單例的(只有一個租主)。租主會對創(chuàng)建和管理租戶負(fù)責(zé)。因此,一個“租主用戶”比所有的租戶等級更高,并獨立于所有租戶,同時還能控制他們。
- 租戶(Tenant):租主的一個客戶,具有自己的用戶角色,權(quán)限,設(shè)置等。每個租戶都可以完全獨立于其他租戶使用應(yīng)用。一個多租戶應(yīng)用會有一個或多個租戶。如果是一個CRM應(yīng)用,那么不同的租戶也有它們自己的賬戶,契約,產(chǎn)品和訂單。因此,當(dāng)我們說“**租戶用戶”的時候,意思就是一個租戶擁有的用戶。
Session
ABP定義了一個獲取當(dāng)前用戶和租戶id的IAbpSession接口。該接口用于多租戶獲取當(dāng)前的租戶id。因此,它可以基于當(dāng)前的租戶id過濾數(shù)據(jù)。ABP中有以下規(guī)則:
- 如果UserId和TenantId都是null,那么當(dāng)前的用戶沒有登錄到系統(tǒng)。因此,我們可以不知道當(dāng)前用戶是否是一個租主用戶還是一個租戶用戶。在這種情況下,用戶不能訪問授權(quán)的內(nèi)容。
- 如果UserId不是null,TenantId是null,那么當(dāng)前用戶是一個租主用戶。
- 如果UserId不是null,TenantId也不是null,那么當(dāng)前用戶是租戶用戶。
更多關(guān)于session的信息請看后面的Session一節(jié)。
數(shù)據(jù)過濾器
當(dāng)從數(shù)據(jù)庫中檢索實體時,我們必須添加一個TenantId過濾器來只獲得當(dāng)前的租戶實體。當(dāng)你為實體實現(xiàn)了IMustHaveTenant和IMayHaveTenant兩個接口之一時,ABP會自動地完成數(shù)據(jù)過濾。
IMustHaveTenant接口
該接口通過定義TenantId屬性來區(qū)分不同租戶的實體。一個實現(xiàn)了IMustHaveTenant的實體例子如下:
public class Product : Entity, IMustHaveTenant
{
public int TenantId { get; set; }
public string Name { get; set; }
//...其他屬性
}
這樣,ABP知道這是一個特定租戶的實體,并且會自動地將一個租戶的實體從其他實體中分離出來。
IMayHaveTenant接口
我們可能需要在租戶和租戶之間共享一個實體類型。因此,一個實體可能會被一個租戶或租主擁有。IMayHaveTenant接口也定義了TenantId(類似于IMustHaveTenant),但在這種情況下是nullable。實現(xiàn)了IMayHaveTenant的一個實體例子:
public class Role : Entity, IMayHaveTenant
{
public int? TenantId { get; set; }
public string RoleName { get; set; }
//...其他屬性
}
我們可能會使用相同的Role類來存儲租主角色和租戶角色。這種情況下,TenantId表明這是一個租戶實體還是一個租主實體。null值表示這是一個租主實體,非null值表示這被一個租戶擁有,該租戶的Id是TenantId。
IMayHaveTenant不像IMustHaveTenant一樣常用。比如,一個Product類可以不實現(xiàn)IMayHaveTenant接口,因為Product和實際的應(yīng)用功能相關(guān),和管理租戶不相干。因此,要小心使用IMayHaveTenant接口,因為它更難維護租戶和租主共享的代碼。
保存實體
一個租戶用戶不應(yīng)該創(chuàng)建或編輯其他租戶的實體。如果相關(guān)的數(shù)據(jù)過濾器開啟了,那么ABP會檢查該實體相對于數(shù)據(jù)庫的改變。
想要獲得更多關(guān)于數(shù)據(jù)過濾器的信息,請看后面關(guān)于數(shù)據(jù)過濾器的博客。
|