SaaS 架构、多租户架构使用文档说明书

“多租户技术或称多重租赁技术” 是一种软件架构技术,是实现如何在多组织环境下共用相同的系统或程序组件,并且可确保各租户间数据的隔离性。在当下云计算时代,多租户技术在共用的数据中心以单一系统架构与服务提供多数客户端相同甚至可定制化的服务,并且仍可以保障客户的数据隔离。目前各种各样的云计算服务就是这类技术范畴。

实现方案及原理

多租户的数据隔离方案,不外乎以下三种方案(来自网络转载)分别是:

1)独立数据库

这是第一种方案,即一个租户一个数据库,这种方案的用户数据隔离级别最高,安全性最好,但成本较高。

优点: 为不同的租户提供独立的数据库,有助于简化数据模型的扩展设计,满足不同租户的独特需求;如果出现故障,恢复数据比较简单。

缺点: 增多了数据库的安装数量,随之带来维护成本和购置成本的增加。这种方案与传统的一个客户、一套数据、一套部署类似,差别只在于软件统一部署在运营商那里。如果面对的是银行、医院等需要非常高数据隔离级别的租户,可以选择这种模式,提高租用的定价。如果定价较低,产品走低价路线,这种方案一般对运营商来说是无法承受的。

2)共享数据库,隔离数据架构

这是第二种方案,即多个或所有租户共享 DataBase,但是每个租户一个 Schema(也可叫做一个user)。

优点: 为安全性要求较高的租户提供了一定程度的逻辑数据隔离,并不是完全隔离;

缺点: 如果出现故障,数据恢复比较困难,因为恢复数据库将牵涉到其他租户的数据;如果需要跨租户统计数据,存在一定困难。

这是第三种方案,即租户共享同一个 DataBase、同一个 Schema,但在表中增加 corp_code 多租户的数据字段。这是共享程度最高、隔离级别最低、维护成本和购置最低的模式。

优点: 三种方案比较,第三种方案的维护和购置成本最低,跨租户统计方便,允许每个数据库支持的租户数量多。

缺点: 隔离级别最低,需要在设计开发时加大对安全的开发量;对单个租户的数据备份和恢复困难。

如果希望以最少的服务器为最多的租户提供服务,并且租户接受牺牲隔离级别换取降低成本,这种方案最适合。

JeeSite 是如何选择的?

通过上述三种方案分析,JeeSite 更倾向于共享数据库共享表方式,因为他最易用,租户之间数据共享更方便,维护运营成本最低。

若考虑租户数据非常大时,可考虑分区分表,以及可使用数据库中间件(TiDB、MyCat、Sharding-JDBC)来处理大数据量的分布式存储。

若你比较倾向于其它方案,官方可提供专业定制。

JeeSite 中如何使用多租户?

开启多租户,打开 application.yml 文件,设置如下:

通过上述的介绍,我们已了解 JeeSite 对多租户的实现方式是表结构数据共享方式,对于租户数据个性化的每张表,都需要有 corp_code 和 corp_name 租户字段,进行数据分离。

这两个字段不需要开发人员手动维护,在 java 代码中,只需要在实体类的 中增加 @Column(includeEntity=BaseEntity.class) 即可,有平台自动维护,不需要开发人员编写相应代码。

  1. public String getCorpCode() {
  2. if (StringUtils.isBlank(corpCode)){
  3. corpCode = CorpUtils.getCurrentCorpCode();
  4. return corpCode;
  5. }
  6. if (StringUtils.isBlank(corpName)){
  7. corpName = CorpUtils.getCurrentCorpName();
  8. }
  9. return corpName;
  10. }

以 Office 举例,引入 includeEntity=BaseEntity.class 列,如下:

再来看下 include 的 BaseEntity 实体,系统已经定义了 corp_code 和 corp_name 字段:

  1. @Table(columns={
  2. @Column(name="corp_name", attrName="corpName", label="租户名称", isUpdate=false, isQuery=false),
  3. })
  4. public abstract class BaseEntity<T> implements Serializable {
  5. }

所以在 Office 中也自动的包含了 corp_code 和 corp_name 这两个字段。

有了这些配置:

  • 当执行 officeService.save() 或 insert() 保存时,系统会自动将 corp_code 和 corp_name 租户字段及当前租户信息,添加到 insert 语句里,不需要你再去通过 set 方式去维护它。

  • 当执行 officeService.findList() 等相关查询方法时,系统会自动将 corp_code 租户字段添加到 where 子句里,自动去过滤当前租户信息,不需要你再去通过 set 方式去设置当前租户。

  • 若查询时,想不想包含 corp_code 字段,你想进行查询全部租户的信息,你只需调用 entity.getSqlMap().getWhere().disableAutoAddCorpCodeWhere() 即可禁用 corp_code 条件的自动添加。

那如何新增一个租户?

用超级管理员(system)登录,进入菜单 “系统管理 -> 权限管理 -> 系统管理员” 可进行新增租户管理员,新增的租户管理员只可管理自己领域内的数据(如:部门、公司、用户、自定义的字典数据等)。

租户管理员关联角色

平台中引入了租户分配角色的功能,也就是说可以让不同的租户管理员,可以看到的菜单是不同的。

租户管理员平台也默认了一个角色,这个角色是所有租户管理员都有的,如下配置:

如何获取租户名称列表?

  1. User where = new User();