gpt4 book ai didi

c# - 使用 .NET Flurl/HttpClient 设置每个请求的代理(或轮换代理)

转载 作者:行者123 更新时间:2023-11-30 22:58:49 81 4
gpt4 key购买 nike

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

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

const request = require('request');
let opts = { url: 'http://random.org', proxy: 'http://myproxy' };
request(opts, callback);

使用 Flurl 做到这一点的理想方法是这样的,目前这是不可能的:

await "http://random.org".WithProxy("http://myproxy").GetAsync();

我也知道创建一个 FlurlClient/ HttpClient由于 socket exhaustion issue,每个请求都不是一个选项,这也是我过去亲身经历过的。

这种情况是当您需要有一个以某种方式轮换的代理池时,以便每个 HTTP 请求可能使用不同的代理 URL。

最佳答案

因此,在与 Flurl 创建者( #228#374 )讨论之后,我们提出的解决方案是使用自定义 FlurlClient 管理器类,该类将负责创建所需的 FlurlClient s 和链接 HttpClient实例。这是必需的,因为每个 FlurlClient一次只能使用一个代理,以限制 .NET HttpClient被设计。
如果您正在寻找实际的解决方案(和代码),您可以跳到本答案的末尾。 如果您想更好地理解,以下部分仍然有帮助。
[ 更新 :我还构建了一个 HTTP 客户端库,它负责处理下面的所有内容,允许开箱即用地设置每个请求的代理。它叫做 PlainHttp .]
因此,第一个探索的想法是创建一个自定义 FlurlClientFactory实现 IFlurlClientFactory界面。
工厂备有FlurlClient池s,当需要发送新的请求时,工厂被调用 Url作为输入参数。然后执行一些逻辑来决定请求是否应该通过代理。 URL 可能被用作选择代理以用于特定请求的鉴别器。在我的情况下,将为每个请求选择一个随机代理,然后缓存 FlurlClient会被退回。
最后,工厂将创建:

  • 最多 FlurlClient每个代理 URL (然后将用于所有必须通过该代理的请求);
  • 一组用于“正常”请求的客户端。

  • 可以找到此解决方案的一些代码 here .注册定制工厂后,就没有什么可做的了。标准请求,如 await "http://random.org".GetAsync();将被自动代理, 如果 工厂决定这样做。
    不幸的是,这个解决方案有一个缺点。原来 自定义工厂被多次调用 在使用 Flurl 构建请求的过程中。根据我的经验,它叫 at least 3 times .这可能会导致问题,因为 工厂可能不会返回相同的 FlurlClient对于相同的输入 URL .
    解决方案
    解决方案是构建自定义 FlurlClientManager 类,以完全绕过 FlurlClient 工厂机制并保留按需提供的自定义客户端池。
    虽然这个解决方案是专门为使用很棒的 Flurl 库而构建的,但使用 HttpClient 也可以完成非常相似的事情。直接上课。
    /// <summary>
    /// Static class that manages cached IFlurlClient instances
    /// </summary>
    public static class FlurlClientManager
    {
    /// <summary>
    /// Cache for the clients
    /// </summary>
    private static readonly ConcurrentDictionary<string, IFlurlClient> Clients =
    new ConcurrentDictionary<string, IFlurlClient>();

    /// <summary>
    /// Gets a cached client for the host associated to the input URL
    /// </summary>
    /// <param name="url"><see cref="Url"/> or <see cref="string"/></param>
    /// <returns>A cached <see cref="FlurlClient"/> instance for the host</returns>
    public static IFlurlClient GetClient(Url url)
    {
    if (url == null)
    {
    throw new ArgumentNullException(nameof(url));
    }

    return PerHostClientFromCache(url);
    }

    /// <summary>
    /// Gets a cached client with a proxy attached to it
    /// </summary>
    /// <returns>A cached <see cref="FlurlClient"/> instance with a proxy</returns>
    public static IFlurlClient GetProxiedClient()
    {
    string proxyUrl = ChooseProxy();

    return ProxiedClientFromCache(proxyUrl);
    }

    private static string ChooseProxy()
    {
    // Do something and return a proxy URL
    return "http://myproxy";
    }

    private static IFlurlClient PerHostClientFromCache(Url url)
    {
    return Clients.AddOrUpdate(
    key: url.ToUri().Host,
    addValueFactory: u => {
    return CreateClient();
    },
    updateValueFactory: (u, client) => {
    return client.IsDisposed ? CreateClient() : client;
    }
    );
    }

    private static IFlurlClient ProxiedClientFromCache(string proxyUrl)
    {
    return Clients.AddOrUpdate(
    key: proxyUrl,
    addValueFactory: u => {
    return CreateProxiedClient(proxyUrl);
    },
    updateValueFactory: (u, client) => {
    return client.IsDisposed ? CreateProxiedClient(proxyUrl) : client;
    }
    );
    }

    private static IFlurlClient CreateProxiedClient(string proxyUrl)
    {
    HttpMessageHandler handler = new SocketsHttpHandler()
    {
    Proxy = new WebProxy(proxyUrl),
    UseProxy = true,
    PooledConnectionLifetime = TimeSpan.FromMinutes(10)
    };

    HttpClient client = new HttpClient(handler);

    return new FlurlClient(client);
    }

    private static IFlurlClient CreateClient()
    {
    HttpMessageHandler handler = new SocketsHttpHandler()
    {
    PooledConnectionLifetime = TimeSpan.FromMinutes(10)
    };

    HttpClient client = new HttpClient(handler);

    return new FlurlClient(client);
    }
    }
    这个静态类保持一个全局池 FlurlClient s。与之前的解决方案一样,池包括:
  • 每个代理一个客户端 ;
  • 每个主机一个客户端 对于所有不能通过代理的请求(这实际上是 Flurl 的默认工厂策略)。

  • 在类的这个实现中,代理是由类本身选择的(使用任何你想要的策略,例如循环或随机),但它可以调整为将代理 URL 作为输入。在这种情况下,请记住,使用此实现,客户端在创建后永远不会被释放,因此您可能需要考虑一下。
    此实现还使用了新的 SocketsHttpHandler.PooledConnectionLifetime选项,自 .NET Core 2.1 起可用,用于解决 HttpClient 时出现的 DNS 问题。实例的生命周期很长。在 .NET Framework 上, ServicePoint.ConnectionLeaseTimeout应改用属性。
    使用 manager 类很容易。对于正常请求,请使用:
    await FlurlClientManager.GetClient(url).Request(url).GetAsync();
    对于代理请求,请使用:
    await FlurlClientManager.GetProxiedClient().Request(url).GetAsync();

    关于c# - 使用 .NET Flurl/HttpClient 设置每个请求的代理(或轮换代理),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52708836/

    81 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com