📚
Hoa Framework
  • 一、框架指南
  • 二、功能特性
  • 三、源码结构
  • 四、代码规范
  • 五、入门指南
  • 六、依赖注入/控制反转
  • 七、控制器和服务
  • 八、对象映射指南
  • 九、数据库操作指南
    • 9.1、正向工程(Code First)
    • 9.2、逆向工程(Database First)
    • 9.3、关于仓储(IRepository)
    • 9.4、增删改操作
    • 9.5、查询操作
    • 9.6、DataSet、DataTable 操作
    • 9.7、查询结果集映射
    • 9.8、批量增删改操作
    • 9.9、存储过程、视图、函数操作
    • 9.10、工作单元和事务
    • 9.11、多上下文、读写分离
    • 9.12、切面上下文(TangentDbContext)
    • 9.13、其他操作
    • 9.14、EF Core 高性能
    • 9.15、常见错误
  • 十、开放接口指南
    • 10.1、RESTFul 和 Swagger
    • 10.2、规范化返回值
  • 十一、数据校验
  • 十二、安全授权
  • 十三、异常处理
  • 十四、日志管理
  • 十五、配置管理
  • 十六、缓存管理
  • 十七、内置工具类
    • 17.1、数据加解密
  • 十八、跨域处理
  • 十九、筛选拦截器(未)
  • 二十、进程服务(Daemon)
  • 二十一、编写测试
    • 20.1、单元测试
    • 20.2、基准测试
    • 20.3、性能测试
  • 二十二、托管部署
    • 22.1、IIS 托管部署
    • 22.2、Nginx 托管部署
    • 22.3、Docker 容器部署
  • 二十三、性能分析(MiniProfiler)
  • 二十四、其他功能
    • 23.1、第三方包管理
    • 23.2、文件上传下载
    • 23.3、Razor 视图引擎
    • 23.4、生成客户端请求代码
    • 23.5、快捷操作
  • 二十五、Docker 容器化
    • 25.1、Docker 介绍
    • 25.2、Docker 安装
    • 25.3、Docker 安装服务
    • 25.4、Docker 常用命令
    • 25.6、Docker run 常用命令
    • 25.7、Docker-Compose 介绍
    • 25.8、docker-compose.yml
    • 25.9、Docker-Compose 常用命令
    • 25.10、Docker-Compose 转换 docker run
    • 25.11、Docker 构建自己的镜像
    • 25.12、Dockerfile指南
    • 25.13、Dockerfile 常用命令
    • 25.14、Dockerfile 打包、上传、分享
    • 25.15、Docker 数据卷
    • 25.16、Docker 域网络
    • 25.17、Docker + Nginx 实现分布式集群、负载均衡
  • 二十六、DevOps 持续部署集成
    • 26.1、DevOps 介绍
    • 26.2、持续集成、交付、部署
    • 26.3、Jenkins 介绍
    • 26.4、Jenkins 安装
    • 26.5、Jenkins 初始化
    • 26.6、Jenkins 实战演练
    • 26.7、Jenkis 项目配置
    • 26.8、Jenkins 插件
  • 二十七、OpenXml/Excel 操作
  • 二十八、SaaS 多租户
  • 二十九、Git 代码管理
    • 29.1、Git 介绍
    • 29.2、Git 安装
    • 29.3、Git 基础配置
    • 29.4、Git 工作流程
    • 29.5、Git 重要概念
    • 29.6、Git 创建仓库
    • 29.7、Git 基本操作
    • 29.8、Git 分支管理
    • 29.9、Git 查看提交历史
    • 29.10、Git 标签
    • 29.11、Git 拉取/获取/推送
    • 28.12、Git GUI工具
    • 29.13、Git 私有化部署
    • 29.14、Git 推荐开发模式
    • 29.15、Svn 转 Git
  • 贡献代码
  • 更新日志
由 GitBook 提供支持
在本页
  • 什么是 SaaS
  • 什么是多租户
  • 实现多租户方案
  • 独立数据库
  • 共享数据库,独立 Schema
  • 共享数据库,共享 Schema
  • 如何使用
  • 第一步
  • 第二步
  • 第三步
  • 第四步
  • 第五步
  • 使用说明

这有帮助吗?

二十八、SaaS 多租户

在软件既是服务的时代,多租户模式已经成为发展的趋势。

什么是 SaaS

SaaS是Software-as-a-Service(软件即服务)的简称,随着互联网技术的发展和应用软件的成熟, 在21世纪开始兴起的一种完全创新的软件应用模式。它与“on-demand software”,the application service provider(ASP,应用服务提供商),hosted software(托管软件)所具有相似的含义。

它是一种通过Internet提供软件的模式,厂商将应用软件统一部署在自己的服务器上,客户可以根据自己实际需求,通过互联网向厂商定购所需的应用软件服务,按定购的服务多少和时间长短向厂商支付费用,并通过互联网获得厂商提供的服务。用户不用再购买软件,而改用向提供商租用基于Web的软件,来管理企业经营活动,且无需对软件进行维护,服务提供商会全权管理和维护软件,软件厂商在向客户提供互联网应用的同时,也提供软件的离线操作和本地数据存储,让用户随时随地都可以使用其定购的软件和服务。

对于许多小型企业来说,SaaS是采用先进技术的最好途径,它消除了企业购买、构建和维护基础设施和应用程序的需要。

什么是多租户

多租户技术或称多重租赁技术,简称SaaS,是一种软件架构技术,是实现如何在多用户环境下(此处的多用户一般是面向企业用户)共用相同的系统或程序组件,并且可确保各用户间数据的隔离性。

简单讲:在一台服务器上运行单个应用实例,它为多个租户(客户)提供服务。从定义中我们可以理解:多租户是一种架构,目的是为了让多用户环境下使用同一套程序,且保证用户间数据隔离。那么重点就很浅显易懂了,多租户的重点就是同一套程序下实现多用户数据的隔离。

实现多租户方案

独立数据库

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

优点:

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

缺点:

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

共享数据库,独立 Schema

这是第二种方案,即多个或所有租户共享Database,但是每个租户一个Schema(也可叫做一个user)。底层库比如是:DB2、ORACLE等,一个数据库下可以有多个SCHEMA

优点:

为安全性要求较高的租户提供了一定程度的逻辑数据隔离,并不是完全隔离;每个数据库可支持更多的租户数量。

缺点:

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

共享数据库,共享 Schema

共享数据表 这是第三种方案,即租户共享同一个Database、同一个Schema,但在表中增加TenantID多租户的数据字段。这是共享程度最高、隔离级别最低的模式。 即每插入一条数据时都需要有一个客户的标识。这样才能在同一张表中区分出不同客户的数据。

优点:

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

缺点:

隔离级别最低,安全性最低,需要在设计开发时加大对安全的开发量; 数据备份和恢复最困难,需要逐表逐条备份和还原。

如何使用

在 Hoa Framework 框架中,默认采用了第三种方案实现多租户模式,即:共享数据库,共享Schema。具体配置如下:

第一步

修改配置文件 appsetting.json 文件,启用 EnableTenantMode: true。

第二步

所有的模型都必须直接或间接继承 IEntity<PrimaryKeyType>,如:

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace Hoa.Core.Entities
{
    [Table("Employee")]
    public class Employee : IEntity<int>
    {
        [MaxLength(32)]
        public string Name { get; set; }
        [MaxLength(10)]
        public string Gender { get; set; }
        public int Age { get; set; }
    }
}

第三步

在 HoaDbContext 中,配置 DbSet<Tenant> ,并新增 GetTenantId() 方法。

using Hoa.DbManager.Tenant;
using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;

namespace Hoa.EntityFrameworkCore
{
    public partial class HoaDbContext : DbContext
    {
        public HoaDbContext(DbContextOptions<HoaDbContext> options)
            : base(options)
        {
        }

        // 配置多租户DbSet
        public virtual DbSet<Tenant> Tenants { get; set; }

        // 新增 GetTenantId 方法
        public Guid GetTenantId(string host)
        {
            var tenant = Tenants.FirstOrDefault(t => t.Host == host);
            return tenant?.Id ?? Guid.Empty;
        }

        // Other Codes
    }
}

第四步

在 Hoa.Core.HoaCoreModule.cs 中注册 ITenantProvider 实例类

using Autofac;
using Hoa.DbManager.ContextPool;
using Hoa.DbManager.Repositories;
using Hoa.DbManager.Tangents;
using Hoa.DbManager.Tenant;

namespace Hoa.Core
{
    public class HoaCoreModule : Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            // 注册多租户Id提供器
            builder.RegisterType<TenantProvider>()
            .As<ITenantProvider>()
            .InstancePerLifetimeScope();
        }
    }
}

第五步

在 HoaDbContext 中注入 ITenantProvider并设置全局过滤器,如:

using Hoa.DbManager.Tenant;
using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;

namespace Hoa.EntityFrameworkCore
{
    public partial class HoaDbContext : DbContext
    {
        private Guid _tenantId;
        // 注入 ITenantProvider 
        public HoaDbContext(DbContextOptions<HoaDbContext> options, ITenantProvider tenantProvider)
            : base(options)
        {
            _tenantId = tenantProvider.GetTenantId();
        }

        public virtual DbSet<Tenant> Tenants { get; set; }

        public Guid GetTenantId(string host)
        {
            var tenant = Tenants.FirstOrDefault(t => t.Host == host);
            return tenant?.Id ?? Guid.Empty;
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
                optionsBuilder.UseSqlServer("Name=HoaDatabase");
            }
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            // 配置全局过滤器
            modelBuilder.Entity<Employee>().HasQueryFilter(b => EF.Property<Guid>(b, "TenantId") == _tenantId);

            OnModelCreatingPartial(modelBuilder);
        }

        partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
    }
}

使用说明

通过上面五步配置后,新增、查询操作的时候会自动应用租户Id并插入到表中。

上一页二十七、OpenXml/Excel 操作下一页二十九、Git 代码管理

最后更新于4年前

这有帮助吗?