我有一项服务,可以使用
FileStream,
通过某些网络写入文件,而不会出现写入超时。我添加了一个 Polly WaitAndRetry
策略来处理偶尔的写入失败,效果很好。我注意到有时写入会挂起,所以我尝试将超时策略包装在重试策略中,以限制每次写入尝试。
public class Retrier
{
private readonly CancellationToken _cancellationToken;
public Retrier(CancellationToken cancellationToken)
{
_cancellationToken = cancellationToken;
}
public void Start(IService service)
{
var retryPolicy = Policy
.Handle<IOException>()
.WaitAndRetry(
retryCount: 3,
sleepDurationProvider: (retries) => TimeSpan.FromSeconds(retries * 10));
var timeoutPolicy = Policy.Timeout(seconds: 60);
var combinedPolicy = retryPolicy.Wrap(timeoutPolicy);
var result = combinedPolicy.ExecuteAndCapture(
(ct) =>
{
service.Write(ct);
},
_cancellationToken);
// Do some other stuff
}
}
但是,使用包装策略时,根本不会调用写入操作,例如输入
ExecuteAndCapture
并调用操作,但执行只是继续在下面“执行其他操作”。一个简单的测试方法验证 Write
被调用了零次。
[TestMethod]
public void Retrier_Start_When_IOExceptionOnce_CallsExecuteTwice()
{
var attempt = 0;
var mock = new Mock<IService>();
mock
.Setup(svc => svc.Write(_cancellationTokenSource.Token))
.Returns(() =>
{
attempt++;
if (attempt == 1)
{
throw new IOException("Failed");
}
return 1;
})
.Verifiable();
_testee.Start(mock.Object);
mock.Verify(svc => svc.Write(_cancellationTokenSource.Token), Times.Exactly(2));
}
Write 是一项简单的服务,没有魔法:
public int Write(CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var tempFullPath = ...
using (var fw = new FileStream(tempFullPath, FileMode.Create, FileAccess.Write))
{
fw.Write(_buffer, 0, _buffer.Length);
}
File.Move(tempFullPath, _finalFullPath);
return ++Count;
}
我浏览了类似的问题,但找不到解决方案。 我做错了什么?
如果超时策略生效,则会抛出一个
TimeoutRejectedException
。
因此,您还需要修改重试来处理该异常。
var timeoutPolicy = Policy.Timeout(seconds: 60);
var retryPolicy = Policy
.Handle<IOException>()
.Or<TimeoutRejectedException>()
.WaitAndRetry(
retryCount: 3,
sleepDurationProvider: (retries) => TimeSpan.FromSeconds(retries * 10));
var combinedPolicy = Policy.Wrap(retryPolicy, timeoutPolicy);
更新#1
这是一个带有工作示例的 dotnet fiddle 链接: http://dotnetfiddle.net/uTvCJA