我有asp.net核心应用程序。给定S3 url,应用程序需要下载文件。这些S3网址可能属于美国的不同AWS区域。
public class Downloader
{
public void DownloadFile(string s3Url, string destFolder)
{
//download files
}
}
DownloadFile()
方法被称为并发。传递给此方法的每个网址都可能属于不同的区域。
AWS文档显示how to Configure the AWS SDK for .NET with .NET Core。在我的情况下,AWS凭证存储在服务器上的配置文件中,并且可以在所有美国地区使用相同的凭证。所以appsettings.json看起来像
"AWS": {
"Profile": "default",
"ProfilesLocation": "C:\\aws\\awsprofile"
},
问题
由于URLS可以属于不同的区域,我不能遵循文档代码。我无法向DI框架注册IAmazonS3
并将该实例注入Downloader
以从不同地区下载文件。因为IAmazonS3
实例尝试过特定区域。
解决方案
所以我创建了一个工厂,它提供IAmazonS3
给定区域名称的实例。
public interface IS3ClientFactory : IDisposable
{
IAmazonS3 GetS3Client(RegionEndpoint region);
}
public class S3ClientFactory : IS3ClientFactory
{
private bool _disposed = false;
private IDictionary<string, IAmazonS3> _container = null;
private S3ClientFactory()
{
_container = new Dictionary<string, IAmazonS3>();
}
public static IS3ClientFactory Configure(AWSOptions option, RegionEndpoint[] regions)
{
var factory = new S3ClientFactory();
foreach (RegionEndpoint region in regions)
{
option.Region = region;
factory._container.Add(region.SystemName, option.CreateServiceClient<IAmazonS3>());
}
return factory;
}
public IAmazonS3 GetS3Client(RegionEndpoint region)
{
return _container[region.SystemName];
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
if (_container != null && _container.Any())
{
foreach (var s3Client in _container)
{
if (s3Client.Value != null)
{
s3Client.Value.Dispose();
}
}
}
_disposed = true;
}
}
}
}
并在startup.cs中使用DI
注册工厂services.AddSingleton<IS3ClientFactory>(S3ClientFactory.Configure(Configuration.GetAWSOptions(),
new RegionEndpoint[]
{
RegionEndpoint.USWest1,
RegionEndpoint.USWest2,
RegionEndpoint.USEast1,
RegionEndpoint.USEast2
}));
和Downloader
类看起来像
public class Downloader : IDownloader
{
private readonly IS3ClientFactory _factory;
public Downloader(IS3ClientFactory factory)
{
_factory = factory;
}
public void DownloadFile(string s3Url, string destFolder)
{
var s3Uri = new AmazonS3Uri(s3Url);
var s3Client = _factory.GetS3Client(s3Uri.Region);
// use s3Client to download file
}
}
问题
在S3ClientFactory
的配置方法中,我动态地将RegionEndpoint
分配给AWSOptions
,然后调用option.CreateServiceClient<IAmazonS3>()
这是创建区域特定实例的正确方法IAmazonS3
?代码需要是单元可测试的,因此我无法使用new AmazonS3Client(RegionEndpoint)
。
foreach (RegionEndpoint region in regions)
{
option.Region = region;
factory._container.Add(region.SystemName, option.CreateServiceClient<IAmazonS3>());
}
是否可以使用IAmazonS3
的单例实例?
答案 0 :(得分:0)
您可以注入IEnumerable服务接口。然后使用LINQ找到你想要的实例。
启动
foreach (string snsRegion in Configuration["SNSRegions"].Split(',', StringSplitOptions.RemoveEmptyEntries))
{
services.AddAWSService<IAmazonSimpleNotificationService>(
string.IsNullOrEmpty(snsRegion) ? null :
new AWSOptions()
{
Region = RegionEndpoint.GetBySystemName(snsRegion)
}
);
}
services.AddSingleton<ISNSFactory, SNSFactory>();
services.Configure<SNSConfig>(Configuration);
SNSConfig
public class SNSConfig
{
public string SNSDefaultRegion { get; set; }
public string SNSSMSRegion { get; set; }
}
appsettings.json
"SNSRegions": "ap-south-1,us-west-2",
"SNSDefaultRegion": "ap-south-1",
"SNSSMSRegion": "us-west-2",
SNS工厂
public class SNSFactory : ISNSFactory
{
private readonly SNSConfig _snsConfig;
private readonly IEnumerable<IAmazonSimpleNotificationService> _snsServices;
public SNSFactory(
IOptions<SNSConfig> snsConfig,
IEnumerable<IAmazonSimpleNotificationService> snsServices
)
{
_snsConfig = snsConfig.Value;
_snsServices = snsServices;
}
public IAmazonSimpleNotificationService ForDefault()
{
return GetSNS(_snsConfig.SNSDefaultRegion);
}
public IAmazonSimpleNotificationService ForSMS()
{
return GetSNS(_snsConfig.SNSSMSRegion);
}
private IAmazonSimpleNotificationService GetSNS(string region)
{
return GetSNS(RegionEndpoint.GetBySystemName(region));
}
private IAmazonSimpleNotificationService GetSNS(RegionEndpoint region)
{
IAmazonSimpleNotificationService service = _snsServices.FirstOrDefault(sns => sns.Config.RegionEndpoint == region);
if (service == null)
{
throw new Exception($"No SNS service registered for region: {region}");
}
return service;
}
}
public interface ISNSFactory
{
IAmazonSimpleNotificationService ForDefault();
IAmazonSimpleNotificationService ForSMS();
}
现在,您可以在自定义服务或控制器中获取所需区域的SNS服务
public class SmsSender : ISmsSender
{
private readonly IAmazonSimpleNotificationService _sns;
public SmsSender(ISNSFactory snsFactory)
{
_sns = snsFactory.ForSMS();
}
.......
}
public class DeviceController : Controller
{
private readonly IAmazonSimpleNotificationService _sns;
public DeviceController(ISNSFactory snsFactory)
{
_sns = snsFactory.ForDefault();
}
.........
}