RabbitMQ C# 驱动程序导致 System.AccessViolationException

Posted

技术标签:

【中文标题】RabbitMQ C# 驱动程序导致 System.AccessViolationException【英文标题】:RabbiMQ C# driver Causes System.AccessViolationException 【发布时间】:2017-05-31 11:34:15 【问题描述】:

我们有一个 ASP.Net WebAPI 2.0 应用程序,可以将消息发布到我们的 RabbitMQ 服务器。在 99% 的情况下,一切都很好……但随机地,应用程序以 System.AccessViolationException 无故终止。

如何防止这种失败?会不会与我们最近升级到 3.6.6 C# 驱动有关(升级前它运行良好)?

我已经消除的东西:

    每次发布​​都使用一个新的IModel(我知道IModel不是线程 安全) 每次通话都会拨打CreateConnection(我 知道我可以重用连接,但我们目前没有)。连接是AutoClose = true; Channel 用在 using 块中...所以每次都被释放

这里是它爆炸位置的示例堆栈跟踪:

异常详情

System.AccessViolationException

试图读或写保护 记忆。这通常表明其他内存已损坏。

在 System.Net.UnsafeNclNativeMethods.SafeNetHandlesXPOrLater.GetAddrInfoW(String nodename, String servicename, AddressInfo& 提示, SafeFreeAddrInfo& 处理) 在 System.Net.Dns.TryGetAddrInfo(字符串名称、AddressInfoHints 标志、IPHostEntry& 主机信息) 在 System.Net.Dns.InternalGetHostByName(字符串主机名,布尔值 includeIPv6) 在 System.Net.Dns.GetHostAddresses(字符串主机名或地址) 在 RabbitMQ.Client.TcpClientAdapter.BeginConnect(字符串主机,Int32 端口,AsyncCallback requestCallback,对象状态) 在 RabbitMQ.Client.Impl.SocketFrameHandler.Connect(ITcpClient 套接字,AmqpTcpEndpoint 端点,Int32 超时) 在 RabbitMQ.Client.Impl.SocketFrameHandler..ctor(AmqpTcpEndpoint 端点,Func`2 socketFactory,Int32 connectionTimeout,Int32 读超时,Int32 写超时) 在 RabbitMQ.Client.Framing.Impl.ProtocolBase.CreateFrameHandler(AmqpTcpEndpoint 端点,Func'2 socketFactory,Int32 connectionTimeout,Int32 读超时,Int32 写超时) 在 RabbitMQ.Client.ConnectionFactory.CreateConnection(IList'1 端点,字符串 clientProvidedName)

还有一个

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)

【问题讨论】:

【参考方案1】:

我不确定您的错误发生的确切原因我需要查看您的一些代码,但我使用 RabbitMQ 并发布我使用这样的类:

(更改了一些与您的情况无关的部分,例如加密,压缩等。但这将是它的基本格式)

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);
            
        
    

这个想法是让您发出多个调用或单个调用,并在您愿意时处理类。用 using 语句和你的集合包装它。

在使用它大约 3-4 年的时间里,从未遇到过任何问题,因此您可以将其与您的代码进行对比以找出差异。

【讨论】:

感谢您的意见。 我们的实现不会每次都创建一个新的“Sender”类,但我们确实将 Channel 包装在 using 中。 作为记录,我的实现也已经运行了 3 年,没有任何问题...最近才开始失败。可能是我刚升级到(3.6.6)的驱动版本有bug 你运行的是什么版本的 RabbitMQ。我们发现了 3.6.4 的几个问题。主要问题是心跳导致客户端出现故障并会中断连接。 (根据补丁说明,我相信在 3.3.6 中已修复)根据您的错误处理,这可能是它。仅通过 Windows 事件日志注意到它,该日志将错误列为源自 .NET rabbitmq 客户端,问题是“心跳”。【参考方案2】:

设法从 RabbitMQ 用户组获得(部分)解决方案。

详情请看:Rabbit MQ Users Group

基本思想是应该共享 IConnection 对象,因为打开和关闭它很重。只有 IModel 应该为每个线程重新打开

【讨论】:

以上是关于RabbitMQ C# 驱动程序导致 System.AccessViolationException的主要内容,如果未能解决你的问题,请参考以下文章

无法连接到在 C# 程序中使用 Docker 启动的 RabbitMQ(使用 RabbitMQ.Client)

将带有参数的 c# 回调方法传递给 c++ dll 会导致 System.ExecutionEngineException

[c#]RabbitMQ的简单使用

搭建高可用的Rabbitmq集群 + Mirror Queue + 使用C#驱动连接

搭建高可用的rabbitmq集群 + Mirror Queue + 使用C#驱动连接

RabbitMQ指南(C#)工作队列