C# 从多个队列读取,RabbitMQ

C# 从多个队列读取,RabbitMQ,c#,.net,rabbitmq,amqp,C#,.net,Rabbitmq,Amqp,我是拉比犬的新手。当有多个队列(从中读取)时,我希望能够在不阻塞的情况下处理读取消息。有关于我如何做到这一点的信息吗 //编辑1 public class Rabbit : IMessageBus { private List<string> publishQ = new List<string>(); private List<string> subscribeQ = new List<string>(); Co

我是拉比犬的新手。当有多个队列(从中读取)时,我希望能够在不阻塞的情况下处理读取消息。有关于我如何做到这一点的信息吗

//编辑1

public class Rabbit : IMessageBus
{   

    private List<string> publishQ = new List<string>();
    private List<string> subscribeQ = new List<string>();

    ConnectionFactory factory = null;
    IConnection connection = null;
    IModel channel = null;  
    Subscription sub = null;

    public void writeMessage( Measurement m1 ) {
        byte[] body = Measurement.AltSerialize( m1 );
        int msgCount = 1;
        Console.WriteLine("Sending message to queue {1} via the amq.direct exchange.", m1.id);

        string finalQueue = publishToQueue( m1.id );

        while (msgCount --> 0) {
            channel.BasicPublish("amq.direct", finalQueue, null, body);
        }

        Console.WriteLine("Done. Wrote the message to queue {0}.\n", m1.id);
    }

     public string publishToQueue(string firstQueueName) {
        Console.WriteLine("Creating a queue and binding it to amq.direct");
        string queueName = channel.QueueDeclare(firstQueueName, true, false, false, null);
        channel.QueueBind(queueName, "amq.direct", queueName, null);
        Console.WriteLine("Done.  Created queue {0} and bound it to amq.direct.\n", queueName);
        return queueName;
    }


    public Measurement readMessage() {
        Console.WriteLine("Receiving message...");
        Measurement m = new Measurement();

        int i = 0;
        foreach (BasicDeliverEventArgs ev in sub) {
            m = Measurement.AltDeSerialize(ev.Body);
            //m.id = //get the id here, from sub
            if (++i == 1)
                break;
            sub.Ack();
        }

        Console.WriteLine("Done.\n");
        return m;
    }


    public void subscribeToQueue(string queueName ) 
    {
        sub = new Subscription(channel, queueName);
    }

    public static string MsgSysName;
    public string MsgSys
    {
        get 
        { 
            return MsgSysName;
        }
        set
        {
            MsgSysName = value;
        }
    }

    public Rabbit(string _msgSys) //Constructor
    {   
        factory = new ConnectionFactory();
        factory.HostName = "localhost"; 
        connection = factory.CreateConnection();
        channel = connection.CreateModel();
        //consumer = new QueueingBasicConsumer(channel);

        System.Console.WriteLine("\nMsgSys: RabbitMQ");
        MsgSys = _msgSys;
    }

    ~Rabbit()
    {
        //observer??
        connection.Dispose();
        //channel.Dispose();
        System.Console.WriteLine("\nDestroying RABBIT");
    }   
}
//附录2.cs

using System;
using System.IO;

using UtilityMeasurement;
using UtilityMessageBus;

public class MainClass
{
    public static void Main()
    {
    //Asks for the message system
    System.Console.WriteLine("\nEnter name of messageing system: ");
    System.Console.WriteLine("Usage: [Rabbit] [Zmq]");
    string MsgSysName = (System.Console.ReadLine()).ToString();

    //Declare an IMessageBus instance:
    //Here, an object of the corresponding Message System
        // (ex. Rabbit, Zmq, etc) is instantiated
    IMessageBus obj1 = MessageBusFactory.GetMessageBus(MsgSysName);

    System.Console.WriteLine("\nA {0} object is now created.", MsgSysName);

    //Create a new Measurement object m
    Measurement m = new Measurement();  

    System.Console.WriteLine("Queue name to subscribe to: ");
    string QueueName1 = (System.Console.ReadLine()).ToString();
    obj1.subscribeToQueue( QueueName1 );

    //Read message into m
    m = obj1.readMessage();

    if (m != null ) {
        System.Console.WriteLine("\nMessage received from queue {0}:\n    ID: {1}", m.id, m.id);
        System.Console.WriteLine("    Time: {0}", m.time);
        System.Console.WriteLine("    Value: {0}", m.value);
    }

    System.Console.WriteLine("Another queue name to subscribe to: ");
    string QueueName2 = (System.Console.ReadLine()).ToString();
    obj1.subscribeToQueue( QueueName2 );

    m = obj1.readMessage();

    if (m != null ) {
        System.Console.WriteLine("\nMessage received from queue {0}:\n    ID: {1}", m.id, m.id);
        System.Console.WriteLine("    Time: {0}", m.time);
        System.Console.WriteLine("    Value: {0}", m.value);
    }

    obj1.disposeAll();
}
}
两个信息来源:

  • 你应该首先尝试去理解这些例子

    • %程序文件%\RabbitMQ\DotNetClient\examples\src(基本示例)

    • 从Mercurial存储库(c#项目)获取完整的工作示例

  • 需要了解的有用操作:

    • 声明/断言/侦听/订阅/发布
    关于: 你的问题——没有理由不能有多个听众。或者,您可以在“exchange”上使用一个Listener订阅n个路由路径

    **回复:非阻塞**

    典型的Listener一次只使用一条消息。您可以将它们从队列中拉出,或者它们将以“窗口化”方式(通过服务质量qos参数定义)自动放置在消费者附近。该方法的优点在于为您做了大量的艰苦工作(例如:可靠性、保证交付等)

    RabbitMQ的一个关键特性是,如果处理过程中出现错误,则消息会重新添加回队列(容错特性)

    需要更多地了解你的情况

    通常,如果您发布到我上面提到的列表,您可以找到RabbitMQ的员工。他们很有帮助

    希望对你有点帮助。一开始,你的头脑很清醒,但值得坚持


    问答

    见:

    问:您能否使用新订阅(频道、队列名称)订阅多个队列

    对。可以使用绑定键,例如abc.*.hij或abc.#.hij,也可以附加多个绑定。前者假设您已经围绕某种对您有意义的原则设计了路由密钥(请参阅常见问题解答中的路由密钥)。对于后者,您需要绑定到多个队列

    手动实现n绑定。 见:

    这个模式背后没有太多代码,所以如果通配符不够,您可以使用自己的订阅模式。您可以从此类继承并为其他绑定添加另一个方法。。。这可能会起作用,或者类似的(未经测试)

    AQMP规范规定可以进行多种手动绑定:

    如果是这样,我如何遍历所有订阅的队列并返回消息(如果没有消息,则返回null)

    当有消息可用时,订阅者会通知您。否则,您所描述的是一个拉接口,在该接口中,您可以根据请求向下拉消息。如果没有可用的消息,您将得到一个空值。顺便说一句:Notify方法可能更方便

    哦,请注意,所有这些操作都有不同的方法。我将编辑我的帖子以反映代码

    实时代码:

    此版本必须使用通配符订阅多个路由密钥

    n使用订阅的手动路由密钥留给读者作为练习。;-)我想你还是倾向于拉界面。顺便说一句:拉接口的效率低于通知接口

            using (Subscription sub = new Subscription(ch, QueueNme))
            {
                foreach (BasicDeliverEventArgs ev in sub)
                {
                    Process(ev.Body);
    
            ...
    
    注意:foreach使用IEnumerable,IEnumerable包装通过“yield”语句到达新消息的事件。实际上,这是一个无限循环

    ---更新

    AMQP的设计理念是将TCP连接数保持在与应用程序数相同的低水平,这意味着每个连接可以有多个通道

    这个问题中的代码(编辑3)尝试使用两个订阅服务器和一个通道,而它应该(我相信)是每个线程每个通道一个订阅服务器,以避免锁定问题。建议:使用路由键“通配符”。可以使用java客户端订阅多个不同的队列名称,但据我所知,.net客户端没有在Subscriber helper类中实现这一点

    如果在同一订阅线程上确实需要两个不同的队列名称,则建议对.net使用以下拉取序列:

            using (IModel ch = conn.CreateModel()) {    // btw: no reason to close the channel afterwards IMO
                conn.AutoClose = true;                  // no reason to closs the connection either.  Here for completeness.
    
                ch.QueueDeclare(queueName);
                BasicGetResult result = ch.BasicGet(queueName, false);
                if (result == null) {
                    Console.WriteLine("No message available.");
                } else {
                    ch.BasicAck(result.DeliveryTag, false);
                    Console.WriteLine("Message:");
                }
    
                return 0;
            }
    
    --更新2:

    从RabbitMQ列表:

    “假设元素.Next()阻塞了其中一个订阅。 您可以从每个订阅中检索传递,超时时间为 读过去。或者你可以设置一个队列来接收 所有的测量和检索信息,从它与一个单一的订阅。”(埃米尔)

    这意味着,当第一个队列为空时,.Next()阻塞等待下一条消息出现。i、 e.订户内置了等待下一条消息

    --更新3:

    在.net下,使用QueueingBasicConsumer从多个队列进行消费

    实际上,这里有一条关于它的线索,可以让你了解它的用法:

    --更新4:

    有关.QueueingBasicConsumer的更多信息

    这里有一个示例代码


    示例通过一些修改复制到答案中(请参见//最简单的方法是使用EventingBasicConsumer。我的网站上有一个关于如何使用它的示例


    此使用者类公开您可以使用的已接收事件,因此不会阻止。其余代码基本保持不变。

    非常感谢您的反馈。我仍在学习消息传递系统,有些操作我仍然不理解。例如,听。我还看到了rabbitmq如何订阅队列。您可以订阅吗使用新订阅(通道、队列名称)发送到多个队列?如果是,如何遍历所有订阅的队列并返回消息(如果没有消息,则返回null)?哦,请注意,我用不同的方法进行了所有这些操作。我将编辑我的帖子以反映代码。再次感谢。我编辑了上面订阅和编写函数的代码。但是,我有这个ru
    using System;
    using System.IO;
    
    using UtilityMeasurement;
    using UtilityMessageBus;
    
    public class MainClass
    {
        public static void Main()
        {
        //Asks for the message system
        System.Console.WriteLine("\nEnter name of messageing system: ");
        System.Console.WriteLine("Usage: [Rabbit] [Zmq]");
        string MsgSysName = (System.Console.ReadLine()).ToString();
    
        //Declare an IMessageBus instance:
        //Here, an object of the corresponding Message System
            // (ex. Rabbit, Zmq, etc) is instantiated
        IMessageBus obj1 = MessageBusFactory.GetMessageBus(MsgSysName);
    
        System.Console.WriteLine("\nA {0} object is now created.", MsgSysName);
    
        //Create a new Measurement object m
        Measurement m = new Measurement();  
    
        System.Console.WriteLine("Queue name to subscribe to: ");
        string QueueName1 = (System.Console.ReadLine()).ToString();
        obj1.subscribeToQueue( QueueName1 );
    
        //Read message into m
        m = obj1.readMessage();
    
        if (m != null ) {
            System.Console.WriteLine("\nMessage received from queue {0}:\n    ID: {1}", m.id, m.id);
            System.Console.WriteLine("    Time: {0}", m.time);
            System.Console.WriteLine("    Value: {0}", m.value);
        }
    
        System.Console.WriteLine("Another queue name to subscribe to: ");
        string QueueName2 = (System.Console.ReadLine()).ToString();
        obj1.subscribeToQueue( QueueName2 );
    
        m = obj1.readMessage();
    
        if (m != null ) {
            System.Console.WriteLine("\nMessage received from queue {0}:\n    ID: {1}", m.id, m.id);
            System.Console.WriteLine("    Time: {0}", m.time);
            System.Console.WriteLine("    Value: {0}", m.value);
        }
    
        obj1.disposeAll();
    }
    }
    
            using (Subscription sub = new Subscription(ch, QueueNme))
            {
                foreach (BasicDeliverEventArgs ev in sub)
                {
                    Process(ev.Body);
    
            ...
    
            using (IModel ch = conn.CreateModel()) {    // btw: no reason to close the channel afterwards IMO
                conn.AutoClose = true;                  // no reason to closs the connection either.  Here for completeness.
    
                ch.QueueDeclare(queueName);
                BasicGetResult result = ch.BasicGet(queueName, false);
                if (result == null) {
                    Console.WriteLine("No message available.");
                } else {
                    ch.BasicAck(result.DeliveryTag, false);
                    Console.WriteLine("Message:");
                }
    
                return 0;
            }
    
                    IModel channel = ...;
                QueueingBasicConsumer consumer = new QueueingBasicConsumer(channel);
                channel.BasicConsume(queueName, false, null, consumer);  //<-----
                channel.BasicConsume(queueName2, false, null, consumer); //<-----
                // etc. channel.BasicConsume(queueNameN, false, null, consumer);  //<-----
    
                // At this point, messages will be being asynchronously delivered,
                // and will be queueing up in consumer.Queue.
    
                while (true) {
                    try {
                        BasicDeliverEventArgs e = (BasicDeliverEventArgs) consumer.Queue.Dequeue();
                        // ... handle the delivery ...
                        channel.BasicAck(e.DeliveryTag, false);
                    } catch (EndOfStreamException ex) {
                        // The consumer was cancelled, the model closed, or the
                        // connection went away.
                        break;
                    }
                }
    
                ch.QueueDeclare(queueName);
                BasicGetResult result = ch.BasicGet(queueName, false);
                if (result == null) {
                    Console.WriteLine("No message available.");
                } else {
                    ch.BasicAck(result.DeliveryTag, false);
                    Console.WriteLine("Message:"); 
                    // deserialize body and display extra info here.
                }