پیاده سازی برنامه های چند مستاجری در ASP.NET Core

سناریویی را در نظر بگیرید که یک برنامه وب نوشته شده قرار است به چندین مستاجر (مشتری یا tenant) خدماتی ارائه کند، در این حالت اطلاعات هر مشتری به صورت کاملا جدا شده از دیگر مشتریان در سیستم قرار دارد و فقط به همان قسمت ها دسترسی دارد.

مثلا یک برنامه مدیریت رستوران را در نظر بگیرید که برای هر مشتری دامین مخصوص به خود قرار دارد و همه آنها به یک سیستم متمرکز متصل شده و اطلاعات خود را از آنجا دریافت می کنند.

در معماری Multi-Tenancy، چندین کاربر می‌توانند از یک نمونه (Single Instance) از اپلیکیشن نرم‌افزاری استفاده کنند. یعنی این نمونه روی سرور اجرا می‌شود و به چندین کاربر سرویس می‌دهد. هر کاربر را یک Tenant می‌نامیم. می‌توان به Tenantها امکان تغییر و شخصی‌سازی بخشی از اپلیکیشن را داد مثلا رنگ رابط کاربری یا قوانین کسب‌وکار، اما آنها نمی‌توانند کدهای اپلیکیشن را شخصی‌سازی کنند.

منبع: TipTech

بدون داشتن دانش کافی، پیاده سازی معماری multi tenant می تواند تبدیل یه یک چالش بزرگ شود. مخصوصا در نسخه قبلی ASP.NET که یکپارچه نبودن فریم ورک های مختلف می توانست باعث ایجاد چندین پیاده سازی مختلف در برنامه شود. موضوع وقتی پیچیده تر می شد که شما قصد داشتید در یک برنامه چندین فریم ورک مختلف مثل SignalR, MVC, Web API را مورد استفاده قرار بدهید.

خوشبختانه اوضاع با وجود OWIN بهتر شده و ما در این مطلب قصد استفاده از یک تولکیت به نام SaasKit رو برای پیاده سازی این معماری در ASP.NET Core داریم، هدف از این toolkit ساده تر کردن هر چه بیشتر ساخت برنامه های SaaS (Software as a Service) هست. با استفاده از OWIN ما قادریم که بدون در نظر گرفتن فریم ورک مورد استفاده، رفتار مورد نظر خودمون را مستقیما در یک چرخه درخواست HTTP پیاده سازی کنیم. و البته به لطف طراحی خاص ASP.NET Core 1.0 و استفاده از میان افزار ها مشابه OWIN در برنامه، کار ما با SaasKit باز هم راحتتر خواهد بود.

شروع کار

یک پروژه ASP.NET Core جدید را ایجاد کنید و سپس ارجاعی را به فضای نام SaasKit.Multitenancy  (موجود در Nuget) بدهید.

PM> Install-Package SaasKit.Multitenancy

بعد از اینکار ما باید به SaasKit اطلاع بدیم که چطور مستاجرهای ما را شناسایی کند.

شناسایی مستاجر (tenant)

اولین جنبه در معماری multi-tenant شناسایی مستاجر بر اساس اطلاعات درخواست جاری می باشد که می تواند از hostname ، کاربر جاری یا یک HTTP header باشد.

ابتدا به تعریف کلاس مستاجر می پردازیم.

public class AppTenant
{
    public string Name { get; set; }
    public string[] Hostnames { get; set; }
}

سپس از طریق پیاده سازی اینترفیس ITenantResolver  و نوشتن یک tenant resolver به SaasKit اطلاع می دهیم که چطور مستاجر جاری رو بر اساس اطلاعات درخواست جاری شناسایی کند و در صورتی که موفق به شناسایی شود یک وهله از جنس  TenantContext<TTenant> بازگشت خواهد داد.

public class AppTenantResolver : ITenantResolver<AppTenant>
{
    IEnumerable<AppTenant> tenants = new List<AppTenant>(new[]
    {
        new AppTenant {
            Name = "Tenant 1",
            Hostnames = new[] { "localhost:6000", "localhost:6001" }
        },
        new AppTenant {
            Name = "Tenant 2",
            Hostnames = new[] { "localhost:6002" }
        }
    });

    public async Task<TenantContext<AppTenant>> ResolveAsync(HttpContext context)
    {
        TenantContext<AppTenant> tenantContext = null;

        var tenant = tenants.FirstOrDefault(t =>
            t.Hostnames.Any(h => h.Equals(context.Request.Host.Value.ToLower())));

        if (tenant != null)
        {
            tenantContext = new TenantContext<AppTenant>(tenant);
        }

        return tenantContext;
    }
}

در نظر داشته باشید که اینجا ما اطلاعات مستاجر رو از روی hostname استخراج کردیم اما از آنجا که شما به شی HttpContext دسترسی کامل دارید می توانید از هر چیزی که مایل باشید استفاده کنید مثل URL، اطلاعات کاربر، هدر های HTTP و غیره. در اینجا فعلا مشخصات مستاجر های خودمون رو توی کد نوشتیم اما شما می توانید در برنامه خودتون این اطلاعات رو از یک فایل تنظیمات یا بانک اطلاعاتی دریافت کنید.

سیم کشی کردن

بعد از پیاده سازی این اینترفیس نوبت به سیم کشی های SaasKit میرسد، من در اینجا سعی کردم که مثل الگوی برنامه های ASP.NET Core عمل کنم. ابتدا نیاز داریم که وابستگی های SaasKit رو ثبت کنیم. فایل startups.cs  رو باز کنید و کدهای زیر را در متد ConfigureServices اضافه نمایید:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMultitenancy<AppTenant, AppTenantResolver>();
}

سپس باید میان افزار SaasKit رو ثبت کنیم، کدهای زیر رو به متد Configure اضافه کنید:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    // after .UseStaticFiles()
    app.UseMultitenancy<AppTenant>();
    // before .UseMvc()
}

دریافت مستاجر جاری

حالا هر جا که نیاز به وهله ای از شی مستاجر جاری داشتید، می توانید به روش زیر عمل کنید:

public class HomeController : Controller
{
    private AppTenant tenant;

    public HomeController(AppTenant tenant)
    {
        this.tenant = tenant;
    }
}

به عنوان مثال قصد داریم نام مستاجر را در عنوان سایت نمایش دهیم، برای اینکار ما از قابلیت جدید MVC 6 یعنی تزریق سرویس ها به View استفاده خواهیم کرد.

در فایل _Layout.cshtml تکه کد زیر را به بالای صفحه اضافه کنید:

@inject AppTenant Tenant;

این کد، AppTenant را برای ما در تمامی View ها از طریق شی Tenant قابل دسترس می کند. حالا می توانیم در View خود از جزییات مستاجر به شکل زیر استفاده کنیم:

<a asp-controller="Home" asp-action="Index" class="navbar-brand">@Tenant.Name</a>

اجرای نمونه مثال

فایل project.json را باز کنید و مقدار web را به شکل زیر مقدار دهی کنید: (در اینجا برای سایت خود دو آدرس را نگاشت کردیم)

"commands": {
  "web": "Microsoft.AspNet.Server.Kestrel --server.urls=http://localhost:6000;http://localhost:6001;http://localhost:6002",
},

سپس کنسول را در محل ریشه پروژه باز نموده و دستور زیر را اجرا کنید:

dnx web

حالا اگر در مرورگر خود آدرس http://localhost:6000 را وارد کنیم، مستاجر 1 را مشاهده می کنیم:

 

و اگر آدرس http://localhost:6001 را وارد کنیم، مستاجر 2 را مشاهده می کنیم:

قابل پیکربندی کردن مستاجر ها

از آنجایی که نوشتن مشخصات مستاجر ها در کد زیاد جالب نیست، برای همین تصمیم داریم که این مشخصات رو با استفاده از قابلیت های ASP.NET Core از فایل appsettings.json دریافت کنیم. تنظیمات مستاجر ها را مطابق زیر به این فایل اضافه کنید:

"Multitenancy": {
  "Tenants": [
    {
      "Name": "Tenant 1",
      "Hostnames": [
        "localhost:6000",
        "localhost:6001"
      ]
    },
    {
      "Name": "Tenant 2",
      "Hostnames": [
        "localhost:6002"
      ]
    }
  ]
}

سپس کلاسی که بیانگر تنظیمات چند مستاجری باشد را می نویسیم:

public class MultitenancyOptions
{
    public Collection<AppTenant> Tenants { get; set; }
}

حالا نیاز داریم که به برنامه اعلام کنیم که تنظیمات مورد نیاز خود را از فایل appsettings.json بخواند، کد زیر را به ConfigureServices اضافه کنید:

services.Configure<MultitenancyOptions>(Configuration.GetSection("Multitenancy"));

سپس کدهای resolver خود را جهت دریافت اطلاعات از MultitenancyOptions مطابق زیر تغییر می دهیم:

public class AppTenantResolver : ITenantResolver<AppTenant>
{
    private readonly IEnumerable<AppTenant> tenants;

    public AppTenantResolver(IOptions<MultitenancyOptions> options)
    {
        this.tenants = options.Value.Tenants;
    }

    public async Task<TenantContext<AppTenant>> ResolveAsync(HttpContext context)
    {
        TenantContext<AppTenant> tenantContext = null;

        var tenant = tenants.FirstOrDefault(t => 
            t.Hostnames.Any(h => h.Equals(context.Request.Host.Value.ToLower())));

        if (tenant != null)
        {
            tenantContext = new TenantContext<AppTenant>(tenant);
        }

        return Task.FromResult(tenantContext);
    }
}

برنامه را یکبار re-build کرده و اجرا کنید .

در آخر

اولین قدم در پیاده سازی یک معماری multi-tenant تصمیم گیری درباره این موضوع است که شما چطور مستاجر خود را شناسایی کنید، به محض این شناسایی شما می توانید عملیات های بعدی خود مثل تفکیک بخشی از برنامه، فیلتر کردن داده ای، نمایش یک view خاص برای هر مستاجر و یا بازنویسی قسمت های مختلف برنامه بر اساس هر مستاجر را انجام بدید.

_ سورس مثال بالا در گیت هاب قابل دریافت می باشد.

_ منبع: اینجا

2 دیدگاه در “پیاده سازی برنامه های چند مستاجری در ASP.NET Core

پاسخ دهید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *