-1

我正在编写一个 Webapi,当第一个 HTTP 端点失败时,我必须调用 HTTP 的第二个端点。但是,对于第二个 HTTP 调用,我的应用程序不应该阻塞主线程并且应该返回不成功的结果。我的 HTTP 客户端已经配备了 Polly 重试策略,重试后,我想在不阻塞主线程的情况下静默调用第二个 URL。我的代码如下所示。

public class Request 
{
    public string Id { get; set; }
}

public class Result
{
    public string Id { get; set; }
    public bool IsSuccess { get; set; }       
    public string Message { get; set; }
}

public class Service
{
   private readonly IHttpClientFactory _httpClientFactory;
   private readonly Ilogger _appLogger;
   private readonly string _url1="www.somedomain.com/api1";
   private readonly string _url2="www.somedomain.com/api2";

   Service(IHttpClientFactory factory,Ilogger logger)
   {
     _httpClientFactory = httpClientFactory;
     _appLogger = logger;
   }

   public async Task<Result> Send(Request request)
   { 
        try
        {
         using var client = _httpClientFactory.CreateClient();
         using HttpRequestMessage httpRequest =new HttpRequestMessage(HttpMethod.Post, _url1);
         var req = JsonConvert.SerializeObject<Request>(body);
         var stringContent = new StringContent(req, Encoding.UTF8, "json");
         var result = await client.SendAsync(httpRequest);
         var resultContent = await result.Content.ReadAsStringAsync();
            return JsonConvert.DeserializeObject<Result>(resultContent));
         }
         catch (Exception ex)
         {
            if (ex is OperationCanceledException || ex is TaskCanceledException)
            {
              //Second call needed but don't want to hold the result 
              // Task.Run(async ()=> await Log(request))    //Approach 1 
              // Task.Factory.StartNew(async () => await Log(request)) //Approach 2
              // HostingEnvironment.QueueBackgroundWorkItem(ct => Log(request).ConfigureAwait(false)); //Approach 3
              // QueuedHostedService defined here [Microsoft][1]
              // var jobId = BackgroundJob.Enqueue(() => Log(request).ConfigureAwait(false)); //Approach 4 by hangfire [Hangfire][2]
              // Custome singleton suggested here // [CustomImplementation][1]
            }

            //No 2nd call needed
            await _appLogger.LogAsync(new ExceptionLog(ex));
            return Result{IsSuccess=false;};
         }
   }
   public async Task Log(Request request)
   {
        try
        {
         using var client = _httpClientFactory.CreateClient();
         using var httpRequest =new HttpRequestMessage(HttpMethod.Post, _url2);
         var req = JsonConvert.SerializeObject<Request>(body);
         var stringContent = new StringContent(req, Encoding.UTF8, "json");
         var result = await client.SendAsync(httpRequest);
         var resultContent = await result.Content.ReadAsStringAsync();
         var result = JsonConvert.DeserializeObject<Result>(resultContent));
          await _appLogger.LogAsync("2nd call successful.",result );

         }
         
         catch (Exception ex)
         {
          
            await _appLogger.LogAsync("2nd call failed",ex);
            
         }
   }
}

我一直在寻找许多解决方案,但现在对它们感到困惑。[1] : https ://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-6.0&tabs=visual-studio [2]:https://www. hangfire.io/ [3]:https ://anduin.aiursoft.com/post/2020/10/14/fire-and-forget-in-aspnet-core-with-dependency-alive

4

2 回答 2

0

我将尝试提供您可能的方法的细分:

- 触发并忘记调用: 它确实有效,是最容易实现的,但它不提供代码执行的任何保证或可见性。fire and forget 方法中的任何异常都将被吞噬。如果您的应用程序在执行 fire and forget 方法期间停止,您将无法获得任何重试尝试。如果调用不是强制性的,请使用此方法。

- Hangfire:它做你想做的事,它有几个选项,比如定义重试策略和设置你可以有多少并行执行。它支持多种不同的存储,包括内存、SQL 和 NoSQL。就个人而言,我是 HangFire 的忠实粉丝,并认为它是最强大的方法,同时易于实施。但是,它确实添加了一个依赖项,您可能需要与团队的其他成员进行讨论。

- 托管服务: 我不做你想要的开箱即用,因为托管服务更像是应用程序中的应用程序。您可以使用它来构建自定义的持久作业执行,但您将在很大程度上重新发明 Hangfire 功能的一个子集。这是迄今为止最复杂的方法,如果可能的话,我会避免它。

于 2021-12-03T12:27:07.270 回答
0

实际上,您正在寻找的是一种fire and forget机制,它应该在当前范围内独立工作。

我们都知道,一旦HttpRequest完成他们的响应,包括我们使用的所有服务在内的当前范围将被处置(默认范围级别行为)。所以,让我们想象一下第二个调用稍后回来,在原始 http 请求完成后,一切都已经处理并清除了。可恶的错误...

看看这个,并在你的 catch 块中处理它,事情应该是好的。

另一种方法是使用后台服务,这个想法是创建一个包含必要信息的队列,让后台扫描和处理它们,假设我们想要的任何间隔率。

于 2021-12-03T11:43:45.367 回答