多租户解析与Demo

在做Saas应用时,多租户解析往往是很重要的组成部分,也是用户访问网站最先处理的逻辑。

文前介绍:

多租户的数据库实现方式主要有三种:

  1. 单一数据库实现,每条数据标识租户Id进行识别数据属于哪个租户
  2. 一租户一个数据库,能够做到完全的数据隔离
  3. 混合模式,部分数据在一张表上,主要是一些基础数据;其他业务数据分库存储。

无论是哪种方式都要知道租户是谁才能查询数据库。

获取租户的方式也可以有多种:

  1. 根据域名或者子域名不同可以获知租户,上面的所有场景都适合使用;(必须要有域名和dns服务)
  2. 根据用户id获取租户信息,从而存入前端的cookie中或者header中,只适用于上面的1、3方式,因为需要不同租户的用户都存入一种表中,方便查询对应的租户信息;

下文中的例子只是简单的一个例子,没有进一步的业务场景,实现功能如下:

用户根据不同的域名进入系统;

后台拿刀http传入的域名并解析;

查询租户数据库,查找出租户名称返回显示。

新建解决方案,并其下新增三个项目:

try_MultiTenantApi:webapi项目,项目的启动项目,为了方方便,租户的Service和IService放入了这个项目中,实际应用时要放入业务层;

MultiTenantApi.Models:类库项目,存放租户对象;

MultiTenantApi.Data:数据层,使用efcore,存放上下文DbContext和迁移文件

多租户解析与Demo

MultiTenantApi.Models中新增实体Tenant

```
public class Tenant
{
    public int Id { get; set; }
    public string Identifier { get; set; } // 租户标识符,例如域名
    public string Name { get; set; }
    public string ConnectionString { get; set; } // 每个租户的独立数据库连接字符串
}
```

MultiTenantApi.Data:

引入nuget包:要注意跟你项目的.net版本相同

多租户解析与Demo

这些包的具体说明:

  1. Microsoft.EntityFrameworkCore

    • 功能: Entity Framework Core 是一个现代的对象-关系映射器(ORM),用于 .NET。它支持 LINQ 查询、变更跟踪、更新和模式迁移。EF Core 可以与多种数据库一起工作,包括 SQL Server、Azure SQL Database、SQLite 和 Azure Cosmos DB。
    • 作用: 提供了核心的 ORM 功能,用于在 .NET 应用程序中与数据库进行交互。
    • Microsoft.EntityFrameworkCore.Design

    • 功能: 提供了 Entity Framework Core 工具的共享设计时组件。这些组件包括用于创建和管理迁移的工具。

    • 作用: 用于在设计时(如运行迁移命令时)提供必要的工具支持,帮助开发者更高效地管理和生成数据库迁移。
    • Microsoft.EntityFrameworkCore.Proxies

    • 功能: 为 Entity Framework Core 提供延迟加载代理。这些代理用于在需要时加载相关对象,从而减少内存使用和提高性能。

    • 作用: 通过代理机制实现延迟加载,提高应用程序的性能和响应速度。
    • Microsoft.EntityFrameworkCore.SqlServer

    • 功能: 提供了针对 Microsoft SQL Server 的数据库提供程序。这使得 Entity Framework Core 能够与 SQL Server 数据库进行交互。

    • 作用: 为 SQL Server 数据库提供特定的数据库提供程序,确保 EF Core 能够正确地与 SQL Server 交互。
    • Microsoft.EntityFrameworkCore.Tools

    • 功能: 提供了 Entity Framework Core 工具,用于 NuGet Package Manager Console 中的 Visual Studio。

    • 作用: 通过 Visual Studio 的 NuGet Package Manager Console 提供 EF Core 工具,帮助开发者更方便地管理和使用 EF Core 的设计时工具。

新增数据上下文:

```
public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext(DbContextOptions options)
    : base(options)
    {
    }
    public DbSet Tenants { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity().HasData(
            new Tenant { Id = 1, Identifier = "tenant1", Name = "Tenant 1", ConnectionString = "Server=(localdb)\mssqllocaldb;Database=TenantDb1;Trusted_Connection=True;" },
            new Tenant { Id = 2, Identifier = "tenant2", Name = "Tenant 2", ConnectionString = "Server=(localdb)\mssqllocaldb;Database=TenantDb2;Trusted_Connection=True;" }
        );
    }
}
```

注意实际应用中上面的数据库连接字符串根据你的具体情况变动一下,因为本例子没有用到这个字段所以想要尝试可以不用改。

try_MultiTenantApi:

引入nuget包:

多租户解析与Demo

新建类TenantService:

```
public interface ITenantService
{
    Task GetTenantByIdentifierAsync(string identifier);
}

public class TenantService : ITenantService
{
    private readonly ApplicationDbContext _context;

    public TenantService(ApplicationDbContext context)
    {
        _context = context;
    }

    public async Task GetTenantByIdentifierAsync(string identifier)
    {
        return await _context.Tenants.FirstOrDefaultAsync(t => t.Identifier == identifier);
    }
}
```

新建一个中间件TenantMiddleware:

用于每次的请求解析出租户信息

```
namespace try_MultiTenantApi
{
    public class TenantMiddleware
    {
        private readonly RequestDelegate _next;

        public TenantMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task InvokeAsync(HttpContext context, ITenantService tenantService)
        {
            var subdomain = GetSubdomain(context.Request.Host.Value);
            if (!string.IsNullOrEmpty(subdomain))
            {
                var tenant = await tenantService.GetTenantByIdentifierAsync(subdomain);
                if (tenant != null)
                {
                    context.Items["Tenant"] = tenant;
                }
            }

            await _next(context);
        }

        private string GetSubdomain(string host)
        {
            var parts = host.Split('.');
            return parts.Length > 2 ? parts[0] : null;
        }
    }
}

public static class TenantMiddlewareExtensions
{
    public static IApplicationBuilder UseTenantMiddleware(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware();
    }
}
```

配置appsetting文件,配置租户数据库连接字符串:

```
"ConnectionStrings": {
  "DefaultConnection": "Data Source=localhost;Initial Catalog=try_MultiTenant;User ID=sa;Password=******;Encrypt=False;"
},
```

修改Program:

```
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddDbContext(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddScoped();

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}


app.UseHttpsRedirection();

app.UseRouting();

app.UseAuthorization();

app.UseTenantMiddleware(); // 使用租户解析中间件
app.MapControllers();

app.Run();
```

新建一个controller:ValuesController

```
[ApiController]
[Route("[controller]")]
public class ValuesController : ControllerBase
{
    private readonly ITenantService _tenantService;

    public ValuesController(ITenantService tenantService)
    {
        _tenantService = tenantService;
    }

    [HttpGet(nameof(Get1))]
    public async Task Get1()
    {
        var tenant = HttpContext.Items["Tenant"] as Tenant;
        if (tenant == null)
        {
            return NotFound("Tenant not found");
        }

        return Ok(new { Message = $"Hello from {tenant.Name}" });
    }
}
```

OK,至此项目就编码完成,接下来就是进行数据迁移。因为前面上下文中已经设置了出事测试数据

add-migration init

update-database

到此,租户的数据库已经创建,并且也有了初始的测试数据:

多租户解析与Demo

为了测试方便咱们在launch启动文件中配置两个租户的访问地址:

```
"profiles": {
  "http": {
    "commandName": "Project",
    "dotnetRunMessages": true,
    "launchBrowser": true,
    "launchUrl": "swagger",
    "applicationUrl": "http://localhost:5252;http://tenant1.Paas.JBWL.com:5252",
    "environmentVariables": {
      "ASPNETCORE_ENVIRONMENT": "Development"
    }
  },
}
```

接下来设置try_MultiTenantApi项目为启动项目,启来后出swagger页面:

多租户解析与Demo

页面地址是默认第一个http://localhost:5252/

下面我们就可以修改一下本地的host文件映射两个域名,可以使用swichhost,也可以找到本地的host文件,增加地址映射

```
127.0.0.1 tenant1.Paas.JBWL.com
127.0.0.1 tenant2.Paas.JBWL.com
```

启用后访问

```
tenant1.Paas.JBWL.com:5252
```

出现前面一模一样的页面,访问一下测试接口看是否解析成功:

多租户解析与Demo

成功!

接下来试一下tenant2.paas.jbwl.com:5252

多租户解析与Demo

至此,这个测试demo也就完成了,只要解析出租户信息,接下来Saas的租户数据就能获取了。

文章整理自互联网,只做测试使用。发布者:Lomu,转转请注明出处:https://www.it1024doc.com/4895.html

(0)
LomuLomu
上一篇 2024 年 12 月 30 日 上午9:21
下一篇 2024 年 12 月 30 日 上午10:22

相关推荐

  • (Java)jdk8下载安装与环境变量配置(手把手教程)

    目录 一.jdk8的下载 1.点击我的阿里云盘链接进行下载jdk8u231 2.官网下载jdk8(比较繁琐,可以直接去我的云盘下载) 以下为官网下载方式: (1.)第一步:点击下载链接,点击以后会来到这个页面 (2).第二步:往下滑,找到如图所示的jdk8 (3.)第三步:点击Java SE 8 (8u211 and later),来到这个页面直接下 滑 (…

    2025 年 1 月 21 日
    83300
  • WxPython跨平台开发框架之图标选择界面

    在使用 wxPython 开发跨平台桌面应用程序时,创建一个图标选择界面 通常用于让用户从图标资源库中选择图标,我们可以把图标分为自定义的图标资源和系统的图标资源两大类,最终我们把它们整合一起使用,在框架的界面中使用,包括工具栏、右键菜单、按钮、图片等所需的地方显示,实现图文并茂的友好界面展示。本篇随笔介绍这两种图标资源的管理和使用过程。 1、图标分类介绍 …

    2025 年 1 月 6 日
    44400
  • 数据结构(Java版)第二期:包装类和泛型

    目录 一、包装类 1.1. 基本类型和对应的包装类 1.2. 装箱和拆箱 1.3. 自动装箱和自动拆箱 二、泛型的概念 三、引出泛型 3.1. 语法规则 3.2. 泛型的优点 四、类型擦除 4.1. 擦除的机制 五、泛型的上界 5.1. 泛型的上界的定义 5.2. 语法规则 六、泛型方法 6.1. 定义语法 6.2. 交换方法的实例 七、通配符 包装类和泛型…

    2025 年 1 月 1 日
    48200
  • 实战指南:理解 ThreadLocal 原理并用于Java 多线程上下文管理

    目录 一、ThreadLocal基本知识回顾分析 (一)ThreadLocal原理 (二)既然ThreadLocalMap的key是弱引用,GC之后key是否为null? (三)ThreadLocal中的内存泄漏问题及JDK处理方法 (四)部分核心源码回顾 ThreadLocal.set()方法源码详解 ThreadLocalMap.get()方法详解 Th…

    2025 年 1 月 22 日
    85700
  • MySQL 面试题

    MySQL 中有哪几种锁? 全局锁、行级锁、自增锁、记录锁、外键锁、间隙锁、表级锁、元数据锁、意向锁、临键锁 MySQL 中有哪些不同的表格? 基础表、临时表、系统表、信息表、性能模式表、分区表、外键表、触发器使用的表、存储过程和函数使用的表 简述在 MySQL 数据库中 MyISAM 和 InnoDB 的区别? 事务支持 InnoDB:支持事务处理,具有提…

    未分类 2025 年 1 月 15 日
    70100

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信