互聯(lián)網(wǎng)廠商在進行公有云saas服務部署的時候,往往會面對多租戶的場 景,多租戶場景的設計,在架構(gòu)上一般分為二個層次:
1、計算集群的多租戶
計算集群的多租戶顧名思義,就是針對不同的租戶提供單獨的服務集群,不同租戶之間cpu相互獨立,個別租戶的流量洪峰不會影響其它租戶正常服務。
計算集群的多租戶實現(xiàn)有很多種方案,一般做法是,根據(jù)不同租戶辟出獨立集群,對外暴露統(tǒng)一的CDN域名或BGP域名,經(jīng)流量調(diào)度到計算中心內(nèi)部,由Nginx集群或網(wǎng)關(guān)根據(jù)URL中的租戶參數(shù),向租戶所在集群轉(zhuǎn)發(fā)請求。
由于多租戶的場景的普遍性,最近幾年新涌現(xiàn)的新框架和平臺在設計伊始就考慮了多租戶的場景,并集成了相關(guān)技術(shù),比如k8的namespace,結(jié)合calico,可以實現(xiàn)非常靈活的多租戶計算分組。
2、數(shù)據(jù)層的多租戶
拿MySQL來舉例,數(shù)據(jù)層的多租戶一般有三種做法:
1)一個租戶一個數(shù)據(jù)庫
2)一個租戶一個schema
3)多個租戶同一個schema
其中區(qū)別,可以查詢相關(guān)資料,網(wǎng)上很多,一般Saas服務從成本等因素考慮,大多數(shù)采用第3種方案,其主要標志為在元數(shù)據(jù)設計中,有多租戶字段tenant_id。
3、多租戶的數(shù)據(jù)層訪問攔截
這里以JAVA+MyBatis來說明。
Mybatis plugin插件支持攔截所有提交到Dao層的SQL,并支持對提交的SQL進行改寫,原理類似于Spring的Interceptor,多租戶Mybatis插件能夠在運行時,動態(tài)獲取到應用上下文中的租戶變量,在執(zhí)行CRUD操作時,自動將多租戶字段(tenant_id)條件附加到sql語句之中,如where條件之后。
1)使用mybatis插件的優(yōu)點
使用mybatis插件,可以大大簡化業(yè)務代碼在多租戶改造過程中的開發(fā)成本,對比硬編碼方式,mApper和dao層無須改造,service層的代碼改造量也可以大大降低。
2)原理
通過mybatis plugin攔截sql處理步驟和result處理步驟。sql預處理,自動增加tenant_id相關(guān)語句,result預處理,驗證數(shù)據(jù)tenant_id字段符合要求。包括:
- insert into,增加寫入tenant_id數(shù)據(jù)
- select 自動在where之后附加條件tenant_id = ?
- update 在where之后附加條件tenant_id
- delete 在where之后附加條件tenant_id
- left join
- 子查詢
等等。
3)實現(xiàn)方式
插件用的是責任鏈模式,由每一個對象對其下家的引用而連接起來形成一條鏈,請求在這個鏈上傳遞,直到鏈上的某一個對象決定處理此請求。
具體實現(xiàn)方式為,實現(xiàn)接口Interceptor.java 的3個方法,分別是plugin、intercept和setProperties。

- intercept:它將直接覆蓋你所攔截的對象,有個參數(shù)Invocation對象,通過該對象,可以反射調(diào)度原來對象的方法;
- plugin:target是被攔截的對象,它的作用是給被攔截對象生成一個代理對象;
- setProperties:允許在plugin元素中配置所需參數(shù),該方法在插件初始化的時候會被調(diào)用一次
實現(xiàn)類需要添加幾個注解,來攔截對應的簽名:

然后實現(xiàn)Interceptor接口的方法即可,通過Plugin工具類方便生成代理類,通過MetaObject工具類方便操作四大對象的屬性,修改對應的值。

然后實現(xiàn)Plugin方法,生成代理類的方法是通過MyBatis提供的Plugin工具類,它實現(xiàn)了InvocationHandler接口(JDK動態(tài)代理的接口),看看它的2個方法:

Plugin提供了靜態(tài)方法wrap方法,它會根據(jù)插件的簽名配置,使用JDK動態(tài)代理的方法,生成一個代理類,當四大對象執(zhí)行方法時,會調(diào)用Plugin的invoke方法,如果方法包含在聲明的簽名里,就會調(diào)用自定義插件的intercept方法,傳入Invocation對象。
最后,插件的初始化時在MyBatis初始化的時候完成的,讀入插件節(jié)點和配置的參數(shù),使用反射技術(shù)生成插件實例,然后調(diào)用插件方法中的setProperties方法設置參數(shù),并將插件實例保存到配置對象中.
4、最后分享一下很不錯的mybatis多租戶插件,當然,你也可以自己實現(xiàn)
https://github.com/Mearalu/mybatis-multi-tenancy