servicestack Servicestack RabbitMQ:当RabbitMqProducer无法在RPC模式中重新声明临时队列时,无限循环将填充死信队列,servicestack,rabbitmq,servicestack,Rabbitmq" /> servicestack Servicestack RabbitMQ:当RabbitMqProducer无法在RPC模式中重新声明临时队列时,无限循环将填充死信队列,servicestack,rabbitmq,servicestack,Rabbitmq" />

servicestack Servicestack RabbitMQ:当RabbitMqProducer无法在RPC模式中重新声明临时队列时,无限循环将填充死信队列

servicestack Servicestack RabbitMQ:当RabbitMqProducer无法在RPC模式中重新声明临时队列时,无限循环将填充死信队列,servicestack,rabbitmq,servicestack,Rabbitmq,当我将临时回复队列声明为独占(例如rpc模式中的匿名队列(exclusive=true,autodelete=true))时,响应消息无法发布到指定的回复队列(例如message.replyTo=“amq.gen-Jg_tv8QYxtEQhq0tF30vAA”),因为RabbitMqProducer.PublishMessage()尝试使用不同的参数(exclusive=false)重新声明队列,这会导致错误,这是可以理解的 不幸的是,RabbitMqProducer.PublishMessag

当我将临时回复队列声明为独占(例如rpc模式中的匿名队列(exclusive=true,autodelete=true))时,响应消息无法发布到指定的回复队列(例如message.replyTo=“amq.gen-Jg_tv8QYxtEQhq0tF30vAA”),因为RabbitMqProducer.PublishMessage()尝试使用不同的参数(exclusive=false)重新声明队列,这会导致错误,这是可以理解的

不幸的是,RabbitMqProducer.PublishMessage()中对channel.RegisterQueue(queueName)的错误调用似乎在传入队列中拒绝了请求消息,因此,当ServiceStack.Messaging.MessageHandler.DefaultInceptionHandler尝试确认请求消息(将其从传入队列中删除)时,消息只是停留在传入队列的顶部,并被重新处理。此过程无限期重复,每次迭代产生一条dlq消息,该消息将慢慢填满dlq

我在想

  • 如果ServiceStack处理此情况,则当ServiceStack.RabbitMq.RabbitMqProducer无法正确声明响应队列时
  • 如果ServiceStack.RabbitMq.RabbitMqProducer muss始终在发布响应之前声明响应队列
  • 如果不是最好使用一些配置标志来省略所有exchange和队列声明调用(在第一次初始化之外)。RabbitMqProducer只会假设每个队列/交换都已正确设置,并只发布消息
(目前,我们的客户端只是将其响应队列声明为exclusive=false,一切正常。但我确实希望使用rabbitmq的内置临时队列。)

MQ客户端代码,需要简单的“SayHello”服务:

当RabbitMqProducer不尝试声明队列时,一切似乎都正常工作,如下所示:

    public void PublishMessage(string exchange, string routingKey, IBasicProperties basicProperties, byte[] body)
    {
        const bool MustDeclareQueue = false; // new config parameter??
        try
        {
            if (MustDeclareQueue && !Queues.Contains(routingKey))
            {
                Channel.RegisterQueueByName(routingKey);
                Queues = new HashSet<string>(Queues) { routingKey };
            }

            Channel.BasicPublish(exchange, routingKey, basicProperties, body);
        }
        catch (OperationInterruptedException ex)
        {
            if (ex.Is404())
            {
                Channel.RegisterExchangeByName(exchange);

                Channel.BasicPublish(exchange, routingKey, basicProperties, body);
            }
            throw;
        }
    }
using (var mqClient = new MqClient("mx.servicestack"))
{
    var pingResponse = mqClient.RpcCall<PingResponse>(new Ping { });
}
public void PublishMessage(字符串交换、字符串路由键、IBasicProperties基本属性、字节[]正文)
{
const bool MustDeclareQueue=false;//新配置参数??
尝试
{
if(MustDeclareQueue&!Queues.Contains(routingKey))
{
Channel.RegisterQueueByName(routingKey);
队列=新哈希集(队列){routingKey};
}
Channel.BasicPublish(交换、路由键、基本属性、正文);
}
捕捉(操作中断异常例外)
{
如果(例如Is404())
{
Channel.RegisterExchangeByName(交换);
Channel.BasicPublish(交换、路由键、基本属性、正文);
}
投掷;
}
}

这个问题在servicestack的v4.0.32版本中得到了解决(在此版本中已修复)

RabbitMqProducer不再尝试重新声明临时队列,而是假设应答队列已经存在(这解决了我的问题)

(无限循环(发布响应消息时错误处理)的根本原因可能仍然存在。)

编辑:示例

以下基本mq客户端(不使用ServiceStackmq客户端,而是直接依赖于rabbitmq的.net库;但它使用ServiceStack.Text进行序列化)可以执行通用RPC:

    public class MqClient : IDisposable
    {
        ConnectionFactory factory = new ConnectionFactory()
        {
            HostName = "192.168.97.201",
            UserName = "guest",
            Password = "guest",
            //VirtualHost = "test",
            Port = AmqpTcpEndpoint.UseDefaultPort,
        };

        private IConnection connection;
        private string exchangeName;

        public MqClient(string defaultExchange)
        {
            this.exchangeName = defaultExchange;
            this.connection = factory.CreateConnection();
        }

        public TResponse RpcCall<TResponse>(IReturn<TResponse> reqDto, string exchange = null)
        {
            using (var channel = connection.CreateModel())
            {
                string inq_queue_name = string.Format("mq:{0}.inq", reqDto.GetType().Name);

                string responseQueueName = channel.QueueDeclare().QueueName;

                var props = channel.CreateBasicProperties();
                props.ReplyTo = responseQueueName;

                var message = ServiceStack.Text.JsonSerializer.SerializeToString(reqDto);

                channel.BasicPublish(exchange ?? this.exchangeName, inq_queue_name, props, UTF8Encoding.UTF8.GetBytes(message));

                var consumer = new QueueingBasicConsumer(channel);
                channel.BasicConsume(responseQueueName, true, consumer);


                var ea = (BasicDeliverEventArgs)consumer.Queue.Dequeue();
                //channel.BasicAck(ea.DeliveryTag, false);

                string response = UTF8Encoding.UTF8.GetString(ea.Body);
                string responseType = ea.BasicProperties.Type;
                Console.WriteLine(" [x] New Message of Type '{1}' Received:{2}{0}", response, responseType, Environment.NewLine);

                return ServiceStack.Text.JsonSerializer.DeserializeFromString<TResponse>(response);

            }
        }

        ~MqClient()
        {
            this.Dispose();
        }

        public void Dispose()
        {
            if (connection != null)
            {
                this.connection.Dispose();
                this.connection = null;
            }
        }

    }

重要信息:您必须使用servicestack 4.0.32+版。

它看起来像是特定的应用程序逻辑问题。声明操作对于队列和交换都是幂等的,所以您遇到的问题是试图用不同的标志或参数重新声明队列。谢谢,但我已经理解了这一点。(我编辑了我的问题以使其更清楚。)我的观点是,在我看来,servicestack的messageproducer似乎能够处理无法很好地声明指定响应队列的情况(=>无限循环填充dlq)。那么您能够让RPC以可靠的多线程方式工作了吗?介意发布您的SayHello服务吗?在我的答案中添加了示例代码。请注意,此代码不在生产中。(我们从java Web服务执行rpc调用。)
using (var mqClient = new MqClient("mx.servicestack"))
{
    var pingResponse = mqClient.RpcCall<PingResponse>(new Ping { });
}