ServiceStack 多租户的实现方案
以SqlServer为例子说明ServiceStack实现多租户,在SqlServer中创建4个Database:TMaster、T1,T2,T3,为了安全起见
每个Database不用sa账号,而是用独立的数据库的账号和密码,为了方便演示这密码设置成一样
租户TMaster Database:TMaster 账号密码: User Id=TMaster;Password=t123
租户T1 Database:T1 账号密码: User Id=T1;Password=t123
租户T2 Database:T2 账号密码: User Id=T2;Password=t123
租户T3 Database:T3 账号密码: User Id=T3;Password=t123
创建数据库的方法可以参见文章: https://www.cnblogs.com/tonge/p/3791029.html
每个登陆用自己的账号和密码登陆,其它的数据库是没有访问权限的,这个各个租户是完全隔离的。
假设Node和npm已经安装
npm install -g @servicestack/cli
执行命令dotnet-new selfhost SSHost
这样就创建了ServiceStack的控制台程序,用VS2017解决方案,在ServiceModel的Types文件夹添加TenantConfig类文件

代码如下:
using System;
using System.Collections.Generic;
using System.Text; namespace ssTest.ServiceModel.Types
{
public interface IForTenant
{
string TenantId { get; }
} public class TenantConfig
{
public string Id { get; set; } public string Company { get; set; }
}
}
修改Hello.cs文件,代码如下:
using ServiceStack;
using ssTest.ServiceModel.Types;
using System; namespace ssTest.ServiceModel
{
[Route("/hello")]
[Route("/hello/{Name}")]
public class Hello : IForTenant, IReturn<HelloResponse>
{ public string Name { get; set; } // 实现接口IForTenant(租户标识Id)
public string TenantId { get; set; }
} public class HelloResponse
{
public string Result { get; set; } public DateTime Date { get; set; } // 返回租户公司信息
public TenantConfig Config { get; set; }
}
}
主程序的Startup代码如下
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
} public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
// JsConfig.DateHandler = DateHandler.ISO8601;
// 保证时间类型的字段可以解析成js识别的时间类型
JsConfig<DateTime>.SerializeFn = time => new DateTime(time.Ticks, DateTimeKind.Local).ToString("o");
JsConfig<DateTime?>.SerializeFn =
time => time != null ? new DateTime(time.Value.Ticks, DateTimeKind.Local).ToString("o") : null;
JsConfig.DateHandler = DateHandler.ISO8601; app.UseServiceStack(new AppHost()); app.Run(context =>
{
context.Response.Redirect("/metadata");
return Task.FromResult();
});
}
}
下面就到核心代码了,在主程序中建立多租户Db工程类,让程序可以自动的根据租户Id访问自己的数据库
public class MultiTenantDbFactory : IDbConnectionFactory
{
private readonly IDbConnectionFactory dbFactory; public MultiTenantDbFactory(IDbConnectionFactory dbFactory)
{
this.dbFactory = dbFactory;
} public IDbConnection OpenDbConnection()
{
var tenantId = RequestContext.Instance.Items["TenantId"] as string;
return OpenTenant(tenantId);
} public IDbConnection OpenTenant(string tenantId = null)
{
return tenantId != null
? dbFactory.OpenDbConnectionString($"Data Source=.; Initial Catalog={tenantId};User Id={tenantId};Password=t123;pooling=true;")
: dbFactory.OpenDbConnection();
} public IDbConnection CreateDbConnection()
{
return dbFactory.CreateDbConnection();
}
}
AppHost中加入如下代码,GlobalRequestFilters的作用,根据传入的租户Id来选择相应的数据库,如果租户Id为null,系统自动使用TMaster数据库
InitDb的作用就是初始化三个数据库,创建表TenantConfig并插入一条记录。
public override void Configure(Container container)
{
ConigureSqlserver(container);
} private void ConigureSqlserver(Container container)
{
var dbFactory = new OrmLiteConnectionFactory(
"Data Source=.; Initial Catalog=TMaster;User Id=TMaster;Password=t123;pooling=true;", SqlServerDialect.Provider); const int noOfTennants = ; container.Register<IDbConnectionFactory>(c =>new MultiTenantDbFactory(dbFactory)); var multiDbFactory = (MultiTenantDbFactory)container.Resolve<IDbConnectionFactory>(); using (var db = multiDbFactory.OpenTenant())
InitDb(db, "TMaster", "Masters inc."); for(int i=; i<= noOfTennants; i++)
{
var tenantId = $"T{i}"; using (var db = multiDbFactory.OpenTenant(tenantId))
InitDb(db, tenantId, $"ACME {tenantId} inc.");
} GlobalRequestFilters.Add((req, res, dto) =>
{
var forTennant = dto as IForTenant;
if (forTennant != null)
RequestContext.Instance.Items.Add("TenantId", forTennant.TenantId);
});
}
public void InitDb(IDbConnection db, string tenantId, string company)
{
db.DropAndCreateTable<TenantConfig>();
db.Insert(new TenantConfig { Id = tenantId, Company = company });
}
这样核心代码就完成了,我们用postman调用试试看,是不是达到了预期的效果
body为空,租户Id没有设置,系统认为是默认的数据库TMaster,返回的是Master数据库中的config表信息

body中设置json参数{"name":"joy", "tenantId":"t1"},可以看到查询返回的是数据库T1的信息

我们再试验一下T2,body中设置json参数{"name":"peter", "tenantId":"t2"}

可以很惊喜的看到,查询的是数据库T2的信息
ServiceStack解决方案真是强大,本来一个复杂的多租户问题就这样轻易解决了,是不是很简单。这里例子用的都是sqlserver数据库,实际上每个租户可以使用不同的数据库。
最近一年很流行ABP解决方案,我想说的是ServiceStack解决方案也很优秀,甚至更加优秀,当你了解越多你就会惊叹当初作者的设计思路是多么的优秀,有兴趣的小伙伴可以一起挖掘和分享啊!
ServiceStack 多租户的实现方案的更多相关文章
- 多租户SaaS平台的数据库方案
1.1 多租户是什么 多租户技术(Multi-TenancyTechnology)又称多重租赁技术:是一种软件架构技术,是实现如何在多用户环境下 (此处的多用户一般是面向企业用户)共用相同的系统或程序 ...
- 实现saas多租户方案比较
看到一篇比较多租户数据隔离方案的文章,总结挺不错.其实大部分内容在我前几年写的文章都有. 文章翻译自: https://blog.arkency.com/comparison-of-approache ...
- 数据层的多租户浅谈(SAAS多租户数据库设计)
在上一篇“浅析多租户在 Java 平台和某些 PaaS 上的实现”中我们谈到了应用层面的多租户架构,涉及到 PaaS.JVM.OS 等,与之相应的是数据层也有多租户的支持. 数据层的多租户综述 多租户 ...
- EF Core 实现多租户
目录 SAAS 和多租户 多租户数据隔离方案 使用 EF Core 简单实现多租户 单数据库实现 多数据库实现 源代码 参考 SAAS 和多租户 SaaS(软件及服务)区别于其他应用程序的主要特征就是 ...
- 在 Windows Azure 上设计多租户应用程序
作者:Suren Machiraju 和 Ralph Squillace 审校:Christian Martinez.James Podgorski.Valery Mizonov 和 Michael ...
- [转载]数据层的多租户浅谈(SAAS多租户数据库设计)
原文:http://www.ibm.com/developerworks/cn/java/j-lo-dataMultitenant/index.html 在上一篇“浅析多租户在 Java 平台和某些 ...
- 一种Django多租户解决方案
什么是多租户? 多租户技术或称多重租赁技术,简称SaaS,是一种软件架构技术,是实现如何在多用户环境下(此处的多用户一般是面向企业用户)共用相同的系统或程序组件,并且可确保各用户间数据的隔离性. 多租 ...
- 如何在多模型的情况下进行EF6的结构迁移
所谓多模型就是在一个数据库中包含两个不同模型,或者换句话说就是两个不同DbContext的数据都放到同一个数据库中.这里的多模型不是指多租户的数据库(有谁知道EF很好处理多租户数据库的方案,可以联系我 ...
- DAL – RDBMS 的分区
编辑人员注释:本文章由AzureCAT 云与企业工程组的高级项目经理Shaun Tinline-Jones 和Chris Clayton 共同撰写. "云服务基础"应用程序也称作& ...
随机推荐
- cpp 区块链模拟示例(七) 补充 Merkle树
Merkle 树 完整的比特币数据库(也就是区块链)需要超过 140 Gb 的磁盘空间.因为比特币的去中心化特性,网络中的每个节点必须是独立,自给自足的,也就是每个节点必须存储一个区块链的完整副本.随 ...
- 解析vue2.0中render:h=>h(App)的具体意思
render:h=>h(App)是ES6中的箭头函数写法,等价于render:function(h){return h(App);}. 注意点:1.箭头函数中的this是 指向 包裹this所在 ...
- ueditor 上传图片
ueditor在配置图片,附件上传 首先,是以web项目为基础的,需要安装好eclipse以及tomcat 其次,需要下载ueditor(可去百度官网下载 http://ueditor.baidu. ...
- rpm安装jdk7
原文:http://www.centoscn.com/image-text/config/2015/0208/4658.html 系统环境:centos-6.5 安装方式:rpm安装 软件:jdk-7 ...
- Django框架之序列化和上传文件
一.Django的序列化(对于ajax请求) Django中的序列化主要应用在将数据库中检索的数据返回给客户端用户,特别的Ajax请求一般返回的为Json格式. 1)django序列化的使用方法 . ...
- 2018.11.08 UVA11021 Tribles(概率dp)
传送门 概率dpdpdp简单题. 设f[i]f[i]f[i]表示第iii天的答案. 然后枚举ppp数组从fi−1f_{i-1}fi−1转移过来就行了. 显然有fi=∑j=0npj∗(fi−1)jf_ ...
- CHAPITRE II
J'ai ainsi vécu seul, sans personne avec qui parler véritablement, jusqu'à une panne[pan][机]故障 dans ...
- Arria10_emif
DDR3 由排(Rank),体(Bank),行(Row),列(Column)组成的四维结构. Arria10是第一批支持ddr4的altera Arria10与老器件相比的新结构 (1) 更多的硬( ...
- (转)使用 vs.php 调试PHP
转自:http://www.jb51.net/article/24618.htm 早先在asp横行的年代,php和asp一样,大都都是html中夹杂代码,说实话,这时候IDE的确用处不是很大,倒是 ...
- Java理论学时第一节。课后作业。
设计思路:用nextFloat()函数将从键盘输入的值分别赋给多个变量,然后直接输出相加结果. 流程图: 源代码: 实验结果: