我对以下Rebus重试政策有疑问:
Configure.With(...)
.Options(b => b.SimpleRetryStrategy(maxDeliveryAttempts: 2))
.(...)
https://github.com/rebus-org/Rebus/wiki/Automatic-retries-and-error-handling#customizing-retries
1它既可以用于发布者(入队消息)又可以用于订户(出队消息)吗?
2我有一个无法使消息出队的订户。因此,消息被发送到错误队列。
以下是将消息放入错误队列时的错误。但是我看不到重试日志。
[ERR] Rebus.Retry.PoisonQueues.PoisonQueueErrorHandler (Thread #9): Moving messa
ge with ID "<unknown>" to error queue "poison"
Rebus.Exceptions.RebusApplicationException: Received message with empty or absen
t 'rbs2-msg-id' header! All messages must be supplied with an ID . If no ID is p
resent, the message cannot be tracked between delivery attempts, and other stuff
would also be much harder to do - therefore, it is a requirement that messages
be supplied with an ID.
是否可以为每次重试定义和存储自定义日志记录,而不是在IErrorHandler中?
3默认情况下,两次重试之间要等待多长时间?
4是否可以为每次重试定义自定义等待时间(不在IErrorHandler中)?如果是,此扫描方式是否支持Polly?如下所示:
Random jitterer = new Random();
Policy
.Handle<HttpResponseException>() // etc
.WaitAndRetry(5, // exponential back-off plus some jitter
retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))
+ TimeSpan.FromMilliseconds(jitterer.Next(0, 100))
);
更新
如何测试重试策略?
以下是我根据以下代码尝试的内容:
public class StringMessageHandler : IHandleMessages<String>
{
public async Task Handle(String message)
{
//retry logic with Polly here
}
}
我向字符串主题发送了一个字符串类型的无效消息,但是,根本没有调用Handle(String message)。
答案 0 :(得分:1)
Rebus的重试机制仅在接收消息时才相关。它通过创建“队列事务”(*)来工作,然后,如果消息处理失败,则消息将回滚到队列。
此后不久,该消息将再次被接收并尝试进行处理。这意味着交付尝试之间没有延迟。
对于每次失败的传递,该消息的ID的计数器都会增加。这就是为什么要使Rebus正常工作必须使用消息ID的原因,这也解释了为什么没有ID的消息会立即移至死信队列。
由于投递尝试的不连贯性(每个消息ID仅存储一个计数器),因此没有很好的位置挂接到Polly之类的重试库中。
如果您想简化消息处理,建议您使用Polly策略执行单独的操作-这样,您可以轻松地使用不同的策略来处理失败的Web请求,失败的网络驱动器上的文件传输等。我自己很多。
为避免在长时间的Polly重试过程中无法正确关闭总线实例,可以将Rebus的内部CancellationToken
传递给Polly执行,如下所示:
public class PollyMessageHandler : IHandleMessages<SomeMessage>
{
static readonly IAsyncPolicy RetryPolicy = Policy
.Handle<Exception>()
.WaitAndRetryForeverAsync(_ => TimeSpan.FromSeconds(10));
readonly IMessageContext _messageContext;
public PollyMessageHandler(IMessageContext messageContext)
{
_messageContext = messageContext;
}
public async Task Handle(SomeMessage message)
{
var cancellationToken = _messageContext.GetCancellationToken();
await RetryPolicy.ExecuteAsync(DoStuffThatCanFail, cancellationToken);
}
async Task DoStuffThatCanFail(CancellationToken cancellationToken)
{
// do your risky stuff in here
}
}
(*)事务的实际类型取决于传输支持的内容。
对于MSMQ,它是一个MessageQueueTransaction
对象,上面具有Commit()
和Rollback()
方法。
对于RabbitMQ,Azure Service Bus和其他服务器,它是基于租约的协议,其中消息在一段时间内不可见,然后,如果在该时间内确认了该消息,则该消息将被删除。否则-如果消息已被拒绝或已被拒绝,或者租约到期,则消息会再次弹出,并可以被其他使用者再次接收。
使用SQL传输,这只是数据库事务。