0
votes

We have an ASP.Net WebAPI 2.0 application that publishes messages to our RabbitMQ Server. In 99% of the cases, all is well... but randomly the application terminates with a System.AccessViolationException for no apparent reason.

How can I prevent this failure? Could it be related to our recent upgrade to the 3.6.6 C# driver (it worked fine before the upgrade)?

Things I have already eliminated:

  1. A new IModel is used for each publish (I know IModel is not thread safe)
  2. A call is made to CreateConnection for each call as well (I know I could reuse the connection, but we do not currently). The connection is AutoClose = true;
  3. The Channel is used in a using block... so it is disposed each time

Here is a sample stack trace of where it explodes:

Exception Details

System.AccessViolationException

Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

at System.Net.UnsafeNclNativeMethods.SafeNetHandlesXPOrLater.GetAddrInfoW(String nodename, String servicename, AddressInfo& hints, SafeFreeAddrInfo& handle) at System.Net.Dns.TryGetAddrInfo(String name, AddressInfoHints flags, IPHostEntry& hostinfo) at System.Net.Dns.InternalGetHostByName(String hostName, Boolean includeIPv6) at System.Net.Dns.GetHostAddresses(String hostNameOrAddress) at RabbitMQ.Client.TcpClientAdapter.BeginConnect(String host, Int32 port, AsyncCallback requestCallback, Object state) at RabbitMQ.Client.Impl.SocketFrameHandler.Connect(ITcpClient socket, AmqpTcpEndpoint endpoint, Int32 timeout) at RabbitMQ.Client.Impl.SocketFrameHandler..ctor(AmqpTcpEndpoint endpoint, Func`2 socketFactory, Int32 connectionTimeout, Int32 readTimeout, Int32 writeTimeout) at RabbitMQ.Client.Framing.Impl.ProtocolBase.CreateFrameHandler(AmqpTcpEndpoint endpoint, Func'2 socketFactory, Int32 connectionTimeout, Int32 readTimeout, Int32 writeTimeout) at RabbitMQ.Client.ConnectionFactory.CreateConnection(IList'1 endpoints, String clientProvidedName)

And another

System.Net.UnsafeNclNativeMethods+SafeNetHandlesXPOrLater.GetAddrInfoW(System.String, System.String, System.Net.AddressInfo ByRef, System.Net.SafeFreeAddrInfo ByRef) System.Net.Dns.TryGetAddrInfo(System.String, System.Net.AddressInfoHints, System.Net.IPHostEntry ByRef) System.Net.Dns.InternalGetHostByName(System.String, Boolean) System.Net.Dns.GetHostAddresses(System.String) RabbitMQ.Client.TcpClientAdapter.BeginConnect(System.String, Int32, System.AsyncCallback, System.Object) RabbitMQ.Client.Impl.SocketFrameHandler.Connect(RabbitMQ.Client.ITcpClient, RabbitMQ.Client.AmqpTcpEndpoint, Int32) RabbitMQ.Client.Impl.SocketFrameHandler..ctor(RabbitMQ.Client.AmqpTcpEndpoint, System.Func'2, Int32, Int32, Int32) RabbitMQ.Client.Framing.Impl.ProtocolBase.CreateFrameHandler(RabbitMQ.Client.AmqpTcpEndpoint, System.Func'2, Int32, Int32, Int32) RabbitMQ.Client.ConnectionFactory.CreateConnection(System.Collections.Generic.IList'1, System.String)

2

2 Answers

0
votes

I'm not sure exactly why your error is occurring I would need to see some of your code for that, but I use RabbitMQ quite a bit and to publish I use a class like so:

(Changed some parts as they are not relevant for your situation such as encryption, compression etc. But this would be the basic format of it)

using System;
using System.Text;
using RabbitMQ.Client;
using RabbitMQ.Client.Framing;

namespace Messaging
{
    public class MessageSender : IDisposable
    {
        private const string EXCHANGE_NAME = "MY_EXCHANGE";

        private readonly ConnectionFactory factory;
        private readonly IConnection connection;
        private readonly IModel channel;

        public MessageSender(string address, string username, string password)
        {
            factory = new ConnectionFactory {UserName = username, Password = password, HostName = address};

            connection = factory.CreateConnection();
            channel = connection.CreateModel();

            channel.ExchangeDeclare(EXCHANGE_NAME, "topic");
        }

        public void Send(string payload, string topic)
        {
            var prop = new BasicProperties();
            var data = Encoding.ASCII.GetBytes(payload);

            channel.BasicPublish(EXCHANGE_NAME, topic.ToUpper(), prop, data);
        }

        public void Dispose()
        {
            try
            {
                channel.Dispose();
                connection.Dispose();
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }
        }
    }
}

The idea is to let you issue multiple calls or a single one and dispose of the class when you wish. Wrap it with a using statement and your set.

Never had a single problem with this in about 3-4 years of using it, so you might be able to contrast this vs your code to find the differences.

0
votes

Managed to get a (partial) resolution on this from RabbitMQ users group.

Details here: Rabbit MQ Users Group

Basic idea is that the IConnection object should be shared, as it is heavy to open and close. Only the IModel should be opened fresh for each thread