日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > asp.net >内容正文

asp.net

基于Citus和ASP.NET Core开发多租户应用

發(fā)布時(shí)間:2023/12/4 asp.net 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 基于Citus和ASP.NET Core开发多租户应用 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

  Citus是基于PsotgreSQL的擴(kuò)展,用于切分PsotgreSQL的數(shù)據(jù),非常簡(jiǎn)單地實(shí)現(xiàn)數(shù)據(jù)“切片(sharp)”。如果不使用Citus,則需要開(kāi)發(fā)者自己實(shí)現(xiàn)分布式數(shù)據(jù)訪問(wèn)層(DDAL),實(shí)現(xiàn)路由和結(jié)果匯總等邏輯,借助Citus可簡(jiǎn)化開(kāi)發(fā),是開(kāi)發(fā)者把精力集中在具體的業(yè)務(wù)邏輯上。

  對(duì)于多租戶程序來(lái)說(shuō),Citus可以幫助企業(yè)對(duì)數(shù)據(jù)進(jìn)行切片,相比于傳統(tǒng)的數(shù)據(jù)管理方式,Citus更智能,操作更為簡(jiǎn)單,運(yùn)維成本更低廉。下面演示Citus的簡(jiǎn)單使用。

Step 01 安裝docker和docker-compose(以Docker方式部署Citus)

curl -sSL https://get.docker.com/ | shsudo usermod -aG docker $USER && exec sg docker newgrp `id -gn` sudo systemctl start dockersudo curl -sSL https://github.com/docker/compose/releases/download/1.19.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-composesudo chmod +x /usr/local/bin/docker-compose

Step 02 安裝并啟動(dòng)Citus 

  Citus有3個(gè)版本Citus Community,Citus Cloud(云端版), Citus Enterprise(支持HA等高級(jí)特性),本文使用Citus Community。

curl -sSLO https://raw.githubusercontent.com/citusdata/docker/master/docker-compose.ymldocker-compose -p citus up -d

Step 03 連接postgres

docker exec -it citus_master psql -U postgres

Step 04 設(shè)置數(shù)據(jù)庫(kù)用戶密碼

postgres=# \password postgres ? ? ? ? ?#給postgres用戶設(shè)置密碼 Enter new password: Enter it again:

Step 05 創(chuàng)建表

CREATE TABLE tenants (

? ? id uuid NOT NULL,

? ? domain text NOT NULL,

? ? name text NOT NULL,

? ? description text NOT NULL,

? ? created_at timestamptz NOT NULL,

? ? updated_at timestamptz NOT NULL

);


CREATE TABLE questions (

? ? id uuid NOT NULL,

? ? tenant_id uuid NOT NULL,

? ? title text NOT NULL,

? ? votes int NOT NULL,

? ? created_at timestamptz NOT NULL,

? ? updated_at timestamptz NOT NULL

);


ALTER TABLE tenants ADD PRIMARY KEY (id);

ALTER TABLE questions ADD PRIMARY KEY (id, tenant_id);

Step 06 告知Citus如何對(duì)數(shù)據(jù)進(jìn)行切片

SELECT create_distributed_table('tenants', 'id'); SELECT create_distributed_table('questions', 'tenant_id');

Step 07 初始化數(shù)據(jù)

INSERT INTO tenants VALUES (

? ? 'c620f7ec-6b49-41e0-9913-08cfe81199af',?

? ? 'bufferoverflow.local',

? ? 'Buffer Overflow',

? ? 'Ask anything code-related!',

? ? now(),

? ? now());


INSERT INTO tenants VALUES (

? ? 'b8a83a82-bb41-4bb3-bfaa-e923faab2ca4',?

? ? 'dboverflow.local',

? ? 'Database Questions',

? ? 'Figure out why your connection string is broken.',

? ? now(),

? ? now());


INSERT INTO questions VALUES (

? ? '347b7041-b421-4dc9-9e10-c64b8847fedf',

? ? 'c620f7ec-6b49-41e0-9913-08cfe81199af',

? ? 'How do you build apps in ASP.NET Core?',

? ? 1,

? ? now(),

? ? now());


INSERT INTO questions VALUES (

? ? 'a47ffcd2-635a-496e-8c65-c1cab53702a7',

? ? 'b8a83a82-bb41-4bb3-bfaa-e923faab2ca4',

? ? 'Using postgresql for multitenant data?',

? ? 2,

? ? now(),

? ? now());

Step 08 新建ASP.NET Core Web應(yīng)用程序,并添加引用

  • 安裝“Npgsql.EntityFrameworkCore.PostgreSQL”包

  Npgsql.EntityFrameworkCore.PostgreSQL:支持Entity Framework Core操作PostgreSQL。

  • 安裝“SaasKit.Multitenancy”包

  SaasKit.Multitenancy:支持ASP.NET Core開(kāi)發(fā)多租戶應(yīng)用。

Step 09 創(chuàng)建models

using System;


namespace QuestionExchange.Models

{

? ? public class Question

? ? {

? ? ? ? public Guid Id { get; set; }


? ? ? ? public Tenant Tenant { get; set; }


? ? ? ? public string Title { get; set; }


? ? ? ? public int Votes { get; set; }


? ? ? ? public DateTimeOffset CreatedAt { get; set; }


? ? ? ? public DateTimeOffset UpdatedAt { get; set; }

? ? }

}

using System;


namespace QuestionExchange.Models

{

? ? public class Tenant

? ? {

? ? ? ? public Guid Id { get; set; }


? ? ? ? public string Domain { get; set; }


? ? ? ? public string Name { get; set; }


? ? ? ? public string Description { get; set; }


? ? ? ? public DateTimeOffset CreatedAt { get; set; }


? ? ? ? public DateTimeOffset UpdatedAt { get; set; }

? ? }

}

using System.Collections.Generic;


namespace QuestionExchange.Models

{

? ? public class QuestionListViewModel

? ? {

? ?   public IEnumerable<Question> Questions { get; set; }

? ? }

}

Step 10 創(chuàng)建數(shù)據(jù)上下文

using System.Linq;

using Microsoft.EntityFrameworkCore;

using QuestionExchange.Models;

namespace QuestionExchange

{

? ? public class AppDbContext : DbContext

? ? {

? ? ? ? public AppDbContext(DbContextOptions<AppDbContext> options)

? ? ? ? ? ? : base(options)

? ? ? ? {

? ? ? ? }


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


? ? ? ? public DbSet<Question> Questions { get; set; }


? ? ? ? /// <summary>

? ? ? ? /// C# classes and properties are PascalCase by convention, but your Postgres tables and columns are lowercase (and snake_case).?

? ? ? ? /// The OnModelCreating method lets you override the default name translation and let Entity Framework Core know how to find?

? ? ? ? /// the entities in your database.

? ? ? ? /// </summary>

? ? ? ? /// <param name="modelBuilder"></param>

? ? ? ? protected override void OnModelCreating(ModelBuilder modelBuilder)

? ? ? ? {

? ? ? ? ? ? var mapper = new Npgsql.NpgsqlSnakeCaseNameTranslator();

? ? ? ? ? ? var types = modelBuilder.Model.GetEntityTypes().ToList();


? ? ? ? ? ? // Refer to tables in snake_case internally

? ? ? ? ? ? types.ForEach(e => e.Relational().TableName = mapper.TranslateMemberName(e.Relational().TableName));


? ? ? ? ? ? // Refer to columns in snake_case internally

? ? ? ? ? ? types.SelectMany(e => e.GetProperties())

? ? ? ? ? ? ? ? .ToList()

? ? ? ? ? ? ? ? .ForEach(p => p.Relational().ColumnName = mapper.TranslateMemberName(p.Relational().ColumnName));

? ? ? ? }

? ? }

}

Step 11 為SaaSKit實(shí)現(xiàn)解析器

using System;

using System.Collections.Generic;

using System.Threading.Tasks;

using Microsoft.AspNetCore.Http;

using Microsoft.EntityFrameworkCore;

using Microsoft.Extensions.Caching.Memory;

using Microsoft.Extensions.Logging;

using SaasKit.Multitenancy;

using QuestionExchange.Models;


namespace QuestionExchange

{

? ? public class CachingTenantResolver : MemoryCacheTenantResolver<Tenant>

? ? {

? ? ? ? private readonly AppDbContext _context;


? ? ? ? public CachingTenantResolver(

? ? ? ? ? ? AppDbContext context, IMemoryCache cache, ILoggerFactory loggerFactory)

? ? ? ? ? ? ?: base(cache, loggerFactory)

? ? ? ? {

? ? ? ? ? ? _context = context;

? ? ? ? }


? ? ? ? // Resolver runs on cache misses

? ? ? ? protected override async Task<TenantContext<Tenant>> ResolveAsync(HttpContext context)

? ? ? ? {

? ? ? ? ? ? var subdomain = context.Request.Host.Host.ToLower();


? ? ? ? ? ? var tenant = await _context.Tenants

? ? ? ? ? ? ? ? .FirstOrDefaultAsync(t => t.Domain == subdomain);


? ? ? ? ? ? if (tenant == null) return null;


? ? ? ? ? ? return new TenantContext<Tenant>(tenant);

? ? ? ? }


? ? ? ? protected override MemoryCacheEntryOptions CreateCacheEntryOptions()

? ? ? ? ? ? => new MemoryCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromHours(2));


? ? ? ? protected override string GetContextIdentifier(HttpContext context)

? ? ? ? ? ? => context.Request.Host.Host.ToLower();


? ? ? ? protected override IEnumerable<string> GetTenantIdentifiers(TenantContext<Tenant> context)

? ? ? ? ? ? => new string[] { context.Tenant.Domain };

? ? }

}

Step 12 修改Startup.cs

using Microsoft.AspNetCore.Builder;

using Microsoft.AspNetCore.Hosting;

using Microsoft.EntityFrameworkCore;

using Microsoft.Extensions.Configuration;

using Microsoft.Extensions.DependencyInjection;

using QuestionExchange.Models;


namespace QuestionExchange

{

? ? public class Startup

? ? {

? ? ? ? public Startup(IConfiguration configuration)

? ? ? ? {

? ? ? ? ? ? Configuration = configuration;

? ? ? ? }


? ? ? ? public IConfiguration Configuration { get; }


? ? ? ? // This method gets called by the runtime. Use this method to add services to the container.

? ? ? ? public void ConfigureServices(IServiceCollection services)

? ? ? ? {

? ? ? ? ? ? var connectionString = "Server=192.168.99.102;Port=5432;Database=postgres;Userid=postgres;Password=yourpassword;";


? ? ? ? ? ? services.AddEntityFrameworkNpgsql()

? ? .AddDbContext<AppDbContext>(options => options.UseNpgsql(connectionString));

? ? ? ? ? ? services.AddMultitenancy<Tenant, CachingTenantResolver>();


? ? ? ? ? ? services.AddMvc();

? ? ? ? }


? ? ? ? // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.

? ? ? ? public void Configure(IApplicationBuilder app, IHostingEnvironment env)

? ? ? ? {

? ? ? ? ? ? if (env.IsDevelopment())

? ? ? ? ? ? {

? ? ? ? ? ? ? ? app.UseDeveloperExceptionPage();

? ? ? ? ? ? ? ? app.UseBrowserLink();

? ? ? ? ? ? }

? ? ? ? ? ? else

? ? ? ? ? ? {

? ? ? ? ? ? ? ? app.UseExceptionHandler("/Home/Error");

? ? ? ? ? ? }


? ? ? ? ? ? app.UseStaticFiles();

? ? ? ? ? ? app.UseMultitenancy<Tenant>();

? ? ? ? ? ? app.UseMvc(routes =>

? ? ? ? ? ? {

? ? ? ? ? ? ? ? routes.MapRoute(

? ? ? ? ? ? ? ? ? ? name: "default",

? ? ? ? ? ? ? ? ? ? template: "{controller=Home}/{action=Index}/{id?}");

? ? ? ? ? ? });

? ? ? ? }

? ? }

}

Step 13 創(chuàng)建View和Controller

@inject Tenant Tenant

@model QuestionListViewModel


@{

? ? ViewData["Title"] = "Home Page";

}


<div class="row">

? ? <div class="col-md-12">

? ? ? ? <h1>Welcome to <strong>@Tenant.Name</strong></h1>

? ? ? ? <h3>@Tenant.Description</h3>

? ? </div>

</div>


<div class="row">

? ? <div class="col-md-12">

? ? ? ? <h4>Popular questions</h4>

? ? ? ? <ul>

? ? ? ? ? ? @foreach (var question in Model.Questions)

? ? ? ? ? ? {

? ? ? ? ? ? ? ? <li>@question.Title</li>

? ? ? ? ? ? }

? ? ? ? </ul>

? ? </div>

</div>

using Microsoft.AspNetCore.Mvc;

using Microsoft.EntityFrameworkCore;

using QuestionExchange.Models;

using System.Diagnostics;

using System.Linq;

using System.Threading.Tasks;


namespace QuestionExchange.Controllers

{

? ? public class HomeController : Controller

? ? {

? ? ? ? private readonly AppDbContext _context;

? ? ? ? private readonly Tenant _currentTenant;


? ? ? ? public HomeController(AppDbContext context, Tenant tenant)

? ? ? ? {

? ? ? ? ? ? _context = context;

? ? ? ? ? ? _currentTenant = tenant;

? ? ? ? }


? ? ? ? public async Task<IActionResult> Index()

? ? ? ? {

? ? ? ? ? ? var topQuestions = await _context

? ? ? ? ? ? ? ? .Questions

? ? ? ? ? ? ? ? .Where(q => q.Tenant.Id == _currentTenant.Id)

? ? ? ? ? ? ? ? .OrderByDescending(q => q.UpdatedAt)

? ? ? ? ? ? ? ? .Take(5)

? ? ? ? ? ? ? ? .ToArrayAsync();


? ? ? ? ? ? var viewModel = new QuestionListViewModel

? ? ? ? ? ? {

? ? ? ? ? ? ? ? Questions = topQuestions

? ? ? ? ? ? };


? ? ? ? ? ? return View(viewModel);

? ? ? ? }


? ? ? ? public IActionResult About()

? ? ? ? {

? ? ? ? ? ? ViewData["Message"] = "Your application description page.";


? ? ? ? ? ? return View();

? ? ? ? }


? ? ? ? public IActionResult Contact()

? ? ? ? {

? ? ? ? ? ? ViewData["Message"] = "Your contact page.";


? ? ? ? ? ? return View();

? ? ? ? }


? ? ? ? public IActionResult Error()

? ? ? ? {

? ? ? ? ? ? return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });

? ? ? ? }

? ? }

}

Step 14 運(yùn)行站點(diǎn)

  首先需要修改本地Hosts文件,添加:

127.0.0.1 bufferoverflow.local127.0.0.1 dboverflow.local

  運(yùn)行cmd(命令行),輸入以下命令,刷新DNS:

ipconfig /flushdns

  分別使用不同Url瀏覽站點(diǎn),可以看到之前插入的測(cè)試數(shù)據(jù)在不同租戶下顯示不同:

  以上,簡(jiǎn)單演示了如何基于Citus開(kāi)發(fā)多租戶應(yīng)用。此外,Citus還比較適合開(kāi)發(fā)需要快速返回查詢結(jié)果的應(yīng)用(比如“儀表板”等)。

  本文演示的例子比較簡(jiǎn)單,僅僅是演示了使用Citus開(kāi)發(fā)多租戶應(yīng)用的可能。具體實(shí)踐中,還涉及到具體業(yè)務(wù)以及數(shù)據(jù)庫(kù)切片技巧等。建議閱讀微軟的《Cloud Design Patterns Book》中的Sharding模式部分,以及Citus的官方技術(shù)文檔。

?

參考資料:

  https://github.com/citusdata/citus

  https://www.citusdata.com/blog/2018/01/22/multi-tenant-web-apps-with-dot-net-core-and-postgres

  https://docs.citusdata.com/en/v7.1/aboutcitus/what_is_citus.html

原文地址:https://www.cnblogs.com/MeteorSeed/p/8446154.html ?


.NET社區(qū)新聞,深度好文,歡迎訪問(wèn)公眾號(hào)文章匯總 http://www.csharpkit.com

總結(jié)

以上是生活随笔為你收集整理的基于Citus和ASP.NET Core开发多租户应用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。