多租户(Multi-Tenancy)是一种软件架构技术,它是在探讨与实现如何于多用户的环境下共用相同的系统或程序组件,并且仍可确保各用户间数据的隔离性。
实现多租户主要有三中方案,分别为:
1、独立数据库(database)
每个租户有不同数据库,这种方案的用户数据隔离级别最高,安全性最好,但成本也高
2、共享数据库,隔离数据架构(scheme)
多个或所有租户共享同一个数据库,但每个租户有不同的数据架构(scheme)
3、共享数据库和数据架构
所有租户共享相同的数据库和数据架构,但在数据表中通过TenantID区分租户的数据。这种方案成本最低,共享程度最高、隔离级别最低的模式
xormt基于xorm实现多租户,主要思路是
1、根据TenantId保存租户DB的map,需要同步实体类列表
相关对象定义
var(
xdbMaps map[string]*TenantDBInfo = nil
xdbMutex sync.Mutex
xSyncModels []interface{} = nil
xSyncModelsMutex sync.Mutex
xTenantIdResolver TenantIdResolver = nil
)
2、对外提供TenantDBProvider(租户DB提供者)和TenantIdResolver(租户ID解析器),实现初始化租户DB的map和获取租户ID
/**
* @brief: 租户数据库提供者
*/
type TenantDBProvider func()[]*TenantDBInfo
/**
* @brief: 住户id解析器
*/
type TenantIdResolver func(*gin.Context)string
初始化时传入上面两个函数实现
/**
* @brief: 初始化
* @param1 provider: 租户数据库提供者
* @param2 idResolver: 租户id解析器
* @return: 错误信息
*/
func Init(provider TenantDBProvider, idResolver TenantIdResolver)error {
if provider == nil {
return &ErrParamEmpty{"provider"}
}
if idResolver == nil{
xTenantIdResolver = defaultIdResolver
}else{
xTenantIdResolver = idResolver
}
tdbs := provider()
hasDefault := false
for i := range tdbs {
Add(tdbs[i])
if tdbs[i].Tid == "default" {
hasDefault = true
}
}
if !hasDefault {
return &ErrDeaultTendarMissing{}
}
return nil
}
3、对gin.HandlerFunc和gin.Context进行封装,实现对请求的拦截并获取对应的租户DB
/**
* @brief: 多租户上下文
* 集成gin.Context
*/
type MultiTenantContext struct {
*gin.Context
DB *xorm.Engine
}
/**
* @brief: 多租户处理函数
*/
type MultiTenantHandlerFunc func(*MultiTenantContext)
1、在main函数中调用Init进行初始化,并传入相关TenantDBProvider和TenantIdResolver的具体实现
func main() {
ginEngine := gin.Default()
xormt.Init(xormtext.GetTenants, xormtext.GetTenantId)
router.Register(ginEngine)
ginEngine.Run(":8080")
}
2、需要同步实体类中调用AddModel
func init(){
xormt.AddModel(new (User))
}
3、映射router,此时需要使用xormt.HandlerGin返回gin.HandlerFunc
v1.GET("/users/:id", xormt.HandlerGin(ctrl.Get))
4、实现controller处理函数,参数为*xormt.MultiTenantContext
func (c *UserController) Get(ctx *xormt.MultiTenantContext) {
i++
user := &entity.User{}
user.Id = util.UUID()
user.Name = fmt.Sprintf("huangrf%d", i)
user.LoginId = fmt.Sprintf("huangrf%d", i)
user.Major = fmt.Sprintf("major%d", i)
_, err := ctx.DB.InsertOne(user)
if err != nil{
ctx.JSON(500, &entity.Resp{500, err.Error(), nil})
}else{
ctx.JSON(http.StatusOK, &entity.Resp{http.StatusOK, "", user.Id})
}
}
基于xorm和gin实现多租户支持,基本实现相关功能,还有其他地方可以改进,例如:数据库读写分离,数据库联通性检测等
xormt仓库地址:https://github.com/hrf304/xormt.git
实例仓库地址:https://github.com/hrf304/ukid.git