多租户解析与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

相关推荐

  • 网站动静加速架构 dcdn+ga 全站加速和全球加速api

    # 背景 我们的公司提供的所有服务均位于香港,这意味着我们的客户,主要分布在中国内地,访问这些服务时可能会遇到速度较慢的问题。由于我们专注于NFT领域,因此选择在香港提供服务。 # 一、加速策略 ## 1.1 静态资源加速 静态资源加速是指对如HTML、JavaScript、CSS和图像文件等静态文件的快速分发。利用云服务提供商的CDN服务,我们可以有效地提…

    未分类 2024 年 12 月 24 日
    26700
  • Java ArrayList 与顺序表:在编程海洋中把握数据结构的关键之锚

    我的个人主页我的专栏:Java-数据结构 ,希望能帮助到大家!!!点赞❤ 收藏❤ 前言 :在 Java编程的广袤世界里,数据结构犹如精巧的建筑蓝图,决定着程序在数据处理与存储时的效率、灵活性以及可扩展性。其中,ArrayList和顺序表作为线性数据结构的典型代表,犹如两颗璀璨的明星,在众多数据处理场景中熠熠生辉。 顺序表,以其简洁而直观的连续内存存储方式,为…

    2024 年 12 月 28 日
    22100
  • Java:IO流详解

    文章目录 基础流 1、IO概述 1.1 什么是IO 1.2 IO的分类 1.3 顶级父类们 2、字节流 2.1 一切皆为字节 2.2 字节输出流 OutputStream 2.3 FileOutputStream类 2.3.1 构造方法 2.3.2 写出字节数据 2.3.3 数据追加续写 2.3.4 写出换行 2.4 字节输入流 InputStream 2.…

    未分类 2025 年 5 月 13 日
    8800
  • 数据结构与算法–顺序表(Java)

    什么是顺序表? 顺序表 是一种线性表 的数据结构。 顺序表通过一组连续地址 的存储单元依次存储 线性表中的数据元素。 顺序表的主要特点: 逻辑上相邻的元素在物理位置上也相邻。 可以随机访问表中的任意元素,通过元素的位置序号可以在 O(1) 的时间复杂度内直接获取对应元素。 插入和删除操作的效率相对较低。例如,在顺序表的中间位置插入一个元素,需要移动大量后续元…

    未分类 2024 年 12 月 28 日
    21400
  • 2024年 Java 面试八股文(20w字)

    🍅我是小宋, 一个只熬夜但不秃头的Java程序员。🍅关注我,带你过面试,读源码。提升简历亮点(14个demo) 🍅我的面试集已有12W+ 浏览量。🌏号:tutou123com。拉你进面试专属群。 📖微信公众号:小宋编码 目录 第一章-Java基础篇 1、你是怎样理解OOP面向对象 难度系数:⭐ 2、重载与重写区别 难度系数:⭐ 3、接口与抽象类的区别 难度系…

    2024 年 12 月 28 日
    19100

发表回复

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

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

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

关注微信