实现多租户系统的一点思考

2020年突发的新冠疫情,让在线协同办公在疫情期间成为了刚需。我们也从 2020 年的 2月3 日开始在家远程办公,直到四月份。协同办公软件一下子火爆了起来,钉钉、企业微信、特别是腾讯会议等都在疫情期间表现突出,呈现出井喷式的发展。

目前大部分的企业信息化都是私有化部署,局限于企业的内部网络,无法实现远程协同办公,所以越来越多的 To B 企业逐步转向 SaaS(Software-as-a-Service,软件即服务),SaaS 最早是美国Salesforce公司(1999年创立)创造的新软件服务模式。这家公司的市值在 2019 年已经超过1000亿美元,国内现在还处在发展中阶段,前景还是十分广阔的。

要将传统的私有化部署的软件重构成支持 SaaS 模式,多租户是一个迈不过去的坎,首先需要将系统改造成多租户模式,然后再逐步实现计费、系统监控、用户行为分析等功能。

我觉得多租户的设计应该分为三个层面来进行讨论,应用、数据库和中间件。

应用

现在的项目或产品开发几乎都是前后端分离的开发模式,应用层主要指的是 WebAPI ,WebAPI 的改造有两种方式:

1、每个租户部署一套 WebAPI、上层通过域名或 Url 地址的解析进行路由,当有新租户注册的时候就动态进行对应的 WebAPI 的部署,这种方式改造成本低,但运维成本高,不建议使用,如果时间紧,可以当过度阶段的临时方案。

2、所有的租户共用一套 WebAPI ,在 WebAPI 中需要获取到租户信息(域名、Url参数、请求头信息、Cookie 等),然后进行租户信息配置的切换。有新租户创建的时候无需进行新的 WebAPI 的创建,只需要初始化租户基本信息即可。

在这种方式下,如果 Cluster1 的负载超过限度了,也要能够进行动态切换,将其中的某些租户切换到其他的 Cluester 中,如上图。

在 WebAPI 的代码实现上,可以参考 Abp 框架中多租户的实现,这里给出一个简化版本:

TenantConfiguration:租户配置信息

[Serializable]
public class TenantConfiguration
{public Guid Id { get; set; }public string Code { get; set; }public string Name { get; set; }public TenantStatus TenantStatus { get; set; }public string DBConfig { get; set; }public string CacheConfig { get; set; }public string MQConfig { get; set; }public string MongoConfig { get; set; }public TenantConfiguration(){TenantStatus = TenantStatus.Enable;}public TenantConfiguration(Guid id, string name): this(){Id = id;Name = name;}
}

TenantStore:从缓存或数据库中获取租户配置信息

public interface ITenantStore
{TenantConfiguration Find(string code);
}
public class TenantStore : ITenantStore
{public TenantConfiguration Find(string code){//从缓存或数据库进行租户配置信息获取throw new NotImplementedException();}
}

CurrentTenant:当前租户类,用来存储当前租户信息,以及切换租户

public interface ICurrentTenant
{TenantConfiguration Config { get;}IDisposable Change(string code);
}
/// <summary>
/// 当前租户
/// </summary>
public class CurrentTenant:ICurrentTenant
{public ITenantStore _tenantStore;public CurrentTenant(ITenantStore tenantStore){_tenantStore = tenantStore;}public TenantConfiguration _config;public TenantConfiguration Config => _config;/// <summary>/// 切换租户/// </summary>/// <param name="code"></param>/// <returns></returns>public IDisposable Change(string code){TenantConfiguration tenantConfig= _tenantStore.Find(code);if (tenantConfig == null){throw new Exception("Tenant not found");}if (tenantConfig.TenantStatus != TenantStatus.Enable){throw new Exception("Tenant is disabled or deleted");}return new DisposeAction(() =>{_config = tenantConfig;});}
}

UrlTenantResolve:根据 Url 参数进行租户解析

public interface ITenantResolve
{string Resolve(HttpContext httpContext);
}
/// <summary>
/// 
/// </summary>
public class UrlTenantResolve:ITenantResolve
{public string Resolve(HttpContext httpContext){return httpContext.Request.QueryString.HasValue? httpContext.Request.Query["__tenant"].ToString(): null;}
}

MultiTenancyMiddleware:租户中间件,关于在 dotNET Core 中自定义中间件可以参考《dotNET Core 3.X 请求处理管道和中间件的理解》

public class MultiTenancyMiddleware: IMiddleware
{protected readonly ITenantResolve _tenantResolve;private readonly ICurrentTenant _currentTenant;public MultiTenancyMiddleware(ITenantResolve tenantResolve,ICurrentTenant currentTenant){_tenantResolve = tenantResolve;_currentTenant = currentTenant;}public async Task InvokeAsync(HttpContext context, RequestDelegate next){var tenantCode = _tenantResolve.Resolve(context);if (tenantCode != _currentTenant.Config.Code){using (_currentTenant.Change(tenantCode)){await next(context);}}else{await next(context);}await next(context);}
}

数据库

数据库在这里指的是关系型数据库,用来存储业务数据,实现多租户,就要对数据进行隔离,通常的数据隔离方式有三种模式:

1、完全隔离,每个租户使用独立数据库;

2、部分共享,租户共享一个数据库,以 schema 或者 table 区分;

3、完全共享,租户共享相同的数据库表,以 tenant_id 进行区分

推荐使用第一种或第二种,隔离程度比较高,也比较容易做横向扩展,如果是第三种,需要处理数据的隔离问题,需要处理单表大数据的问题等,对技术要求比较高。

中间件

除了数据库,一个系统还需要依赖其他的一些中间件,比如缓存、消息队列、文件存储:

  • 缓存:Redi

  • 消息队列:RabbitMQ

  • 文件存储:MongoDB 的 GridFS

Redis

1、Redis 中使用数据库的方式进行租户隔离;

2、Redis 可以通过修改配置文件的方式进行数据库的扩展,默认为 16 个;3、通过 Redis 分片集群的方式进行部署,可以进行横向扩展;3、在 Redis 集群中,官方推荐节点数量不超过 1000 个,这个对于多租户系统的前期来说应该是够用了,如果到了租户数量的爆发期,再进行架构的扩展,比如,不同的租户路由到不同的 Redis 集群中。

RabbitMQ

在 Rabbitmq 有 vhost 机制,可以一个租户创建一个vhost,通过 vhost 来进行租户的隔离,目前还没查询到 vhost 是否有上限,需要做进一步验证。

MongoDB

MongoDB 中主要使用 GridFS 来进行非结构化数据的存储,通过创建数据库的方式来进行租户的隔离,而且 MongoDB 支持分片的集群部署方式,可以进行扩展横扩展,在前期,一个 MongoDB 集群应该就够用了。

最后

技术方案和架构没有最好的,只有最适合的,符合当下的业务场景、团队的技术能力就可以,然后要做的就是做 MVP (最小可行性产品),进而进行系统的改造。

希望本文对您有所帮助!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/302359.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mysql cast 四舍五入_MySQL数据库中CAST与CONVERT函数实现类型转换的讲解

MySQL 的CAST()和CONVERT()函数可用来获取一个类型的值&#xff0c;并产生另一个类型的值。两者具体的语法如下&#xff1a; CAST(value as type);CONVERT(value, type);就是CAST(xxx AS 类型), CONVERT(xxx,类型)。可以转换的类型是有限制的。这个类型可以是以下值其中的一个&…

都说Python库千千万,这几个你认识不?

目前&#xff0c;人工智能的应用日渐广泛。而作为人工智能核心的机器学习&#xff0c;是一门多领域的交叉学科&#xff0c;专门研究计算机模拟或实现人类学习行为的方法&#xff0c;以获取新的知识或技能&#xff0c;重新组织已有的知识结构使之不断改善自身的性能。简单来说&a…

Js黑客帝国效果 文字下落 制作过程和思路

效果预览: http://jsfiddle.net/dtdxrk/m8R6b/embedded/result/ Js黑客帝国效果 文字向下落制作过程和思路 1.css控制文字竖显示 2.动态添加div 用随机数当文本 3.获取分辨率 把div随机分布到屏幕里 4.随机文字的大小和透明度 5.用setInterval定时替换文本 改变div的top值 6.定…

C# 8.0 默认接口实现

C# 8.0 默认接口实现IntroC# 8.0 开始引入了默认接口实现&#xff0c;也就是可以在接口里写方法实现。在之前的版本中接口上是没有办法定义实现的&#xff0c;方法也都是 public 的&#xff0c;除了接口和属性之外是不能定义其他数据的&#xff0c;这也意味着&#xff0c;接口从…

50款大数据分析神器 :你还在用Excel

全世界只有3.14 % 的人关注了数据与算法之美你平时用什么大数据分析工具&#xff1f;D3&#xff1f; R&#xff1f; 还是Processing&#xff1f;PS和计算器...只有你还在用excel&#xff01;工欲善其事&#xff0c;必先利其器&#xff01;一款好的工具可以让你事半功倍。大数据…

WEB安全测试软件

为什么80%的码农都做不了架构师&#xff1f;>>> 五种必会软件&#xff1a; SubSonic CodeSmith Professional 4.1 HttpWatch Professional IE Developer Toolbar Fiddler 是一个web调试代理。它能够记录所有客户端和服务器间的http请求&#xff0c;允许你监视&…

python区域增长算法_区域增长算法

嘿大家好。我真的很难搞清楚这个逻辑&#xff0c;希望你能帮我。在我继续之前&#xff0c;我只想告诉你&#xff0c;我是业余程序员&#xff0c;也是一个初学者&#xff0c;没有任何形式的正式计算机科学培训&#xff0c;所以请容忍我。&#xff1a;D另外&#xff0c;我使用的是…

P6砖家:对不起,我没.NET5高并发经验,我要跑路了!

“秒杀活动”“抢红包”“微博热搜”“12306抢票”“共享单车拉新”等都是高并发的典型业务场景&#xff0c;那么如何解决这些业务场景背后的难点问题呢&#xff1f;秒杀系统中&#xff0c;QPS达到10万/s时&#xff0c;如何定位并解决业务瓶颈&#xff1f;明星婚恋话题不断引爆…

孩子觉得数学难?那是底子没打好!

孩子觉得数学难&#xff1f;那是底子没打好&#xff01;&#xff08;北师大学前教育博士帮你一起塑造孩子的数学思维&#xff01;&#xff09;要说陪娃写作业这事儿的状态和成果&#xff0c;用一句诗词就能概括&#xff1a;我本将心向明月&#xff0c;奈何明月照沟渠。陪得好&a…

Android 读取meta-data元素的数据

在AndroidManifest.xml中&#xff0c;<meta-data>元素可以作为子元素&#xff0c;被包含在<activity>、<application> 、<service>和<receiver>元素中&#xff0c;但 不同的父元素&#xff0c;在应用时读取的方法也不同。 1 &#xff1a;在Activ…

python科学计算环境配置_ATLAS + NumPy + SciPy + Theano 的Python科学计算环境搭建

Theano是一个Python库&#xff0c;提供了定义、优化以及评估数学表达式的库&#xff0c;尤其适合处理高维数组。使用Theano能获得和C差不多的处理速度&#xff0c;并且当利用GPU进行计算时&#xff0c;效率要优于CPU上运行的C语言程序。利用Theano能快速验证各种算法模型。但是…

朋友圈有趣的灵魂都去哪了?这几个优质公号给你答案

全世界有3.14 % 的人已经关注了数据与算法之美又到每周限量推荐公众号的时间啦关注了那么多公众号&#xff0c;百无聊奈地看文章你是否觉得时间被浪费&#xff0c;生命被辜负了&#xff1f;在号的数量上做减法&#xff0c;质量上做加法接下来给大家推荐最近一直在阅读的几个优质…

Istio 1.10 发布及官网改版

本文译自 Istio 官方文档 [1]&#xff0c;有部分修改。北京时间 5 月 19 日&#xff0c;我们很高兴地宣布 Istio 1.10 的发布&#xff01;我们要特别感谢我们的发布经理 Sam Naser[2] 和 张之晗 [3]&#xff0c;以及整个测试和发布工作组在 1.10 中的工作。这是我们 2021 年的第…

CSS各属性表

1、CSS 背景属性&#xff08;Background&#xff09; 属性描述CSSbackground在一个声明中设置所有的背景属性。1background-attachment设置背景图像是否固定或者随着页面的其余部分滚动。1background-color设置元素的背景颜色。1background-image设置元素的背景图像。1backgrou…

java接口课程_用java定义一个接口,用于查询课程

定义一个类Stu&#xff0c;包括如下属性&#xff1a;学号、姓名、性别、专业、课程&#xff0c;实现以下方法&#xff1a;每个属性的获取和定义&#xff0c;要求至少包含一个构造函数。定义一个接口类&#xff0c;定义方法qcc()用来查询课程。编写一...定义一个类 Stu&#xff…

深入探讨编程到底需要知道多少数学知识

全世界只有3.14 % 的人关注了数据与算法之美这篇文章中我会深入探讨编程中所需要的数学知识。你可能已经都知道了。对于基本的编程&#xff0c;你需要知道下面的&#xff1a;加减乘除 — 实际上&#xff0c;电脑会帮你作加减乘除运算。你仅需要知道什么时候运用它们。模运算 —…

sql server和mysql的区别是什么

开源MySQL是一个开源关系数据库管理系统&#xff08;RDBMS&#xff09;&#xff1b;而SQL Server不是开源的&#xff0c;是商业的。程序MySQL主要用C和C 编程语言编程。SQL Server主要用C 编程&#xff0c;但在C语言中也有一些部分。平台SQL Server仅支持Linux和Windows平台&am…

zabbix2.0安装与配置

一、zabbix服务端安装&#xff1a;官方下载&#xff1a;http://www.zabbix.com/download.php1.安装配置所需要软件(zabbix需要一个lamp环境)[rootlocalhost ~]# yum install httpd php php-devel php-gd php-bcmath php-mbstring mysql-devel mysql-serverphp-xml php-mysql gd…

sql java 创建数据库_java动态创建数据库(sql server)

public static void main(String[] args) {// TODO Auto-generated method stubString url "jdbc:jtds:sqlserver://127.0.0.1:1433;databaseNamebase_name;usersa;password pas";//sa身份连接Connection con null;Statement stmt null;//ResultSet rs null;bool…

BeetleX.FastHttpApi之控制器调度设计

为了可以更灵活地在Webapi应用服务中分配线程资源&#xff0c;BeetleX.FastHttpApi在线程调度上直接细化到Action级别&#xff1b;组件不仅可以精准控制每个Action的最大RPS限制&#xff0c;还能精细到控制使用多少线程资源来处理这些API的请求。接下来详细讲解组件针对这一块的…