多租户框架案例

概念

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

需求

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

开始

解析租户信息

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

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

DefaultHeaderTenantResolver:

    public class DefaultHeaderTenantResolver : ITenantResolver
    {
        private readonly IHttpContextAccessor _accessor;
        public DefaultHeaderTenantResolver(IHttpContextAccessor accessor)
        {
            _accessor = accessor;
        }
        public async Task<string> 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
    {
        /// <summary>
        /// 租户标识
        /// </summary>
        public string Identifier { get; set; }
        /// <summary>
        /// 数据库类型
        /// </summary>
        public string DBType { get; set; }
        /// <summary>
        /// 数据库连接字符串
        /// </summary>
        public string ConnectionString { get; set; }
    }

存储租户信息

    /// <summary>
    /// 存储租户信息
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public interface ITenantStore<T>
    {
        /// <summary>
        /// 获取多租户信息
        /// </summary>
        /// <param name="identifier"></param>
        /// <returns></returns>
        Task<T> GetTenantAsync(string identifier);

        /// <summary>
        /// 获取全部多租户信息
        /// </summary>
        /// <returns></returns>
        Task<IEnumerable<T>> GetTenantsAsync();
    }

LoadBalanceTenantStore:


    public class LoadBalanceTenantStore : ITenantStore<LoadBalanceTenant>
    {
        private List<LoadBalanceTenant> _cache = null;
        private IConfiguration _configuration = null;
        public LoadBalanceTenantStore(IConfiguration configuration)
        {
            _configuration = configuration;
        }
        public async Task<LoadBalanceTenant> GetTenantAsync(string identifier)
        {
            List<LoadBalanceTenant> tenantInfos = (await GetTenantsAsync()).Cast<LoadBalanceTenant>().ToList();
            var tenant = tenantInfos.Find(tenant => tenant.Identifier == identifier) ?? throw new NullReferenceException($"租户不存在,identifier:{identifier}");
            return tenant;
        }
        public async Task<IEnumerable<LoadBalanceTenant>> GetTenantsAsync()
        {
            if (_cache == null)
            {
                List<LoadBalanceTenant> tenantInfoList = new List<LoadBalanceTenant>();
                ChangeToken.OnChange(() => _configuration.GetReloadToken(), () => { _cache = null; });
                var tenantSections = _configuration.GetSection("Tenants").GetChildren();
                bool enable = false;
                if (tenantSections.Any())
                {
                    foreach (var tenantSection in tenantSections)
                    {
                        enable = tenantSection.GetValue<bool>("Enable");
                        if (enable)
                        {
                            bool updateCurrency = tenantSection.GetValue<bool>("UpdateCurrency");
                            string dbType = tenantSection.GetValue<string>("DBType");
                            string masterConnectionString = tenantSection.GetValue<string>("MasterConnectionString");
                            //List<SlaveConnection> slaveConnections = tenantSection.GetValue<SlaveConnection[]>("Slaves")?.ToList() ?? new List<SlaveConnection>(0);
                            var slaveSections = tenantSection.GetSection("Slaves").GetChildren();
                            List<SlaveConnection> slaveConnections = new List<SlaveConnection>(slaveSections.Count());
                            foreach (var slaveSection in slaveSections)
                            {
                                slaveConnections.Add(new SlaveConnection
                                {
                                    ConnectionString = slaveSection["ConnectionString"],
                                    Weight = slaveSection.GetValue<int>("Weight")
                                });
                            }
                            tenantInfoList.Add(new LoadBalanceTenant
                            {
                                UpdateCurrency = updateCurrency,
                                Identifier = tenantSection.Key,
                                MasterConnectionString = masterConnectionString,
                                SlaveConnections = slaveConnections,
                                DBType = dbType
                            });
                            _cache = tenantInfoList;
                        }
                    }
                }
            }
            return await Task.FromResult(_cache);
        }
    }

ITenantService

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

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

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

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

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

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

参考:https://www.cnblogs.com/ms27946/p/How-To-Implement-Multi-Tenant-System.html

posted @ 2021-03-15 00:25  .Neterr  阅读(141)  评论(0编辑  收藏  举报