这是最近在实际开发中遇到的一个问题,用 asp.net core 开发一个后端 web api ,根据指定的 key 清除 2 台 memcached 服务器上的缓存。背景是我们在进行 .net core 迁移工作,asp.net 项目与 asp.net core 项目并存,为了避免两种类型项目的缓存冲突,我们分别用了 2 台不同的 memcached 服务器。
之前使用 1 台 memcached 服务器时,只需要一个客户端,所以只需创建一个 MemcachedClient 单例并注入到 IMemcachedClient 接口。
public void ConfigureServices(IServiceCollection services) { services.AddOptions(); services.Configure<MemcachedClientOptions>(Configuration.GetSection("memcached")); services.Add(ServiceDescriptor.Transient<IMemcachedClientConfiguration, MemcachedClientConfiguration>()); services.Add(ServiceDescriptor.Singleton<IMemcachedClient, MemcachedClient>()); }
(注:memcached 的配置存储在 appsettings.json 中)
而现在需要用 2 个 memcached 客户端实例分别连接 2 台不同的 memcached 服务器,需要 2 个不同配置的 MemcachedClient 单例,而之前针对 1 个 IMemcachedClient 接口的依赖注入方法不管用了。咋整?
首先想到的是一个变通的方法,1 个接口不行,那就用 2 个接口,于是增加下面的 2 个接口:
public interface IMemcachedClientCore : IMemcachedClient { } public interface IMemcachedClientLegacy : IMemcachedClient { }
因为 MemcachedClient 并没有实现这个这 2 个接口,还要另外增加这 2 个接口的实现:
public class MemcachedClientCore : MemcachedClient, IMemcachedClientCore { public MemcachedClientCore( ILogger<MemcachedClient> logger, IMemcachedClientConfiguration configuration) : base(logger, configuration) { } } public class MemcachedClientLegacy : MemcachedClient, IMemcachedClientLegacy { public MemcachedClientLegacy( ILogger<MemcachedClient> logger, IMemcachedClientConfiguration configuration) : base(logger, configuration) { } }
沿着这条路发现越走越不对劲,还要增加更多的接口与实现。由于 2 个 memcached 客户端的不同在于 IMemcachedClientConfiguration 的不同,而上面的 MemcachedClientCore 与 MemcachedClientLegacy 的构造函数都注入 IMemcachedClientConfiguration 是不行的,还要基于 IMemcachedClientConfiguration 再增加 2 个接口,增加了接口就又不得不再增加实现。。。这样解决问题岂不让人疯掉,遂弃之。
后来转念一想,自己解决问题的思路走偏了,一味地将关注的焦点放在如何通过 Dependency Injection 注入 2 个不同的 MemcachedClient 实例,而忽略了一个很简单的解决方法 —— 用工厂类创建 MemcachedClient 实例,通过 Dependency Injection 注入工厂类,就像 ILoggerFactory 那样。
于是通过基于依赖注入的工厂模式轻松解决了这个问题。
定义一个 IMemcachedClientFactory 接口:
public interface { IMemcachedClientFactory Add(string keyOfConfiguration); IMemcachedClient Create(string keyOfConfiguration); }
添加 MemcachedClientFactory 类实现 IMemcachedClientFactory 接口:
public class MemcachedClientFactory : IMemcachedClientFactory { private readonly ILoggerFactory _loggerFacotry; private readonly IConfiguration _configuration; private readonly Dictionary<string, IMemcachedClient> _clients = new Dictionary<string, IMemcachedClient>(); public MemcachedClientFactory( ILoggerFactory loggerFacotry, IConfiguration configuration) { _loggerFacotry = loggerFacotry; _configuration = configuration; } public IMemcachedClientFactory Add(string keyOfConfiguration) { var options = new MemcachedClientOptions(); _configuration.GetSection(keyOfConfiguration).Bind(options); var memcachedClient = new MemcachedClient( _loggerFacotry, new MemcachedClientConfiguration(_loggerFacotry, options)); _clients.Add(keyOfConfiguration, memcachedClient); return this; } public IMemcachedClient Create(string keyOfConfiguration) { return _clients[keyOfConfiguration]; } }
在 Startup.ConfigureServices() 中注入 MemcachedClientFactory 的单例:
public void ConfigureServices(IServiceCollection services) { services.AddSingleton<IMemcachedClientFactory, MemcachedClientFactory>(); }
在 Startup.Configure() 中调用 IMemcachedClientFactory 接口的 Add() 方法,根据不同配置创建 MemcachedClient 的实例:
public void Configure(IApplicationBuilder app, IMemcachedClientFactory memcachedClientFactory) { memcachedClientFactory.Add("MemcachedLegacy").Add("MemcachedCore"); }
在使用 MemcachedClient 的地方通过 IMemcachedClientFactory 接口的 Create() 方法获取所需 MemcachedClient 的实例:
public class CacheController : Controller { private readonly IMemcachedClient _memcachedClientLegacy; private readonly IMemcachedClient _memcachedClientCore; public CacheController(IMemcachedClientFactory memcachedClientFacotry) { _memcachedClientLegacy = memcachedClientFacotry.Create("MemcachedLegacy"); _memcachedClientCore = memcachedClientFacotry.Create("MemcachedCore"); } [HttpDelete("{key}")] public async Task<IActionResult> Delete(string key) { var removeCoreTask = _memcachedClientCore.RemoveAsync(key); var removeLegacyTask = _memcachedClientLegacy.RemoveAsync(key); await removeCoreTask; await removeLegacyTask; return Ok(); } }