SaaS多租户架构--应用程序
本文主要介绍SaaS架构中除 数据库 之外的开发过程。
租户标识
通常有2个:
- TenantId
- TenantCode
使用建议:
- TenantId:用于数据持久化,对用户不可见
- TenantCode:供用户输入或显示,它与 TenantId 一一对应
租户存储
Nebula的设计目标是一款通用的开发框架,因此对 租户存储 没有任何定义,应用程序可以根据实际业务需求来自由定义数据结构,这里只给出几条参考建议:
- 租户总表存放在 master 数据库,其中不要包含【数据库连接字符串】
- 数据库连接字符串 由配置服务来管理
- 不要在内存中缓存所有租户数据,想像下未来可能会有100W个租户
用户身份
多租户架构中,最核心的就是用户身份的设计,因为一切数据来源都是用户的操作。
Nebula提供了一个接口来定义用户身份信息:
public interface IUserInfo
{
string TenantId { get; }
string UserId { get; }
string UserName { get; }
string UserRole { get; }
其中一个重要的成员 TenantId 就是租户ID,所以当用户执行操作时会知道TA对应哪个租户库。
Nebula提供2个实现类供开发者使用
- WebUserInfo:表示一个普通用户(浏览器前端)
- AppClientInfo:用于第三方的应用程序客户端
如果这些类型的数据成员不能满足你的需求,你可以自行实现这个接口。
当执行用户登录时,调用 AuthenticationManager.Login(...) 方法,第一个参数就是 IUserInfo 类型。
在已登录场景,调用 nhttpContext.GetUserInfo() 返回的就是 登录时 传入的 IUserInfo 对象
URL
核心原则:
- URL地址能区分租户(URL中要包含TenantCode),例如使用场景:链接分享
- 对于已登录用户,URL中的TenantCode必须和用户身份中的TenantId保持一致(注意校验)
建议实现方式:
- 二级域名,例如:http://{TenantCode}.YourApp.com
- URL片段,例如:http://www.YourApp.com/v20/api/xxapp/{TenantCode}/....
RabbitMQ
虽然RabbitMQ有内建的多租户架构支持,即:使用 vhost 来隔离,但是不建议使用这个特性,原因如下:
- 当租户数量较多时,例如 1万个租户,那么 1万个 vhost 在界面中就没法看了!
- 会导致大量的连接,极度浪费连接资源(线程和内存),流量负载也不均衡。
所以,建议的方式是:
- 在消息体中增加一个数据成员 TenantId 来区分租户
- 不同的业务数据,使用不同的队列
- 如果业务的响应时间要求较高,可规划不同的优先级,并创建不同的队列
Redis
建议:
- key 中包含 TenantId 信息
- 根据业务用途,可以使用到不同的Redis实例
OSS存储
建议:
- path格式: bucket/{TenantId}/{业务用途}/{日期-可选部分}/{filename}