簡體   English   中英

消費者運行時應用程序崩潰並退出調試器 & rabbit mq 服務重新啟動

[英]Application crashes & exits debugger while consumer is running & rabbit mq service restarts

我有一個 C# 應用程序,我試圖在其中測試我的 rabbit mq 客戶端的彈性。 當消費者正在運行時,我停止了 rabbit mq 服務以查看我的消費者將如何處理這個問題。

我已經try catch幾乎所有我的消費者,但由於后台線程中可能出現異常,我的應用程序在輸出窗口中打印如下並存在調試器。

線程 'AMQP Connection amqp://test.com:5671' (0x6da18) 已退出,代碼為 0 (0x0)。

“System.Net.WebException”類型的第一次機會異常發生在 System.dll 中

然后存在調試器。 我唯一注意到的是我的消費者類的析構函數在代碼存在之前被調用。

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Threading;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using RabbitMQ.Client.Exceptions;
using RabbitMQ.Util;

namespace RabbitMQClient
{
  public class MessageQueueConsumer : IHealthVerifiable
  {
    public class TimeoutException : Exception { }

    // Have to do this because, somehow, SharedQueue implementation of IEnumerable is faulty
    // Count() method hangs, and never returns
    private class BufferQueue : SharedQueue<BasicDeliverEventArgs>
    {
      public int Count()
      {
        return this.m_queue.Count;
      }
    }

    private const int DEFAULT_ACK_COUNT = 1000;

    private String connString;
    private QueueingBasicConsumer consumer;
    private IConnection conn;
    private IModel channel;
    private String queueName;
    private BufferQueue buffer;
    private Object locker = new Object();
    private ushort prefetchCount;
    private ushort ackCount;

    public MessageQueueConsumer(String queueName, String connString, ushort? ackCount = null)
    {
      this.queueName = queueName;
      this.connString = connString;
      if (ackCount != null)
        this.ackCount = ackCount.Value;
      else
        this.ackCount = DEFAULT_ACK_COUNT;
      this.prefetchCount = (ushort)(this.ackCount * 2);

      InitConsumer();
    }

    ~MessageQueueConsumer()
    {
      Close();
    }

    public void Close()
    {
      try
      {
        channel.Close(200, queueName + " Goodbye");
        conn.Close();
      }
      catch { } //if already closed, do nothing
    }

    private void InitConsumer()
    {
      try
      {
        ConnectionFactory factory = new ConnectionFactory();
        factory.Uri = connString;
        conn = factory.CreateConnection();
        channel = conn.CreateModel();
        channel.BasicQos(0, prefetchCount, false);
        buffer = new BufferQueue();

        consumer = new QueueingBasicConsumer(channel, buffer);
        channel.BasicConsume(queueName, false, consumer);
      }
      catch (Exception e)
      {
        InitConsumer();
      }
    }

    /// <summary>
    /// Get the next event from the queue
    /// </summary>
    /// <returns>Event</returns>
    public byte[] Dequeue(int? timeout = null)
    {
      lock (locker)
      {
        try
        {
          return AttemptDequeue(timeout);
        }
        catch (EndOfStreamException)
        {
          // Network interruption while reading the input stream
          InitConsumer();
          return AttemptDequeue(timeout);
        }
        catch (OperationInterruptedException)
        {
          // The consumer was removed, either through channel or connection closure, or through the
          // action of IModel.BasicCancel().
          // Attempt to reopen and try again
          InitConsumer();
          return AttemptDequeue(timeout);
        }
        catch (ConnectFailureException)
        {
          //Problems connecting to the queue, wait 10sec, then try again. 
          Thread.Sleep(10000);
          InitConsumer();
          return AttemptDequeue(timeout);
        }
        catch (Exception e)
        {
          //Problems connecting to the queue, wait 10sec, then try again. 
          Thread.Sleep(10000);
          InitConsumer();
          return AttemptDequeue(timeout);
        }
      }
    }

    private byte[] AttemptDequeue(int? tomeout)
    {
      BasicDeliverEventArgs message;

      if (tomeout == null)
        message = (BasicDeliverEventArgs)consumer.Queue.Dequeue();
      else
      {
        if (!consumer.Queue.Dequeue(tomeout.Value, out message))
          throw new TimeoutException();
      }

      if (buffer.Count() == 0 || buffer.Count() == ackCount)
        channel.BasicAck(message.DeliveryTag, true);

      try
      {
        return message.Body;
      }
      catch (Exception e)
      {
        throw new SerializationException("Error deserializing queued message:", e);
      }
    }

    /// <summary>
    /// Attempt to connect to queue to see if it is available
    /// </summary>
    /// <returns>true if queue is available</returns>
    public bool IsHealthy()
    {
      try
      {
        if (channel.IsOpen)
          return true;
        else
        {
          InitConsumer();
          return true;
        }
      }
      catch
      {
        return false;
      }
    }
  }
}

任何想法如何捕獲此異常並嘗試重試連接?

問題是使用QueueingBasicConsumer沒有實現任何恢復方法。 我改為EventingBasicConsumer並從故障中恢復。

namespace RabbitMQClient {   public class MessageQueueConsumer : IHealthVerifiable   {
    public class TimeoutException : Exception { }

private class BufferQueue : SharedQueue<BasicDeliverEventArgs>
{
  public int Count()
  {
    return this.m_queue.Count;
  }
}

private const int DEFAULT_ACK_COUNT = 1000;

private String connString;
private EventingBasicConsumer consumer;
private IConnection conn;
private IModel channel;
private String queueName;
private BufferQueue buffer;
private Object locker = new Object();
private ushort prefetchCount;
private ushort ackCount;

public MessageQueueConsumer(String queueName, String connString, ushort? ackCount = null)
{
  this.queueName = queueName;
  this.connString = connString;
  if (ackCount != null)
    this.ackCount = ackCount.Value;
  else
    this.ackCount = DEFAULT_ACK_COUNT;
  this.prefetchCount = (ushort)(this.ackCount * 2);

  InitConsumer();
}

~MessageQueueConsumer()
{
  Close();
}

public void Close()
{
  try
  {
    channel.Close(200, queueName + " Goodbye");
   // conn.Close();
  }
  catch { } //if already closed, do nothing
}

private void InitConsumer()
{
  ConnectionFactory factory = new ConnectionFactory();
  factory.Uri = connString;
  conn = factory.CreateConnection();
  channel = conn.CreateModel();
  channel.BasicQos(0, prefetchCount, false);
  buffer = new BufferQueue();

  consumer = new EventingBasicConsumer(channel);
  channel.BasicConsume(queueName, false, consumer);

  // when message is recieved do following
  consumer.Received += (model, message) =>
  {

      if (buffer.Count() > DEFAULT_ACK_COUNT)
        Thread.Sleep(3000);

        buffer.Enqueue(message);

        if (buffer.Count() == 0 || buffer.Count() == ackCount)
          channel.BasicAck(message.DeliveryTag, true);


  };
}

/// <summary>
/// Get the next event from the queue
/// </summary>
/// <returns>Event</returns>
public byte[] Dequeue(int? timeout = null)
{
  lock (locker)
  {
    try
    {
      return AttemptDequeue(timeout);
    }
    catch (EndOfStreamException)
    {
      // Network interruption while reading the input stream
      InitConsumer();
      return AttemptDequeue(timeout);
    }
    catch (OperationInterruptedException)
    {
      // The consumer was removed, either through channel or connection closure, or through the
      // action of IModel.BasicCancel().
      // Attempt to reopen and try again
      InitConsumer();
      return AttemptDequeue(timeout);
    }
    catch (ConnectFailureException)
    {
      //Problems connecting to the queue, wait 10sec, then try again. 
      Thread.Sleep(10000);
      InitConsumer();
      return AttemptDequeue(timeout);
    }
  }
}

private byte[] AttemptDequeue(int? tomeout)
{
  BasicDeliverEventArgs message;

  while (true)
  {
    //while buffer has no events
    if (buffer.Count() == 0)
    {
      Thread.Sleep(3000);
    }
    else
    {
      message = buffer.Dequeue();
      break;
    }

  }

  try
  {
    return message.Body;
  }
  catch (Exception e)
  {
    throw new SerializationException("Error deserializing queued message:", e);
  }
}

/// <summary>
/// Attempt to connect to queue to see if it is available
/// </summary>
/// <returns>true if queue is available</returns>
public bool IsHealthy()
{
  try
  {
    if (channel.IsOpen)
      return true;
    else
    {
      InitConsumer();
      return true;
    }
  }
  catch
  {
    return false;
  }
}   } }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM