通过一些变量限制 HttpClient 请求
Throttling HttpClient requests by some variable
我正在编写一个 class,它使用 HttpClient 来访问一个 API,我想限制可以对该 [=38= 中的某个函数进行的并发调用的数量].诀窍在于,限制是针对每个租户的,并且多个租户可能一次使用他们自己的 class 实例。
My Tenant class 只是只读上下文信息的容器。
public class Tenant
{
public string Name { get; }
public string ApiKey { get; }
}
这是 ApiClient:
public class ApiClient
{
private readonly Tenant tenant;
public ApiClient(Tenant tenant)
{
this.tenant = tenant;
}
public async Task<string> DoSomething()
{
var response = await this.SendCoreAsync();
return response.ToString();
}
private Task<XElement> SendCore()
{
using (var httpClient = new HttpClient())
{
var httpRequest = this.BuildHttpRequest();
var httpResponse = await httpClient.SendAsync(httpRequest);
return XElement.Parse(await httpResponse.Content.ReadAsStringAsync());
}
}
}
我想做的是限制 SendCore
方法并将其限制为每个租户 两个并发请求 。我已经阅读了使用 TPL
或 SemaphoreSlim
进行基本节流的建议(例如此处:Throttling asynchronous tasks),但我不清楚如何添加更复杂的房客。
感谢您的建议。
更新
我尝试使用 ConcurrentDictionary
中包含的一组 SemaphoreSlim
个对象(每个租户一个)。这似乎可行,但我不确定这是否理想。新代码是:
public class ApiClient
{
private static readonly ConcurrentDictionary<string, SemaphoreSlim> Semaphores = new ConcurrentDictionary<string, SemaphoreSlim>();
private readonly Tenant tenant;
private readonly SemaphoreSlim semaphore;
public ApiClient(Tenant tenant)
{
this.tenant = tenant;
this.semaphore = Semaphores.GetOrAdd(this.tenant.Name, k => new SemaphoreSlim(2));
}
public async Task<string> DoSomething()
{
var response = await this.SendCoreAsync);
return response.ToString();
}
private Task<XElement> SendCore()
{
await this.semaphore.WaitAsync();
try
{
using (var httpClient = new HttpClient())
{
var httpRequest = this.BuildHttpRequest();
var httpResponse = await httpClient.SendAsync(httpRequest);
return XElement.Parse(await httpResponse.Content.ReadAsStringAsync());
}
}
finally
{
this.semaphore.Release();
}
}
}
你的 SemaphoreSlim
方法对我来说似乎很合理。
一个潜在的问题是,如果 Tenant
s 可以在应用程序的生命周期内来来去去,那么即使 Tenant
s 不再存在,您也会保留信号量。
一个解决方案是使用 ConditionalWeakTable<Tenant, SemaphoreSlim>
而不是你的 ConcurrentDictionary
,这确保它的键可以被垃圾收集,当它们被垃圾收集时,它会释放值。
我正在编写一个 class,它使用 HttpClient 来访问一个 API,我想限制可以对该 [=38= 中的某个函数进行的并发调用的数量].诀窍在于,限制是针对每个租户的,并且多个租户可能一次使用他们自己的 class 实例。
My Tenant class 只是只读上下文信息的容器。
public class Tenant
{
public string Name { get; }
public string ApiKey { get; }
}
这是 ApiClient:
public class ApiClient
{
private readonly Tenant tenant;
public ApiClient(Tenant tenant)
{
this.tenant = tenant;
}
public async Task<string> DoSomething()
{
var response = await this.SendCoreAsync();
return response.ToString();
}
private Task<XElement> SendCore()
{
using (var httpClient = new HttpClient())
{
var httpRequest = this.BuildHttpRequest();
var httpResponse = await httpClient.SendAsync(httpRequest);
return XElement.Parse(await httpResponse.Content.ReadAsStringAsync());
}
}
}
我想做的是限制 SendCore
方法并将其限制为每个租户 两个并发请求 。我已经阅读了使用 TPL
或 SemaphoreSlim
进行基本节流的建议(例如此处:Throttling asynchronous tasks),但我不清楚如何添加更复杂的房客。
感谢您的建议。
更新
我尝试使用 ConcurrentDictionary
中包含的一组 SemaphoreSlim
个对象(每个租户一个)。这似乎可行,但我不确定这是否理想。新代码是:
public class ApiClient
{
private static readonly ConcurrentDictionary<string, SemaphoreSlim> Semaphores = new ConcurrentDictionary<string, SemaphoreSlim>();
private readonly Tenant tenant;
private readonly SemaphoreSlim semaphore;
public ApiClient(Tenant tenant)
{
this.tenant = tenant;
this.semaphore = Semaphores.GetOrAdd(this.tenant.Name, k => new SemaphoreSlim(2));
}
public async Task<string> DoSomething()
{
var response = await this.SendCoreAsync);
return response.ToString();
}
private Task<XElement> SendCore()
{
await this.semaphore.WaitAsync();
try
{
using (var httpClient = new HttpClient())
{
var httpRequest = this.BuildHttpRequest();
var httpResponse = await httpClient.SendAsync(httpRequest);
return XElement.Parse(await httpResponse.Content.ReadAsStringAsync());
}
}
finally
{
this.semaphore.Release();
}
}
}
你的 SemaphoreSlim
方法对我来说似乎很合理。
一个潜在的问题是,如果 Tenant
s 可以在应用程序的生命周期内来来去去,那么即使 Tenant
s 不再存在,您也会保留信号量。
一个解决方案是使用 ConditionalWeakTable<Tenant, SemaphoreSlim>
而不是你的 ConcurrentDictionary
,这确保它的键可以被垃圾收集,当它们被垃圾收集时,它会释放值。