到期后,RabbitMQ未确认的消息不会从队列中删除

时间:2020-02-03 08:59:53

标签: rabbitmq timeout

我有一个运行简单的交换扇出队列绑定的RabbitMQ服务器(v.3.8.2),有多个生产者和一个消费者。平均传送/确认速率非常低,约为6 msg / s。

队列是由生产者在运行时创建的,其x-message-ttl参数设置为900000(15分钟)。

在非常特殊的条件下(例如,罕见的错误情况),消费者会拒绝消息。这些消息然后无限期地显示在RabbitMQ管理网页上的 unacked 计数器中。它们永不过期,也不会在超时后被丢弃。

ttl参数中没有特定的按消息覆盖。

我不需要任何死信处理,因为这些特殊消息不需要处理高可靠性,因此我可以承受在某些特定错误情况下不时丢失其中一些消息的可能性。

交换参数:

name: poll
type: fanout
features: durable=true
bound queue: poll
routing key: poll

队列参数:

name: poll
features: x-message-ttl=900000 durable=true

例如,这是我当前在RabbitMQ服务器队列管理页面中看到的内容:

"Poll" Queue summary

如您所见,队列中有12条被拒绝/未被确认的消息,并且它们已经在那儿居住了一个多星期。

如何按照ttl参数使退回的邮件过期? 我是否缺少一些配置?

以下是消费者源代码的摘录:

// this code is executed during setup
...
consumer = new EventingBasicConsumer(channel);
consumer.Received += (sender, e) =>
{
    // Retrieve retry count & death list if present
    List<object> DeathList = ((e?.BasicProperties?.Headers != null) && e.BasicProperties.Headers.TryGetValue("x-death", out object obj)) ? obj as List<object> : null;
    int count = ((DeathList != null) &&
        (DeathList.Count > 0) &&
        (DeathList[0] is Dictionary<string, object> values) &&
        values.TryGetValue("count", out obj)
    ) ? Convert.ToInt32(obj) : 0;

    // call actual event method
    switch (OnRequestReceived(e.Body, count, DeathList))
    {
        default:
            channel.BasicAck(e.DeliveryTag, false);
            break;
        case OnReceivedResult.Reject:
            channel.BasicReject(e.DeliveryTag, false);
            break;
        case OnReceivedResult.Requeue:
            channel.BasicReject(e.DeliveryTag, true);
            break;
    }
};
...

// this is the actual "OnReceived" method

static OnReceivedResult OnRequestReceived(byte[] payload, int count, List<object> DeathList)
{
    OnReceivedResult retval = OnReceivedResult.Ack; // success by default

    try
    {
        object request = MessagePackSerializer.Typeless.Deserialize(payload);
        if (request is PollRequestContainer prc)
        {
            Log.Out(
                Level.Info,
                LogFamilies.PollManager,
                log_method,
                null,
                "RequestPoll message received did={0} type=={1} items#={2}", prc.DeviceId, prc.Type, prc.Items == null ? 0 : prc.Items.Length
            );
            if (!RequestManager.RequestPoll(prc.DeviceId, prc.Type, prc.Items)) retval = OnReceivedResult.Reject;
        }
        else if (request is PollUpdateContainer puc)
        {
            Log.Out(Level.Info, LogFamilies.PollManager, log_method, null, "RequestUpdates message received dids#={0} type=={1}", puc.DeviceIds.Length, puc.Type);
            if (!RequestManager.RequestUpdates(puc.DeviceIds, puc.Type)) retval = OnReceivedResult.Reject;
        }
        else Log.Out(Level.Error, LogFamilies.PollManager, log_method, null, "Message payload deserialization error length={0} count={1}", payload.Length, count);
    }
    catch (Exception e)
    {
        Log.Out(Level.Error, LogFamilies.PollManager, log_method, null, e, "Exception dequeueing message. Payload length={0} count={1}", payload.Length, count);
    }

    // message is rejected only if RequestUpdates() or RequestPoll() return false
    // message is always acked if an exception occurs within the try-catch or if a deserialization type check error occurs
    return retval;
}

2 个答案:

答案 0 :(得分:0)

当消费者在收到消息后未确认或拒绝两个消息时,将发生此状态。
在未确认状态下,消息不会过期。
收到消息后,您必须确认或拒绝它。

这个问题不是没有过期的问题,而是您没有确认或拒绝邮件。

答案 1 :(得分:0)

x-message-ttl = 900000表示消息在队列中停留了多长时间而没有传递给使用者。

在您的情况下,您的消息已经传递给消费者了,需要被确认/拒绝。