多租户框架案例


概念

多租户软件架构就是在同一个系统实例上运行不同用户,能做到应用程序共享,服务自治,并且还能做到数据互相隔离的软件架构思想。

需求

公司有多个集群需要访问配置中心项目,不同集群访问到的数据可能不同,所以每个租户一个独立的数据库,但是共用一个配置中心项目。

开始

解析租户信息

可以通过域名、URL、Header等方式解析租户信息

/// 
    /// 解析租户信息
    /// 
    public interface ITenantResolver
    {
        Task GetIdentifierAsync();
    }

DefaultHeaderTenantResolver:

    public class DefaultHeaderTenantResolver : ITenantResolver
    {
        private readonly IHttpContextAccessor _accessor;
        public DefaultHeaderTenantResolver(IHttpContextAccessor accessor)
        {
            _accessor = accessor;
        }
        public async Task GetIdentifierAsync()
        {
            string identifier = _accessor.HttpContext.Request.Headers["Identifier"];
            if (string.IsNullOrWhiteSpace(identifier))
            {
                throw new System.Exception("Identifier不能为空");
            }
            return await Task.FromResult(identifier);
        }
    }

租户信息体基类

public class TenantInfoBase
    {
        /// 
        /// 租户标识
        /// 
        public string Identifier { get; set; }
        /// 
        /// 数据库类型
        /// 
        public string DBType { get; set; }
        /// 
        /// 数据库连接字符串
        /// 
        public string ConnectionString { get; set; }
    }

存储租户信息

    /// 
    /// 存储租户信息
    /// 
    /// 
    public interface ITenantStore
    {
        /// 
        /// 获取多租户信息
        /// 
        /// 
        /// 
        Task GetTenantAsync(string identifier);

        /// 
        /// 获取全部多租户信息
        /// 
        /// 
        Task> GetTenantsAsync();
    }

ITenantService

/// 
    /// 多租户对外服务(外观模式)
    /// 
    /// 
    public interface ITenantService where TTenant : TenantInfoBase
    {
        /// 
        /// 异步获取当前请求租户信息
        /// 
        /// 
        Task GetTenantAsync();
        /// 
        /// 获取全部多租户信息
        /// 
        /// 
        Task> GetTenantsAsync();
    }

    public class TenantService : ITenantService
    {
        private readonly ITenantResolver _tenantResolver;
        private readonly ITenantStore _tenantStore;

        public TenantService(ITenantResolver tenantResolver, ITenantStore tenantStore)
        {
            _tenantResolver = tenantResolver;
            _tenantStore = tenantStore;
        }

        public async Task GetTenantAsync()
        {
            var identifier = _tenantResolver.GetIdentifierAsync().Result;
            return await _tenantStore.GetTenantAsync(identifier);
        }
        public async Task> GetTenantsAsync()
        {
            return (await _tenantStore.GetTenantsAsync()).ToList();
        }
    }
public class TenantBuilder where TTenant : TenantInfoBase
    {
        private readonly IServiceCollection _services;
        public TenantBuilder(IServiceCollection services)
        {
            _services = services;
        }

        public TenantBuilder WithResolver(ServiceLifetime lifetime = ServiceLifetime.Transient) where TResolver : ITenantResolver
        {
            _services.TryAddSingleton();
            _services.Add(new ServiceDescriptor(typeof(ITenantResolver), typeof(TResolver), lifetime));
            return this;
        }

        public TenantBuilder WithStore(ServiceLifetime lifetime = ServiceLifetime.Transient)
        {
            _services.Add(new ServiceDescriptor(typeof(ITenantStore), typeof(TStore), lifetime));
            return this;
        }
    }
public static class ServiceCollectionExtensions
    {
        public static TenantBuilder AddMultiTenant(this IServiceCollection services) where TTenant : TenantInfoBase
        {
            services.AddTransient(typeof(ITenantService), typeof(TenantService));
            return new TenantBuilder(services);
        }
    }

参考: