在 c# 异常处理中有没有办法检测瞬态错误?

In the c# Exception handling is there a way to detect transient errors?

我正在使用 Azure,我有简单的 try catch 块 我现在正在做的是,当我们遇到任何错误时,我会通过电子邮件发送错误消息,现在我想检测暂时性错误,而忽略为他们发送电子邮件

private void SomeMethod()
{
    try
    {
        // Do stuff
    }
    catch (Exception ex) 
    {         
         HandleError(ex);
         return RedirectToAction("Index", "Error");
    }
}

protected void HandleError(Exception ex) 
{
      //and here i want to check if the cause of exception is not a transient      error then write a code to email the error details 
}

基于您刚刚发布的 link 和提供的代码示例:

// Define your retry strategy: retry 3 times, 1 second apart.
var retryStrategy = new FixedInterval(3, TimeSpan.FromSeconds(1));

// Define your retry policy using the retry strategy and the Azure storage
// transient fault detection strategy.
var retryPolicy = 
  new RetryPolicy<StorageTransientErrorDetectionStrategy>(retryStrategy);

// Do some work that may result in a transient fault.
try
{
  // Call a method that uses Azure storage and which may
  // throw a transient exception.
  retryPolicy.ExecuteAction(
    () =>
    {
        this.queue.CreateIfNotExist();
    });
}
catch (Exception)
{
  // All of the retries failed.
}

根据该文档,不会引发异常 直到 它不再是重试策略定义的暂时性错误。实际上 - 利用瞬态故障处理应用程序块已经在做你在问题中所问的事情。重试会静默重试(无一例外),直到出现异常的时间点 - 当重试超出您的重试策略时。

以下不应视为"good code",它只是TFHAB 如何确定一个暂时性错误。

private void DoStuff()
{
    try
    {
        this.DoSomethingThatCouldPotentiallyCauseTransientErrors(5);
    }
    catch (Exception ex)
    {
        // This would not be caught until the "retries" have occurred.
        this.HandleException(ex);
        return RedirectToAction("Index", "Error");
    }
}

private void DoSomethingThatCouldPotentiallyCauseTransientErrors(int retryAttemptsBeforeExceptionThrown)
{
    // Note that this will *always throw an exception*, 
    // I'm (attempting to) simply demonstrate my point of how the transient errors could be defined.

    for (int i = 0; i < retryAttemptsBeforeExceptionThrown)
    {
        try    
        {
            int x = 0;
            int y = 0;

            int result = x / y;
        }
        catch (Exception)
        {
            if (i < retryAttemptsBeforeExceptionThrown-1)
            {
                // Swallow/ignore the exception, and retry
                // Note that anything hitting this block would be considered a "transient error", 
                // as we are not raising an exception
            }
            else
            {
                // Too many failed attempts have occurred, ***now*** we raise an exception to the caller
                throw;
            }
        }
    }
}

private void HandleException(Exception ex)
{
    // Implementation
}

希望这能回答你向 John 提出的有关 "transient error"

的问题

您可以使用来自我的 Apache 许可的信号 Griffin.Framework

public class YourController
{
    static Signal _errorsignal = Signal.Create("YourController.Error");

    public ActionResult SomeAction()
    {
        try
        {
            //some logic

            //logic succeeded, reset
            _errorSignal.Reset();
        }
        catch (Exception ex) 
        { 
            //Raised for the first time, notify
            if (_errorSignal.Raise(ex))
                HandleError(ex); 
            return RedirectToAction("Index", "Error"); 
        }

    //[...]
}

它跟踪状态,还可以将状态变化(来自所有信号)上传到 HTTP 服务器。

除了在每个控制器中都出现 HandleError 之外,您还可以将以下内容放入 global.asax:

//put in the Init method
Signal.SignalRaised += OnGlobalHandling;

public void OnGlobalHandling(object sender, SignalRaisedEventArgs e)
{
    var signal = (Signal)sender;
    //send email
}

如果您使用的是Azure Client SDK(最新版本>= v4.3.0.0),默认重试策略是指数重试。即使您没有专门定义任何重试策略也是如此。客户端 SDK 指数重试策略具有基于几乎从 Azure 存储服务响应返回的 HttpStatusCode 来检测暂时性错误的逻辑。当您向 Azure 存储发出请求并且该请求失败并出现客户端 sdk 认为是暂时的错误(几乎所有 5xx Http 状态代码)时,所有这些重试都将对您的代码透明地完成。

如果你想完全停止客户端 SDK 重试并在你的 catch 块中完全控制它,那么你可以将 NoRetry 策略作为你的 [Table/Blob/Queue]RequestOptions 的一部分来传递由客户端 SDK 完成的后台重试。

然后在你的 catch 块中,你需要捕获客户端 SDK 在你失败的请求时抛出的 StorageException ,解析其中嵌入的 HttpStatusCode ,根据该错误是否为暂时性错误做出决定,并且采取相应的行动。

当然不是所有的异常都是 StorageException ,您也可能会遇到非存储异常,例如 null ref 等。您也可以对这些异常执行相同的处理,它们通常是非瞬态的。

如果你想走那条路,你应该确切地知道你在做什么,实际的客户端 SDK 在它自己的瞬态错误检测和重试机制中做了什么,以及完全关闭它的影响。我见过很多情况,其中 fiddle 使用 Azure 存储 RetryPolicies 的人没有完全理解它的作用,最终导致服务过载,不必要的重试已经由客户端 SDK 重试,或者导致更多的延迟、更低的吞吐量等。