保证在RabbitMQ上发布有关网络丢失的消息

时间:2016-12-22 08:56:33

标签: c# .net rabbitmq

我正在使用RabbitMQ .NET客户端,当网络断开连接时,我们的服务正在丢失消息。 我试图编写一个测试应用程序并使用“BasicAcks”事件,并重新发送断开连接时未收到确认的每条消息,但它仍然丢失消息。使用ConnectionShutdown事件检测断开连接(查找ReplyCode“451”)。 要检查收到的消息,我会使用它们并读取内容,该内容应至少包含0到49999之间的每个数字。 当网络稳定时,它可以正常工作。在模拟不稳定的网络(禁用网络适配器)时,有时会丢失数百条消息。

以下是代码:

private static ConcurrentQueue<byte[]> sendQueue = new ConcurrentQueue<byte[]>();
private static ConcurrentDictionary<ulong, byte[]> waitingForAck = new ConcurrentDictionary<ulong, byte[]>();
private static bool stop;

private static void Main()
{
    var server = "192.168.1.123";
    var userName = "rabbitmq";
    var password = "rabbitmq";
    var sendCount = 50000;
    try
    {
        Task.Run(() => Send(server, userName, password, "TestExchange", sendCount));

        Task.Run(() =>
        {
            while (true)
            {
                Console.WriteLine("Total sent:{0}", totalPackages.Count);
                Console.WriteLine("Packages waiting in send queue:{0}", sendQueue.Count);
                Console.WriteLine("Packages waiting for ack:{0}", waitingForAck.Count);
                Console.WriteLine();

                if (stop)
                {
                    break;
                }
                Thread.Sleep(1000);
            }
        });

        Console.ReadLine();

        stop = true;
    }
    catch (Exception exception)
    {
        Console.WriteLine("Exception: {0}", exception.Message);
    }

    Console.ReadLine();
}

public static void Send(string server, string userName, string password, string exchangeName, int sendCount)
{
    for (int i = 0; i < sendCount; i++)
    {
        var content = String.Format("Hello World: {0}", i);
        var data = Encoding.UTF8.GetBytes(content);
        sendQueue.Enqueue(data);
    }

    var factory = new ConnectionFactory { HostName = server, UserName = userName, Password = password };
    factory.AutomaticRecoveryEnabled = true;
    using (var connection = factory.CreateConnection())
    {
        connection.ConnectionShutdown += Connection_ConnectionShutdown;

        using (var channel = connection.CreateModel())
        {
            channel.ExchangeDeclare(exchangeName, ExchangeType.Fanout, false, false, null);

            channel.ConfirmSelect();

            channel.BasicAcks += Channel_BasicAcks;

            while (!stop)
            {
                byte[] data;
                if (!sendQueue.TryDequeue(out data))
                {
                    Thread.Sleep(100);
                    continue;
                }
                if (data == null)
                {
                    continue;
                }
                    var publishTag = channel.NextPublishSeqNo;

                    try
                    {
                        if (!waitingForAck.TryAdd(publishTag, data))
                        {
                            Console.WriteLine("Cannot prepare {0}", publishTag);
                        }

                        channel.BasicPublish(exchangeName, string.Empty, null, data);
                        totalPackages.Enqueue(data);
                    }
                    catch (Exception)
                    {
                        byte[] ignored;
                        if (!waitingForAck.TryRemove(publishTag, out ignored))
                        {
                            Console.WriteLine("cannot delete - exception");
                        }
                        Console.WriteLine("Requeue {0}", publishTag);
                        sendQueue.Enqueue(data);
                        Thread.Sleep(1000);
                        continue;
                    }

            }

            while (waitingForAck.Count > 0)
            {
                Thread.Sleep(1000);
                Console.WriteLine("Waiting for missing acks");
            }
        }
    }            
}

private static void Channel_BasicAcks(object sender, BasicAckEventArgs e)
{
    byte[] ignored;
    if (e.Multiple)
    {
        var ids = waitingForAck.Keys.Where(x => x <= e.DeliveryTag).ToArray();
        foreach (var id in ids)
        {
            if (!waitingForAck.TryRemove(id, out ignored))
            {
                Console.WriteLine("cannot delete {0}", id);
            }
        }
    }
    else
    {
        if (!waitingForAck.TryRemove(e.DeliveryTag, out ignored))
        {
            Console.WriteLine("cannot delete {0}", e.DeliveryTag);
        }
    }
}

private static void Connection_ConnectionShutdown(object sender, ShutdownEventArgs e)
{
    if (e.ReplyCode == 541)
    {
        var temp = waitingForAck.Values.ToList();
        waitingForAck.Clear();
        Console.WriteLine("Connection lost, requeue {0} messages", temp.Count);
        foreach (var message in temp)
        {
            sendQueue.Enqueue(message);
        }
    }
}

有人能告诉我我做错了吗?

2 个答案:

答案 0 :(得分:0)

您需要声明一个持久的队列:

channel.QueueDeclare(queue: "hello", durable: true);

this示例中一样。

答案 1 :(得分:0)

好的,问题不在于发送部分,而是接收部分。我在每封邮件sort_values

的末尾检查号码'i'时犯了一个错误

上面的代码工作正常,所有消息至少发送一次。