C# 使用.NET Flurl/HttpClient设置每请求代理(或旋转代理)

C# 使用.NET Flurl/HttpClient设置每请求代理(或旋转代理),c#,flurl,C#,Flurl,我知道使用HTTP.NET库,我可以通过使用自定义代理设置全局代理,但是有没有办法为每个请求选择自定义代理 对于许多其他编程语言,设置代理与设置选项一样简单。例如,使用Node.js,我可以做到: const request=require('request'); 让opts={url:'http://random.org'代理:'http://myproxy' }; 请求(opts、回调); 使用Flurl实现这一点的理想方法是这样的,这在目前是不可能的: 等待”http://random.

我知道使用HTTP.NET库,我可以通过使用自定义代理设置全局代理,但是有没有办法为每个请求选择自定义代理

对于许多其他编程语言,设置代理与设置选项一样简单。例如,使用Node.js,我可以做到:

const request=require('request');
让opts={url:'http://random.org'代理:'http://myproxy' };
请求(opts、回调);
使用Flurl实现这一点的理想方法是这样的,这在目前是不可能的:

等待”http://random.org“.WithProxy(”http://myproxyGetAsync();
我还知道,为每个请求创建
FlurlClient
/
HttpClient
不是一个选项,因为我过去也经历过这种情况


这种情况下,您需要一个代理池,这些代理池以某种方式进行旋转,以便每个HTTP请求可能使用不同的代理URL。

因此,在与Flurl创建者(和)讨论之后,我们提出的解决方案是使用自定义FlurlClient manager类,负责创建所需的
FlurlClient
s和链接的
HttpClient
实例。这是必需的,因为每个
FlurlClient
一次只能使用一个代理,因为.NET
HttpClient
的设计方式有限制

如果您正在寻找实际的解决方案(和代码),可以跳到答案的末尾。如果您想更好地理解,下面的部分仍然很有帮助

[更新:我还构建了一个HTTP客户端库,它可以处理下面的所有内容,允许在开箱即用的情况下设置每个请求的代理。它被称为。]

因此,首先探索的想法是创建一个定制的
FlurlClientFactory
,它实现
iflurllclientfactory
接口

工厂保留一个
FlurlClient
s池,当需要发送新请求时,工厂将以
Url
作为输入参数调用。然后执行一些逻辑来决定请求是否应该通过代理。URL可能被用作选择用于特定请求的代理的鉴别器。在我的例子中,将为每个请求选择一个随机代理,然后返回一个缓存的
FlurlClient

最终,工厂将创造:

  • 每个代理URL最多一个
    FlurlClient
    (然后将用于必须通过该代理的所有请求)
  • 用于“正常”请求的一组客户端
可以找到此解决方案的一些代码。在注册了定制工厂之后,就没有什么别的事可做了。标准请求,如
等待“http://random.org“.GetAsync()将自动代理

不幸的是,这种解决方案有一个缺点。事实证明,在使用Flurl构建请求的过程中,会多次调用自定义工厂。根据我的经验,它被称为。这可能会导致问题,因为工厂可能不会为相同的输入URL返回相同的
FlurlClient

解决方案 解决方案是构建一个定制的
FlurlClientManager
类,以完全绕过FlurlClient工厂机制,并保留按需提供的自定义客户端池

虽然此解决方案是专门为使用awesome Flurl库而构建的,但是可以直接使用
HttpClient
类完成类似的工作

//
///管理缓存的客户端实例的静态类
/// 
公共静态类FlurlClientManager
{
/// 
///客户端缓存
/// 
私有静态只读ConcurrentDictionary客户端=
新的ConcurrentDictionary();
/// 
///获取与输入URL关联的主机的缓存客户端
/// 
///或
///主机的缓存实例
公共静态IFlurlClient GetClient(Url)
{
如果(url==null)
{
抛出新ArgumentNullException(nameof(url));
}
返回PerHostClientFromCache(url);
}
/// 
///获取连接了代理的缓存客户端
/// 
///带有代理的缓存实例
公共静态IFlurlClient GetProxiedClient()
{
字符串proxyUrl=ChooseProxy();
返回ProxiedClientFromCache(proxyUrl);
}
私有静态字符串ChooseProxy()
{
//执行某些操作并返回代理URL
返回“http://myproxy";
}
私有静态IFlurlClient PerHostClientFromCache(Url)
{
return Clients.AddOrUpdate(
关键字:url.ToUri().Host,
addValueFactory:u=>{
返回CreateClient();
},
updateValueFactory:(u,客户端)=>{
return client.IsDisposed?CreateClient():client;
}
);
}
专用静态IFlurlClient ProxiedClientFromCache(字符串代理URL)
{
return Clients.AddOrUpdate(
关键字:proxyUrl,
addValueFactory:u=>{
返回CreateProxiedClient(proxyUrl);
},
updateValueFactory:(u,客户端)=>{
return client.IsDisposed?CreateProxiedClient(proxyUrl):客户端;
}
);
}
专用静态IFlurlClient CreateProxiedClient(字符串proxyUrl)
{
HttpMessageHandler=new SocketsHttpHandler()
{
Proxy=新的WebProxy(proxyUrl),
UseProxy=true,
PooledConnectionLifetime=TimeSpan.FromMinutes(10)
};
HttpClient=新的HttpClient(处理程序);
返回新FlurlClient(客户端