【WTM-多租户改造】
嗨。第一次写文章,给了WTM,先简单介绍下WTM吧。
WTM框架地址 https://wtmdoc.walkingtec.cn
支持4个版本:Layui React Vue Blazor
WtmPlus是建立在WTM开源框架基础上的低代码开发平台,他提供了可视化的模型和页面编辑,更加复杂和智能的代码生成,可使开发效率提升50%以上。
感兴趣可以看一下 地址 WtmPlus
第一次写,写的不太好,大家见谅哈。
19年就开始接触WTM框架了,刚开始没有仔细了解。20年开始买亮哥的课程,开始看视频看文档慢慢的对WTM layui版本逐渐熟悉。也不知道说什么好,WTM牛逼,亮哥牛逼。
框架真的很好,希望大家可以一直支持WTM。
简单说下多租户的实现方式
多租户(Multi-Tenant ),即多个租户共用一个实例,租户的数据既有隔离又有共享,说到底是要解决数据存储的问题。
常用的数据存储方式有三种。
方案一:独立数据库
一个Tenant,一个Database的数据存储方式。隔离级别最高、最安全,但成本也高。
优点:
a.为不同租户提供独立数据库,有助于简化数据模型的扩展设计,满足个性化需求;
b.数据恢复简单;
缺点:增大了数据库的安装数量,购置和维护成本高;
方案二:共享数据库,隔离数据架构
多个租户或所有租户共享Database,但一个Tenant,一个Schema的方式。
优点:
a.一定程度的逻辑数据隔离(并非完全),可满足较高程度的安全性保障;
b.每个数据库,可支持更多租户数量;
缺点:
a.恢复数据较困难,因为将牵扯到其他租户数据;
b.跨租户统计数据,实现难度大;
方案三:共享数据库,共享数据架构
一种租户共享同一个Database、同一个Schema,而另行通过TenantID区分租户数据的方式。
优点:
a.每个数据库可支持租户数量多,维护和购置成本低;
缺点:
a. 隔离级别低,安全性低,开发时需做大量安全开发工作;
b. 逐表逐条备份和还原数据,数据备份和恢复困难。
今天主要讲的就是用WTM 改造简易的多租户,我这里用的是Layui版本,其他UI也可以用这种方式实现,我还没有试过,大家有空可以自己试一试。我用的是方案一 独立数据库方式。技术有限,只是希望在这里可以给大家提供一个思路。
开始说下整体步骤
咱们先来创建一个租户表,我这里简单创建几个字段,为了演示,大家根据实际需要自己调整。我这里为了演示方便租户角色直接用系统自带的角色表了,大家自己可以增加一个租户角色表。
public class Tenant : BasePoco{[Display(Name = "编号")][Required(ErrorMessage = "{0}是必填项")]public string Code { get; set; }[Display(Name = "域名")][Required(ErrorMessage = "{0}是必填项")]public string DomainName { get; set; }[Display(Name = "租户角色")][Required(ErrorMessage = "{0}是必填项")]public Guid RoleId { get; set; }[Display(Name = "租户角色")]public FrameworkRole Role { get; set; }[Display(Name = "账号")][Required(ErrorMessage = "{0}是必填项")]public string Account { get; set; }[Display(Name = "名称")][Required(ErrorMessage = "{0}是必填项")]public string Name { get; set; }}
添加完租户信息后,Create方法里需要创建好 这个租户的库、基本信息和提供的域名。我这里租户的库生成规则直接就是默认用主库名+编号生成的。
[HttpPost][ActionDescription("Sys.Create")]public ActionResult Create(TenantVM vm){using (var trans = DC.BeginTransaction()){if (!ModelState.IsValid){return PartialView(vm);}else{vm.DoAdd();if (!ModelState.IsValid){vm.DoReInit();return PartialView(vm);}else{//我这代码直接写这了 生成租户库和基本信息 随便写了下简单的系统表数据var NDC = new DataContext(Wtm.ConfigInfo.Connections[0].Value.Replace("SAASDEMODB", "SAASDEMODB" + vm.Entity.Code), DBTypeEnum.SqlServer);var Result = NDC.Database.EnsureCreated();if (Result){var role = DC.Set<FrameworkRole>().Where(x => x.ID == vm.Entity.RoleId).FirstOrDefault();//角色拥有的菜单权限var pr = DC.Set<FunctionPrivilege>().Where(x => x.RoleCode == role.RoleCode).ToList();var user = new FrameworkUser{ITCode = vm.Entity.Account,Password = Utils.GetMD5String("000000"),IsValid = true,Name = vm.Entity.Name};var userrole = new FrameworkUserRole{UserCode = vm.Entity.Account,RoleCode = role.RoleCode};NDC.Set<FrameworkUser>().Add(user);NDC.Set<FrameworkRole>().Add(role);NDC.Set<FrameworkUserRole>().Add(userrole);//这里框架自带角色表 页面权限FunctionPrivilege表没有加父级菜单数据 会导致约束冲突。后期自己添加租户角色表吧NDC.Set<FrameworkMenu>().AddRange(DC.Set<FrameworkMenu>().CheckContain(pr.Select(x => x.MenuItemId).ToList(), x => x.ID).ToList());NDC.Set<FunctionPrivilege>().AddRange(pr);NDC.SaveChanges();//云解析DNS-添加解析记录if (!new CommonHelp().DomainNameResolution(vm.Entity.Code)){trans.Rollback();return FFResult().CloseDialog().RefreshGrid().Alert("域名解析失败!");}}else{trans.Rollback();return FFResult().CloseDialog().RefreshGrid().Alert("租户信息初始化失败!");}trans.Commit();return FFResult().CloseDialog().RefreshGrid();}}}}
主要用到了一个云解析DNS-添加解析记录的方法。调用AddDomainRecord根据传入参数添加解析记录。
云解析 DNS(Domain Name System,简称DNS) 是一种安全、快速、稳定、可靠的权威DNS解析管理服务。 它能够帮助企业和开发者将易于管理识别的域名转换为计算机用于互连通信的数字IP地址,从而将用户的访问路由到相应的网站或应用服务器。
具体看文档 添加解析记录 (aliyun.com)
#region 云解析DNS-添加解析记录 可以去阿里云地址看文档 https://help.aliyun.com/document_detail/29772.html 但是这种方式服务器要求 80端口只允许部署这一套系统,因为现在这种方式是域名直接指向服务器IPpublic bool DomainNameResolution(string Name){IClientProfile profile = DefaultProfile.GetProfile("cn-hangzhou", "", "");//域名 AccessKeyID SecretDefaultAcsClient client = new DefaultAcsClient(profile);var request = new AddDomainRecordRequest();request._Value = ""; //指向服务器IPrequest.Type = "A";request.RR = Name; //随便定义 request.DomainName = "xxx.com"; //域名try{var response = client.GetAcsResponse(request);return true;//Console.WriteLine(System.Text.Encoding.Default.GetString(response.HttpResponse.Content));}catch (ServerException e){return false;}catch (ClientException e){return false;}}#endregion
这种方式不好的一点是 服务器要求 80端口只允许部署这一套系统,因为现在这种方式是域名直接指向服务器IP。我是部署在IIS上,需要注意的一点是应用中不要绑定主机名。(如果大家有更好的办法可以一起沟通沟通)
到这里创建的这个租户的库和基本信息和域名就创建好了。
这个时候所有域名都可以访问到部署的系统了,但是appsettings.json文件中Connections只有一个默认的库,当然不可能添加一个租户就在这加一个连接字符串,不现实。
正好框架支持动态选择连接字符串。框架可以根据页面传递过来的数据,或者session里的信息等动态选择需要连接的数据库,只需编辑Startup文件中的CSSelector方法。
访问系统肯定会先读主库,我这里是根据域名去租户表里查,如果存在就动态添加一个ConnectionStrings,利用Wtm.Session.Set("TenantKey", CS.key);,否则就正常访问主库。
#region 获取当前urlpublic string GetAbsoluteUri(HttpRequest request){return new StringBuilder().Append(request.Scheme).Append("://").Append(request.Host).Append(request.PathBase).Append(request.Path).Append(request.QueryString).ToString();}#endregion[Public][ActionDescription("Login")]public IActionResult Login(){LoginVM vm = Wtm.CreateVM<LoginVM>();string TenantKey = "default";string displayUrl = GetAbsoluteUri(HttpContext.Request);displayUrl = displayUrl.Replace("http://", "").Replace("https://", "") + "/";displayUrl = displayUrl.Substring(0, displayUrl.IndexOf("/"));var ZDC = new DataContext(Wtm.ConfigInfo.Connections[0].Value, DBTypeEnum.SqlServer);var Tenant = ZDC.Set<Tenant>().Where(x => x.DomainName.Replace("http://", "").Replace("https://", "") == displayUrl).FirstOrDefault();if (Tenant != null){TenantKey = "SAASDEMODB" + Tenant.Code;Wtm.Session.Set("TenantKey", "SAASDEMODB" + Tenant.Code);}else{Wtm.Session.Set("TenantKey", TenantKey);}int i = 0;foreach (var item in Wtm.ConfigInfo.Connections){if (item.Key == TenantKey){i++;break;}}if (i == 0){CS cs = new CS();cs.DbContext = "DataContext";cs.DbType = DBTypeEnum.SqlServer;cs.Key = TenantKey;cs.Value = Wtm.ConfigInfo.Connections[0].Value.Replace("SAASDEMODB", cs.Key);cs.DcConstructor = Wtm.ConfigInfo.Connections[0].DcConstructor;Wtm.ConfigInfo.Connections.Add(cs);}vm.Redirect = HttpContext.Request.Query["ReturnUrl"];if (Wtm.ConfigInfo.IsQuickDebug == true){vm.ITCode = "admin";vm.Password = "000000";}return View(vm);}
Startup文件中的CSSelector方法
public string CSSelector(ActionExecutingContext context){var wtm = (context.Controller as IBaseController)?.Wtm;var TenantKey = wtm.Session.Get<string>("TenantKey");return TenantKey;}
OK,咱们来测试一下下。
我这里就用默认超级管理员角色创建租户了,为了添加一个租户让大家看下效果。
添加成功,访问一下租户的地址
添加一条新的角色数据,跟主库作下比较,发下数据已经隔离了。
如果你跟着测试到这一步,说明已经通了,可以自己多试试。有问题或者有好的想法,可以在群里一起沟通学习学习。
有些可能需要用到数据共享,框架本身支持在控制器中设置[FixConnection(DBOperationEnum.Default, CsName = "")]设置Cs指定连接字符串。
项目我已经上传到Gitee上了,大家可以下载看一下。
地址:wtm-layui版本多租户: wtm框架 layui版本都租户改造
下载完项目,如果想直接运行调试的话,记得去Common文件下的CommonHelp类中把DomainNameResolution方法中的参数补充全,就可以直接运行看效果了。
目前这种方式有正式的项目,目前也比较稳定。第一次写文章,希望大家多支持哈。
有什么问题想法,大家在WTM群里沟通哈。谢谢,最后也感谢亮哥、感谢WTM哈,感谢有这么好的框架。
【WTM-多租户改造】相关推荐
- Jeecg-boot 2.4.6+ 多租户改造方案(涉及菜单部门角色等基础模块)
场景: jeecg boot提供了多租户的配置,但是并没有完整实现该功能,此文就原系统表的菜单.部门.角色.用户为例实现多租户功能实现方案. 修改菜单表:sys_permission 增加两个字段te ...
- TPP稳定性之场景隔离和多租户
TPP有3600+个场景,每个场景是一些AB(算法方案代码+业务配置+流量分配策略)的集合,场景按业务团队划分物理集群,同一个物理集群内的容器是对等的,JVM内部署着算法容器,算法容器内混布相同的场 ...
- 多租户实现之基于Mybatis、Mycat的共享数据库,共享数据架构
阅读文本大概需要3分钟. 前言 SaaS模式是什么? 传统的软件模式是在开发出软件产品后,需要去客户现场进行实施,通常部署在局域网,这样开发.部署及维护的成本都是比较高的. 现在随着云服务技术的蓬勃发 ...
- 软骨鱼颠覆传统软件SaaS化改造之路:30分钟快速SaaS化
什么是SaaS SaaS就是将应用软件统一部署在云端服务器上,客户可以根据工作实际需求,通过互联网向厂商定购所需的应用软件服务,按定购的服务多少和时间长短向厂商支付费用,并通过互联网获得Saas平台供 ...
- docker部署nacos单机版
记录下使用docker-compose安装nacos的过程,nacos版本2.0.2, Mysql数据库为5.8版本,是本地电脑的数据库,没有使用docker版本.项目结果如下: 1 创建nacos需 ...
- zk Acl权限:只有一个账号有crdwa权限,匿名用户只有r权限
2019独角兽企业重金招聘Python工程师标准>>> 起因 最近在做多租户改造,租户使用的配置项都要放到zk上(如数据库配置.redis配置.阿里oss配置.每个租户的域名配置等) ...
- docker搭建nacos1.4.1挂载文件并实现mysql持久化
之前已经搭建过nacos环境,但没有进行配置持久化mysql,这篇文章完善一下. 一.拉取镜像 docker pull nacos/nacos-server:1.4.1 二.创建挂载目录 /mnt/n ...
- 基于 Knative 打造生产级 Serverless 平台 | KubeCon NA2019
简介: 本文基于 Knative 构建一个优秀的 Serverless 计算平台,详细分析如何用独特的技术,解决性能.容量.成本三大问题.欢迎阅读- 本文推荐知道的背景知识: Kubernetes 的 ...
- Nacos配置管理-nacos集群搭建
Nacos集群搭建 1.集群结构图 官方给出的Nacos集群图: 其中包含3个nacos节点,然后一个负载均衡器代理3个Nacos.这里负载均衡器可以使用nginx. 我们计划的集群结构: 三个nac ...
最新文章
- 作业 3 利用分支和循环结构解决问题
- 轻松搞定面试中的红黑树问题
- 加载dict_PyTorch 7.保存和加载pytorch模型的两种方法
- python图像插值
- mac 完全卸载mysql
- 正则判断字符串是否为数字
- Gtk:利用alignment控制button大小
- 编程语言python怎么读-0编程基础,什么语言也没学过,请问学Python怎样入门?...
- 【例7.4】 循环比赛日程表
- python学习第七天--文件系统常用模块os,os.path,pickle
- rbf神经网络_黄小龙,陈阳舟:高阶非线性不确定多智能体系统自适应RBF神经网络协同控制...
- 六.全文检索ElasticSearch经典入门-高亮
- 什么是Base64算法?——全网最详细讲解
- imageview显示服务器图片,imageview 直接加载图片流
- 什么软件测试充电器快不快,手机充电的快慢到底取决于什么?充电头还是数据线?看完就知道了...
- Go | 一分钟掌握Go | 9 - 通道
- 下载微软虚拟学院视频字幕,解决本地播放没有字幕
- spring的工作原理介绍
- 世界十大最美历史遗迹
- 中国科学技术大学计算机科学夏令营,中国科学技术大学计算机科学2019年推免夏令营通知...