4

我正在尝试找到一种方法来使用using HttpClientwhere IHttpClientFactory.CreateClient([name])[name] 是在 Startup 类中使用. 我希望该策略适用于我使用该客户端进行调用的每个 url。HttpClientPollyPolicyRegistry

因此,如果我创建一个名为“myclient”的客户端,并且在整个应用程序中,我将使用该命名客户端调用 N 个不同的 url,我希望将策略单独应用于每个 url,而不是作为一个整体。

启动

PolicyRegistry registry = new PolicyRegistry();
AsyncPolicyWrap type1Policy = //somePolicy;
registry.Add("type1",type1Policy);
services.AddHttpClient("someClient").AddPolicyHandlerFromRegistry("type1");

一些消费者服务

public class SomeService
{
    private IHttpClientFactory factory;
    public SomeService(IHttpClientFactory factory)
    {
        this.factory=factory;
    }
    public void SomeCall(string url)
    {
        var client=factory.GetClient("someClient");
        client.SendAsync(...);
    }
}

在上述情况下,我希望为我正在调用的每个 url 单独采用启动类中设置的策略。

那可能吗 ?

4

1 回答 1

2

为了实现这一点,您需要分别注册 HttpClient 和 PolicyRegistry:

services.AddHttpClient("someClient");
services.AddPolicyRegistry(registry);

然后您可以要求 DI 为您提供该注册表:

public class SomeService
{
    private readonly IHttpClientFactory factory;
    private readonly IReadOnlyPolicyRegistry<string> policyRegistry;

    public SomeService(IHttpClientFactory factory, IReadOnlyPolicyRegistry<string> policyRegistry)
    {
        this.factory = factory;
        this.policyRegistry = policyRegistry;
    }
    ...
}

最后在您SomeCall的内部,您可以检索策略:

public async Task SomeCall(string url)
{
     var client = factory.GetClient("someClient");
     var policy = policyRegistry.Get<IAsyncPolicy>("type1");

     await policy.ExecuteAsync(async ct => await client.SendAsync(..., ct), CancellationToken.None);
}

更新 #1:为每个 url 创建新策略

在函数式编程中有一种众所周知的技术,称为记忆化。它基本上根据接收到的参数存储/缓存方法的输出。换句话说,如果该方法接收到相同的输入,那么它会从缓存中发出结果,而不是重新执行它。

这种技术适用于纯函数。如果它们以无副作用的方式接收完全相同的输入,它们将返回相同的值。

所以,如果我们能有一个解决方案

  • 如果 url 是新的,则创建一个新策略
  • 如果 url 是“已经看到”的,则返回缓存策略,那么我们就可以开始了。

这是一个简单的实现,但是您可以找到很多变体,例如123等。

public interface IMemoizer<K, V> where K : IComparable
{
    Func<K, V> Memoize(Func<K, V> toBeMemoized);
}
public sealed class Memoizer<K, V> : IMemoizer<K, V> where K: IComparable
{
    public static readonly ConcurrentDictionary<K, Lazy<V>> memoizedValues;
    static Memoizer()
    {
        memoizedValues = new ConcurrentDictionary<K, Lazy<V>>();
    }

    public Func<K, V> Memoize(Func<K, V> toBeMemoized)
        => (K input) => memoizedValues.GetOrAdd(input, (K param) => new Lazy<V>(() => toBeMemoized(param))).Value;
}

这是一个使用示例:

static void Main()
{
    var memoizer = new Memoizer<string, IAsyncPolicy<HttpResponseMessage>>();
    var policyMemoizer = memoizer.Memoize(GetSamplePolicy);

    var gPolicy1 = policyMemoizer("https://google.com");
    var soPolicy = policyMemoizer("https://stackoverflow.com");
    var gPolicy2 = policyMemoizer("https://google.com");

    Console.WriteLine(gPolicy1 == gPolicy2); //true
    Console.WriteLine(gPolicy1 == soPolicy); //false
    
    var policyMemoizer2 = memoizer.Memoize(GetSamplePolicy);
    var gPolicy3 = policyMemoizer2("https://google.com");

    Console.WriteLine(gPolicy1 == gPolicy3); //true
}

static IAsyncPolicy<HttpResponseMessage> GetSamplePolicy(string _)
        => Policy<HttpResponseMessage>
        .Handle<HttpRequestException>()
        .WaitAndRetryAsync(3, _ => TimeSpan.FromSeconds(2));

您可以将 memoizer 注册为单例,然后就可以使用它了。:D

我希望它可以帮助您实现所需的行为。

于 2021-12-14T17:03:17.647 回答