RabbitMQ:使用Routing实现消息选择

时间:2016-09-16 09:06:13

标签: c# queue rabbitmq

我尝试使用RabbitMQ实现一个解决方案来实现类似分布式RPC的解决方案,只使用一个请求和一个响应队列来处理大量处理器,我已经用Apache Apollo实现了这样的解决方案我本来希望能够将它迁移到RabbitMQ。以下是要点:

  • 每个服务器都连接到请求队列
  • 每个服务器只处理应该为他的请求(标​​题字段)

在我对Apollo的实现中,关键点是使用选择器(比如头字段值的where子句),我认为这是通过路由和路由键在RabbitMQ中实现的,但我一定是错的,因为我看到工作人员接收不应该是他们的消息。

我修改了路由示例(http://www.rabbitmq.com/tutorials/tutorial-four-dotnet.html)以便复制问题,我有两个消费者,我可以从定义routingKey的不同参数开始,以及为其中一个消费者生成消息的生产者。我看到的行为是消息的消费似乎是随机的(约翰'消息由消费者处理,约翰'第一次和消费者为'玛丽& #39;第二次)

有没有人在RabbitMQ中使用选择器有任何指示或代码片段?

在我的消费者代码下面:

public static void Main( String[] args )
{
    var factory = new ConnectionFactory { HostName = "localhost" };
    using ( var connection = factory.CreateConnection() )
        using ( var channel = connection.CreateModel() )
        {
            const String request = "request";
            channel.ExchangeDeclare( request, "direct" );

            channel.QueueDeclare( request, true, false, false, null );

            if ( args.Length < 1 )
            {
                Console.WriteLine( " Press [enter] to exit." );
                Console.ReadLine();
                Environment.ExitCode = 1;
                return;
            }

            var myRoutingKey = args[0];
            channel.QueueBind( request, request, myRoutingKey );

            Console.WriteLine( $" [*] Waiting for messages for {myRoutingKey}." );

            var consumer = new EventingBasicConsumer( channel );
            consumer.Received += ( model, ea ) =>
            {
                var body = ea.Body;
                var message = Encoding.UTF8.GetString( body );
                var routingKey = ea.RoutingKey;
                Console.WriteLine( $" [x] Received '{routingKey}':'{message}'" );
            };
            channel.BasicConsume( request, true, consumer );

            Console.WriteLine( " Press [enter] to exit." );
            Console.ReadLine();
        }
}

和制片人:

public static void Main( String[] args )
{
    var factory = new ConnectionFactory { HostName = "localhost" };
    using ( var connection = factory.CreateConnection() )
        using ( var channel = connection.CreateModel() )
        {
            const String request = "request";
            channel.ExchangeDeclare( request, "direct" );

            channel.QueueDeclare( request, true, false, false, null );

            var routingKey = args.Length > 0 ? args[0] : "John";

            const String message = "Hi";
            var body = Encoding.UTF8.GetBytes( message );
            channel.BasicPublish( request, routingKey, null, body );
            Console.WriteLine( $" [x] Sent '{routingKey}':'{message}'" );
        }

    Console.WriteLine( " Press [enter] to exit." );
    Console.ReadLine();
}

提前致谢。

1 个答案:

答案 0 :(得分:0)

我可以猜到为什么这对你不起作用。关键是消费者中的这两行。

channel.QueueDeclare( request, true, false, false, null );
channel.QueueBind( request, request, myRoutingKey );

&#34;要求&#34;是&#34; all&#34;的队列名称你的消费者如果您运行此程序使用多个路由键设置多个绑定,最终结果是名为&#34; request&#34;与多个路由密钥(例如&#34; John&#34;,&#34; Mary&#34;)进行交换是必然的。请记住,当您执行此绑定时,绑定在RabbitMQ服务器中不是瞬态的,并且它们会一直存在。

现在回到如何解决您的问题。有多种选择,但这里有一种。首先,我建议您阅读RabbitMQ Model

您可以使用相同的教程代码而不是您的代码:

 var queueName = channel.QueueDeclare().QueueName;
 channel.QueueBind( queueName, request, myRoutingKey );

但上述意味着每次运行您的消费者计划时,都会创建一个新队列,并使用您所需的路由键绑定到交换机。另一种方法是使用您之前使用的相同代码,但只需选择适当的队列名称而不是固定队列名称。例如,每个路由密钥可以有一个队列

var queueName = myRoutingKey ;
channel.QueueDeclare( queueName, true, false, false, null );
channel.QueueBind( queueName, request, myRoutingKey );

或者,您可以将多个路由键分组到一个类似于教程示例的单个队列中。

关键是您需要做的事情不能用一个队列完成。 (除了在使用消息时过滤掉消息)。但这听起来并不像是对你的真正要求。你问的是每个消费者服务器只处理你可以在这个模型中做的相关消息。生产者只发布到那个交易所(这是你想要的)。