互聯網廠商在進行公有云saas服務部署的時候,往往會面對多租戶的場 景,多租戶場景的設計,在架構上一般分為二個層次:
1、計算集群的多租戶
計算集群的多租戶顧名思義,就是針對不同的租戶提供單獨的服務集群,不同租戶之間cpu相互獨立,個別租戶的流量洪峰不會影響其它租戶正常服務。
計算集群的多租戶實現有很多種方案,一般做法是,根據不同租戶辟出獨立集群,對外暴露統一的CDN域名或BGP域名,經流量調度到計算中心內部,由Nginx集群或網關根據URL中的租戶參數,向租戶所在集群轉發請求。
由于多租戶的場景的普遍性,最近幾年新涌現的新框架和平臺在設計伊始就考慮了多租戶的場景,并集成了相關技術,比如k8的namespace,結合calico,可以實現非常靈活的多租戶計算分組。
2、數據層的多租戶
拿MySQL來舉例,數據層的多租戶一般有三種做法:
1)一個租戶一個數據庫
2)一個租戶一個schema
3)多個租戶同一個schema
其中區別,可以查詢相關資料,網上很多,一般Saas服務從成本等因素考慮,大多數采用第3種方案,其主要標志為在元數據設計中,有多租戶字段tenant_id。
3、多租戶的數據層訪問攔截
這里以JAVA+MyBatis來說明。
Mybatis plugin插件支持攔截所有提交到Dao層的SQL,并支持對提交的SQL進行改寫,原理類似于Spring的Interceptor,多租戶Mybatis插件能夠在運行時,動態獲取到應用上下文中的租戶變量,在執行CRUD操作時,自動將多租戶字段(tenant_id)條件附加到sql語句之中,如where條件之后。
1)使用mybatis插件的優點
使用mybatis插件,可以大大簡化業務代碼在多租戶改造過程中的開發成本,對比硬編碼方式,mApper和dao層無須改造,service層的代碼改造量也可以大大降低。
2)原理
通過mybatis plugin攔截sql處理步驟和result處理步驟。sql預處理,自動增加tenant_id相關語句,result預處理,驗證數據tenant_id字段符合要求。包括:
- insert into,增加寫入tenant_id數據
- select 自動在where之后附加條件tenant_id = ?
- update 在where之后附加條件tenant_id
- delete 在where之后附加條件tenant_id
- left join
- 子查詢
等等。
3)實現方式
插件用的是責任鏈模式,由每一個對象對其下家的引用而連接起來形成一條鏈,請求在這個鏈上傳遞,直到鏈上的某一個對象決定處理此請求。
具體實現方式為,實現接口Interceptor.java 的3個方法,分別是plugin、intercept和setProperties。
- intercept:它將直接覆蓋你所攔截的對象,有個參數Invocation對象,通過該對象,可以反射調度原來對象的方法;
- plugin:target是被攔截的對象,它的作用是給被攔截對象生成一個代理對象;
- setProperties:允許在plugin元素中配置所需參數,該方法在插件初始化的時候會被調用一次
實現類需要添加幾個注解,來攔截對應的簽名:
然后實現Interceptor接口的方法即可,通過Plugin工具類方便生成代理類,通過MetaObject工具類方便操作四大對象的屬性,修改對應的值。
然后實現Plugin方法,生成代理類的方法是通過MyBatis提供的Plugin工具類,它實現了InvocationHandler接口(JDK動態代理的接口),看看它的2個方法:
Plugin提供了靜態方法wrap方法,它會根據插件的簽名配置,使用JDK動態代理的方法,生成一個代理類,當四大對象執行方法時,會調用Plugin的invoke方法,如果方法包含在聲明的簽名里,就會調用自定義插件的intercept方法,傳入Invocation對象。
最后,插件的初始化時在MyBatis初始化的時候完成的,讀入插件節點和配置的參數,使用反射技術生成插件實例,然后調用插件方法中的setProperties方法設置參數,并將插件實例保存到配置對象中.
4、最后分享一下很不錯的mybatis多租戶插件,當然,你也可以自己實現
https://github.com/Mearalu/mybatis-multi-tenancy






