首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Fallback.AsyncFallbackEngine异常

Fallback.AsyncFallbackEngine异常
EN

Stack Overflow用户
提问于 2021-07-15 10:20:47
回答 1查看 202关注 0票数 1

我在.NET Framework4.8 (WCF)中有一个应用程序,它进行http调用,也使用Polly进行重试和回退管理,但有时会引发System.NullReferenceException,但我不知道问题出在哪里。这是代码:

代码语言:javascript
复制
private static async Task<bool> CallApi<TRequest>(TRequest request, string requestUri, Func<string, StringContent, Task<HttpResponseMessage>> func)
{
    var jsonRequest = JsonConvert.SerializeObject(request);

    var fallBackPolicy = Policy<HttpResponseMessage>.Handle<Exception>()
        .FallbackAsync(new HttpResponseMessage(HttpStatusCode.SeeOther)
        {
            Content = new StringContent($"Exception has occurred in migration call. RequestUri: {requestUri}")
        }, 
        result => 
        {
            LogEventService.Logger.Error(result.Exception, "An unhandled exception occurred while retrying calling");
            return Task.CompletedTask;
        });

    var waitAndRetryPolicy = Policy.HandleResult<HttpResponseMessage>(res => res.StatusCode == HttpStatusCode.InternalServerError).
        WaitAndRetryAsync(2, retryAttempts => TimeSpan.FromMilliseconds(500));

    var response = await fallBackPolicy
        .WrapAsync(waitAndRetryPolicy)
        .ExecuteAsync(async () =>
        {
            using (var content = new StringContent(jsonRequest, Encoding.UTF8, "application/json"))
            {
                content.Headers.Add("X-Correlation-ID", HttpContext.Current.Session[RequestId].ToString());

                return await func(requestUri, content);
            }
        });
    
    if(response.IsSuccessStatusCode)
        return true;

    await LogMessage(LogLevel.Error, response, requestUri);
    
    return false;
}

这是StackTrace

System.NullReferenceException:对象引用没有设置为对象的实例。 在UserMigrationService.<>c__DisplayClass22_0'1.d.MoveNext()在...\Services\UserMigrationService.cs:line 474年 -从抛出异常的前一个位置开始的堆栈跟踪的结束--在System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()。 在Polly.Retry.AsyncRetryEngine.d__0'1.MoveNext() -从抛出异常的前一个位置开始的堆栈跟踪的结束--在System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()。 (在System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task任务) at‘1.d_13.13.MoveNext() -从抛出异常的前一个位置开始的堆栈跟踪的结束--在System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()。 (在System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task任务) 在Polly.Wrap.AsyncPolicyWrapEngine.<>c__DisplayClass0_0'1.d.MoveNext() -从抛出异常的前一个位置开始的堆栈跟踪结束-- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() > at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task任务) 在Polly.Fallback.AsyncFallbackEngine.d__0'1.MoveNext()

你能帮我找错地方吗?如何更好地理解抛出此异常的原因?

谢谢大家

EN

回答 1

Stack Overflow用户

发布于 2021-07-16 09:32:35

实际上,NRE被抛到了这一行:

代码语言:javascript
复制
ontent.Headers.Add("X-Correlation-ID", HttpContext.Current.Session[RequestId].ToString());

这表明HttpContext.Current可能是nullExecuteAsync接收的委托可能与CallApi方法的其余代码不在同一线程上运行。

这就是为什么HttpContext不会流到委托中。

修复非常容易:您必须在CallApi中捕获CallApi,而不是在ExecuteAsync委托中捕获:

代码语言:javascript
复制
var correlationId = HttpContext.Current.Session[RequestId].ToString();
var response = await fallBackPolicy
        .WrapAsync(waitAndRetryPolicy)
        .ExecuteAsync(async () =>
        {
            using (var content = new StringContent(jsonRequest, Encoding.UTF8, "application/json"))
            {
                content.Headers.Add("X-Correlation-ID", correlationId);
                return await func(requestUri, content);
            }
        });

我还建议使用Policy.Wrap (参考文献),而不是在其中一个策略上调用WrapAsync方法。以下两行相等:

代码语言:javascript
复制
fallBackPolicy.WrapAsync(waitAndRetryPolicy)
Policy.WrapAsync(fallbackPolicy, waitAndRetryPolicy)

因此,您的代码可以这样重写:

代码语言:javascript
复制
var correlationId = HttpContext.Current.Session[RequestId].ToString();
var strategy = Policy.WrapAsync(fallbackPolicy, waitAndRetryPolicy);
var response = await strategy
        .ExecuteAsync(async (ct) =>
        {
            using (var content = new StringContent(jsonRequest, Encoding.UTF8, MediaTypeNames.Application.Json))
            {
                content.Headers.Add("X-Correlation-ID", correlationId);
                //TODO: pass the cancellationToken to the func
                return await func(requestUri, content);
            }
        }, CancellationToken.None);

我使用了ExecuteAsync的另一个重载,在这里您正在接收一个CancellationToken。无论何时考虑使用TimeoutPolicy,这都是非常有用的。在这种情况下,您应该将该令牌传递给func函数。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68392013

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档