如何在Servicestack JsonServiceClient的Get方法中实现重试的最佳解决方案?

4

在我的项目中,我使用Servicestack从特定的URL获取数据。这个过程是可配置的,并且我在单独的线程中调用获取数据的方法。如果超时错误被引发,我想要实现重试机制。我创建了一个JsonServiceClient的包装类,并在那里实现了重试机制,但我想知道这种方法的最佳解决方案是什么。

var _client = new JsonServiceClient { Timeout = timeout };
var counter = 0;
do
{
    try
    {
        result = _client.Get<TResponse>(url);
        break;
    }
    catch (Exception exp)
    {
        //Logging exception
    }
}
while (++counter < this.Retry);

请查看http://www.hanselman.com/blog/NuGetPackageOfTheWeekPollyWannaFluentlyExpressTransientExceptionHandlingPoliciesInNET.aspx。 - abatishchev
谢谢@abatishchev,我已经看到这些API了,它们真的很棒,但在这种情况下无法使用它们,因为我想针对特定情况实现重试,并且根据条件不同设置不同的重试次数。 - Peyman
1个回答

6
我在JsonServiceClient上创建了一个包装类,并在那里实现了重试,但我想知道这种方法的最佳解决方案是什么。
我同意你的方法。扩展JsonServiceClient并在那里实现重试逻辑是可重用性和可维护性最好的方法,前提是你已经实现了下面这样的内容。

扩展JsonServiceClient

扩展JsonServiceClient以便你可以整合自己的重试逻辑。然后,你可以在代码中轻松地重复使用它,而不需要每次想要进行请求时都使用while和计数器。
如果你在这里看到{{link1:JsonServiceClientBase.cs}}代码,你会注意到所有动词方法,例如Get<TResponse>Post<TResponse>...Put等,都通过Send<TResponse>(object request)方法调用。
因此,通过覆盖此方法,我们可以在所有动词上轻松实现重试功能,而无需更改其用法。
public class MyJsonServiceClientWithRetry : JsonServiceClient
{

    public MyJsonServiceClientWithRetry()
    {
    }

    public MyJsonServiceClientWithRetry(int retryAttempts)
    {
        RetryAttempts = retryAttempts;
    }

    public MyJsonServiceClientWithRetry(string baseUri) : base(baseUri)
    {
    }

    public MyJsonServiceClientWithRetry(string syncReplyBaseUri, string asyncOneWayBaseUri) : base(syncReplyBaseUri, asyncOneWayBaseUri)
    {
    }


    // Retry attempts property
    public int RetryAttempts { get; set; }


    public override TResponse Send<TResponse> (string httpMethod, string relativeOrAbsoluteUrl, object request)
    {
        int attempts = RetryAttempts;

        while(true) 
        {
            attempts--;

            try {
                return base.Send<TResponse> (httpMethod, relativeOrAbsoluteUrl, request);
            } catch (WebServiceException webException) {

                // Throw the exception if the number of retries is 0 or we have made a bad request, or are unauthenticated
                if(attempts == 0 || webException.StatusCode == 400 || webException.StatusCode == 401)
                    throw;

            } catch (Exception exception) {

                // Throw the exception if the number of retries is 0
                if(attempts == 0) 
                    throw;
            }
        }
    }
}

使用方法:

  • 将对 JsonServiceClient 的引用替换为 MyJsonServiceClientWithRetry
  • 设置尝试次数
  • 像平常一样使用客户端。(在重试次数超过限制后,使用 try/catch 块捕获异常)
var client = new MyJsonServiceClientWithRetry ("http://localhost:8080") {
    RetryAttempts = 3,
    Timeout = new TimeSpan(0, 0, 30)
};


try
{
    var myRequestDto = new MyRequest {
        Name = "John Smith"
    };

    // This request will be attempted 3 times (if there is an exception)
    var response = client.Get<MyResponse>(myRequestDto);

    // Do something with response ...

} catch(Exception ex) {
    // Exception thrown here after 3 attempts (or immediately if status code is 400 / 401)
}

注意事项:

如果出现状态码为400或401的WebServiceException异常,我不会重试,因为不改变请求再次尝试似乎是多余的。当然,您可以自定义此逻辑。

如果连接超时,则会抛出超时错误作为WebException。如果您想要特别处理此情况。

希望这有所帮助。


谢谢 @Scott,我猜只需要做一个简单的修改。应该覆盖 Get 方法而不是 Send。 - Peyman
@Peyman 不用谢。你不需要覆盖 Get 方法,因为底层的 Get 方法调用了你已经重写的 Send<TResponse> 方法。我添加了一个小修复,因为我错过了 Send 方法的参数。但是上面的代码应该允许调用 Get、Put、Post、Delete 和 Send,而无需覆盖每个动词方法。 - Scott

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接