具有 HttpClientFactory 实现的动态代理
Posted
技术标签:
【中文标题】具有 HttpClientFactory 实现的动态代理【英文标题】:Having dynamic proxy with HttpClientFactory implementation 【发布时间】:2019-04-08 12:59:46 【问题描述】:我有Asp.Net Core WebApi
。我正在根据HttpClientFactory pattern 发出 Http 请求。这是我的示例代码:
public void ConfigureServices(IServiceCollection services)
...
services.AddHttpClient<IMyInterface, MyService>();
...
public class MyService: IMyInterface
private readonly HttpClient _client;
public MyService(HttpClient client)
_client = client;
public async Task CallHttpEndpoint()
var request = new HttpRequestMessage(HttpMethod.Get, "www.customUrl.com");
var response = await _client.SendAsync(request);
...
我想通过动态代理实现发送请求。这基本上意味着我可能需要为每个请求更改代理。至于现在,我发现了 2 个 approuces,其中没有一个对我来说似乎不错:
1.有一个像这样的静态代理:
public void ConfigureServices(IServiceCollection services)
...
services.AddHttpClient<IMyInterface, MyService>().ConfigurePrimaryHttpMessageHandler(() =>
return new HttpClientHandler
Proxy = new WebProxy("http://127.0.0.1:8888"),
UseProxy = true
;
);
...
但在这种方法中,我只能为每个服务设置一个代理。
2.Dispose HttpClient
处理每个请求:
HttpClientHandler handler = new HttpClientHandler()
Proxy = new WebProxy("http://127.0.0.1:8888"),
UseProxy = true,
;
using(var client = new HttpClient(handler))
var request = new HttpRequestMessage(HttpMethod.Get, "www.customUrl.com");
var response = await client.SendAsync(request);
...
但是这样我就违反了 HttpClientFactory 模式,它可能会导致应用程序性能出现问题,如下面的article 所述
是否有第三种方法可以在不重新创建 HttpClient
的情况下动态更改代理?
【问题讨论】:
据此贴:docs.microsoft.com/en-us/dotnet/standard/… 每次从 IHttpClientFactory 获取 HttpClient 对象时,都会返回一个新实例。但是每个 HttpClient 使用一个由 IHttpClientFactory 汇集和重用的 HttpMessageHandler 来减少资源消耗,只要 HttpMessageHandler 的生命周期没有过期。所以它是有范围的,但有额外的好处。 【参考方案1】:在实例化后,无法更改 HttpClientHandler
的任何属性或将新版本的 HttpClientHandler
分配给现有的 HttpClient
。因此,不可能为特定的HttpClient
设置动态代理:您只能指定一个代理。
实现这一点的正确方法是使用命名客户端,并为每个代理端点定义一个客户端。然后,您需要注入 IHttpClientFactory
并选择要使用的代理之一,请求实现它的命名客户端。
services.AddHttpClient("MyServiceProxy1").ConfigurePrimaryHttpMessageHandler(() =>
return new HttpClientHandler
Proxy = new WebProxy("http://127.0.0.1:8888"),
UseProxy = true
;
);
services.AddHttpClient("MyServiceProxy2").ConfigurePrimaryHttpMessageHandler(() =>
return new HttpClientHandler
Proxy = new WebProxy("http://127.0.0.1:8889"),
UseProxy = true
;
);
...
然后:
public class MyService : IMyInterface
private readonly HttpClient _client;
public MyService(IHttpClientFactory httpClientFactory)
_client = httpClientFactory.CreateClient("MyServiceProxy1");
public async Task CallHttpEndpoint()
var request = new HttpRequestMessage(HttpMethod.Get, "www.customUrl.com");
var response = await _client.SendAsync(request);
...
【讨论】:
如果事先不知道代理网址怎么办? 那你真的不走运吗?但是,在什么情况下您会事先不知道这样的事情?您可能直到运行时才知道要使用 哪个 代理,但您至少知道可能性。您为每种可能性配置命名客户端,然后您可以在运行时轻松切换它们。【参考方案2】:我可以通过继承 HttpClientHandler 来做到这一点:
public class ProxyHttpHandler : HttpClientHandler
private int currentProxyIndex = 0;
private ProxyOptions proxyOptions;
public ProxyHttpHandler(IOptions<ProxyOptions> options)
proxyOptions = options != null ? options.Value : throw new ArgumentNullException(nameof(options));
UseProxy = true;
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
var proxy = proxyOptions.Proxies[currentProxyIndex];
var proxyResolver = new WebProxy(proxy.Host, proxy.Port)
Credentials = proxy.Credentials
;
Proxy = proxyResolver;
currentProxyIndex++;
if(currentProxyIndex >= proxyOptions.Proxies.Count)
currentProxyIndex = 0;
return base.SendAsync(request, cancellationToken);
然后我在 IoC 中注册我的 ProxyHttpHandler
和 ProxyOptions
:
public IForksCoreConfigurationBuilder ConfigureProxy(Action<ProxyOptions> options)
Services.AddOptions<ProxyOptions>().Configure(options);
Services.AddTransient<ProxyHttpHandler>();
Services.AddHttpClient<IService, MyService>()
.ConfigurePrimaryHttpMessageHandler<ProxyHttpHandler>();
return this;
【讨论】:
在第二个请求中你不能再次设置代理,有一个异常:这个实例已经启动了一个或多个请求以上是关于具有 HttpClientFactory 实现的动态代理的主要内容,如果未能解决你的问题,请参考以下文章
.NET Core HttpClientFactory+Consul实现服务发现
asp.net core 使用HttpClientFactory Polly实现熔断降级
HttpClientFactory与Steeltoe结合来完成服务发现
ASP.NET Core 2.1 中的 HttpClientFactory (Part 3) 使用Handler实现传出请求中间件