代码改变世界

RabbitMQ基础学习笔记(C#代码示例)

2017-07-01 22:15  sunice  阅读(402)  评论(0编辑  收藏  举报

一、定义:

    MQ是MessageQueue,消息队列的简称(是流行的开源消息队列系统,利用erlang语言开发)。MQ是一种应用程序对应用程序的通信方法。应用程序通过读写入队和出队的消息来通信,无需专用连接来链接它们。

    消息传递是程序之间通过在消息中发送数据进行通信,而不是通过直接调用彼此来通信,一般应用于远程过程调用的技术。

    排队指的是应用程序通过队列来通信。应用队列避免接收和发送数据的同时进行。

二、特点:

    MQ是消费者-生产者模型的代表。一端往消息队列中写入消息,另一端可以读取或者订阅队列中的消息。MQ遵循的是AMQP协议(高级消息队列协议:使得遵从该规范的客户端应用和消息中间件服务器的全功能互操作成为可能)的具体实现和产品。

三、应用:

    在使用MQ时,我们不需要实时的返回信息。获取信息和返回信息进行异步处理。例如:在项目中,我们需要从汽车系统中利用CAN总线实时的获取汽车的相关信息,但是没有必要给汽车返回信息。如,获取汽车的轮胎气压,但是我们不需要给汽车一个返回的信息或结果。

    C#项目要利用RabbitMQ来获取实时数据的话,需要先安装客户端的库文件:RabbitMQ.Client.dll,下载地址如下:

http://download.csdn.net/detail/qq_30507287/9599941

四、RabbitMQ的结构图:

 

五、基本概念:

    Broker:消息队列服务器实体。

    Exchange:消息交换机,指定消息按照什么规则,路由到哪个队列。可以理解成具有路由表的程序。每个消息都有一个成为路由键(routing key)的属性。交换机中有一系列的绑定(binding)即路由规则。

    Queue:消息队列载体,每个消息都会被投入到一个或多个队列。消息一直在里面,直到有客户端(消费者)连接到这个队列并且将其取走为止。队列是有消费者通过程序建立的。

    Binding:绑定,作用:把exchange和queue按照路由规则绑定起来。

    Routing Key:路由关键字,exchange根据这个关键字进行消息投递。

    Vhost(virtualhost):虚拟主机,一个broker里可以开设多个vhost,用作不同用户的权限分离。

    producer:消息生产者,就是投递消息的程序。

    consumer:消息消费者,就是接受消息的程序。

    channel:消息通道,在客户端的每个连接里,可建立多个channel,每个channel代表一个会话任务。

    注1:比较重要的四个:vhost,exchange,queue,binding。一个虚拟主机持有一组交换机、队列和绑定。

    注2:消费者程序要负责创建交换机们(不止一个)?因为每个交换机在自己独立的进程当中执行,增加多个交换机就是增加多个进程,可以充分利用服务器上的CPU的核来提高效率。(一个8核服务上,可以用5核来创建5个交换机,剩余的3个用来处理消息。)

    注3:一个绑定(binding)就是一个基于路由键将交换机和队列连接起来的路由规则。

六、消息队列的使用过程大概如下:

    (1)客户端连接到消息队列服务器,打开一个channel。

    (2)客户端声明一个exchange,并设置相关属性。

    (3)客户端声明一个queue,并设置相关属性。

    (4)客户端使用routingkey,在exchange和queue之间建立好绑定关系。

    (5)客户端投递消息到exchange。

    说明:exchange接收到消息后,就根据消息的key和已经设置的binding,进行消息路由,将消息投递到一个或多个队列里。

七、Exchange(交换机的类型)的类型:

1)Direct交换机:

    (处理路由键)完全根据key进行投递。需要将一个队列绑定到交换机上,要求该消息与一个特定的路由键完全匹配。

    绑定时设置了routing key为“abc”,那么客户端提交的消息,只有设置了key为“abc”的才会投递到队列。

2)Topic交换机:

    (将路由键和某模式进行匹配)此时队列需要绑定到一个模式上,对key进行模式匹配后进行投递。

    “#”匹配一个或多个词,“*”匹配正好一个词。“abc.#”匹配“abc.def.ghi”,“abc.*”只匹配“abc.def”。

3)Fanout交换机:(不处理路由键)不需要key值,采用广播模式,消息进来时,投递到与该交换机绑定的所有队列。

八、队列消息的持久化:

1、为什么会有持久化?

    花费大量时间来创建队列,交换机和绑定,如果服务器出现意外或外界故障,那么队列,交换机和绑定就会清空。RabbitMQ重启之后就会清空原来的东西。因此在创建队列和交换机时就会指定一个标志durable来控制。当然,durable表示的含义:含有该标志的队列和交换机在重启之后会重新建立,而不是,在队列中的消息会重启后恢复。

2、消息队列持久化包括3个部分:

    (1)exchange持久化,在声明时指定durable => 1;

    (2)queue持久化,在声明时指定durable => 1;

    (3)消息持久化,在投递时指定delivery_mode=> 2(1是非持久化,2表示persistent,持久化);

    如果exchange和queue都是持久化的,那么它们之间的binding也是持久化的。如果exchange和queue两者之间有一个持久化,一个非持久化,就不允许建立绑定。

    说明:delivery mode(投递模式)

3、绑定(binding)的持久化:

    绑定在创建的时候无法设置durable,那么绑定的持久化是靠队列和交换机来实现的。如果绑定一个durable的队列和一个durable的交换机,RabbitMQ会自动保留这个绑定。只要队列和交换机之一不是durable,那么依赖它们的绑定就会自动删除。

九、.NET/C#客户端中的RabbitMQ

    (1)主要的命名空间,接口和类

    核心的API接口和类都定义在命名空间为RabbitMQ.Client中。

    using RabbitMQ.Client;

    核心的接口和类是:

    IModel: representsan AMQP 0-9-1 channel, and provides most of the operations (protocol methods).代表通道,提供协议方法。

    IConnection:represents an AMQP 0-9-1 connection

    ConnectionFactory:constructs IConnection instances

    IBasicConsumer:represents a message consumer表示消息的消费者。

    其他的接口和类:

    DefaultBasicConsumer:commonly used base class for consumers

    其他公共的RabbitMQ.Client命名空间还包括:

    RabbitMQ.Client.Events:various events and event handlers that are part of the client library,including EventingBasicConsumer, a consumer implementation built around C#event handlers.

    RabbitMQ.Client.Exceptions:exceptions visible to the user.

(2)连接代理

        连接到RabbitMQ,必须要实例化一个ConnectionFactory和configure一个RabbitMQ到主机,虚拟机或认证的设备上。下面的代码是在主机上连接RabbitMQ结点。

        ConnectionFactory factory = newConnectionFactory();

       factory.Uri = "amqp://user:pass@hostName:port/vhost";

        IConnection conn = factory.CreateConnection();

        然后用iconnection接口可以用来打开一个通道:

        IModel channel = conn.CreateModel();

(3)使用交换机和队列

       model.ExchangeDeclare(exchangeName,ExchangeType.Direct);

       model.QueueDeclare(queueName,false,false,false,null);

       model.QueueBind(queueName, exchangeName, routingKey,null);

(4)发布消息

        利用交换机(exchange)发送消息,使用的是IModel.BasicPublish

        byte[]messageBodyBytes = System.Text.Encoding.UTF8.GetBytes("Hello, world!");

       model.BasicPublish(exchangeName, routingKey,null,messageBodyBytes);

        对于细节,可以用重载变量来指定标志或特殊的消息属性:

        byte[]messageBodyBytes = System.Text.Encoding.UTF8.GetBytes("Hello, world!");

        IBasicProperties props = model.CreateBasicProperties();

       props.ContentType = "text/plain";

       props.DeliveryMode = 2;

       model.BasicPublish(exchangeName,routingKey, props, messageBodyBytes);

        发送自定义的消息:

        byte[]messageBodyBytes = System.Text.Encoding.UTF8.GetBytes("Hello, world!");

 

        IBasicProperties props = model.CreateBasicProperties();

       props.ContentType = "text/plain";

       props.DeliveryMode = 2;

       props.Headers = new Dictionary<string,object>();

       props.Headers.Add("latitude",  51.5252949);

       props.Headers.Add("longitude",-0.0905493);

       model.BasicPublish(exchangeName, routingKey, props, messageBodyBytes);

 

十、(C#实例解析)生产者应用程序解析

(1)从生产者应用程序来看,建立一个使用默认设置的连接,创建连接并创建一个通道:

namespaceProducer
    {
        classProgram
        {
            staticvoidMain(string[] args)
            {
               var connectionFactory =newConnectionFactory();
               IConnection connection =connectionFactory.CreateConnection();
               IModel channel = connection.CreateModel();
            }
        }
    }

  

(2)声明一个交换机并发布消息

    channel.ExchangeDeclare("direct-exchange-example",ExchangeType.Direct);

    第二个参数表名了参数的类型,可以选择direct,fanout,topic,或者headers。

(3)调用方法,产生一个返回值,本例中调用的是DoSomethingInteresting(),并且返回一个字符串的值。

   string value = DoSomethingInteresting();

(4)dosomethinginteresting()实施可以返回一个新的GUID字符串值

    staticstringDoSomethingInteresting()

    {

        returnGuid.NewGuid().ToString();

    }

(5)利用返回值来创建一个日志消息:

    string logMessage =string.Format("{0}: {1}", TraceEventType.Information, value);

(6)将日志消息转换为字节数组,并将消息发布到新的交换机上:

    byte[]message =Encoding.UTF8.GetBytes(logMessage);

    channel.BasicPublish("direct-exchange-example","",null,message);

(7)最后,要关闭通道和连接

    channel.Close();

    connection.Close();

十一、(C#实例解析)消费者应用程序解析

(1)和生成者一样创建消费者

using RabbitMQ.Client;
 
namespace Consumer
{
    classProgram
    {
        staticvoidMain(string[] args)
        {
            varconnectionFactory = new ConnectionFactory();
            IConnection connection = connectionFactory.CreateConnection();
            IModel channel = connection.CreateModel();
 
           channel.ExchangeDeclare("direct-exchange-example",ExchangeType.Direct);
        }
    }
}

  

(2)声明一个队列去绑定交换机,队列的名字为“logs”

    channel.QueueDeclare("logs",false,false,true,null);

(3)绑定“logs”队列,利用QueueBind()

    channel.QueueBind("logs","direct-exchange-example","");

(4)声明消费者对象

    var consumer =newQueueingBasicConsumer(channel)

(5)推送消息

    channel.BasicConsume(“logs”, true,consumer);

(6)任何消息都将被自动检索,并放置在内存队列的本地内存队列中。

    var eventArgs =(BasicDeliverEventArgs)consumer.Queue.Dequeue();

    将eventArgs转换成字符串并打印输出

    var message =Encoding.UTF8.GetString(eventArgs.Body);

    Console.WriteLine(message);

(7)关闭通道和连接

    channel.Close();

    connection.Close();

十二、(C#实例代码)生产-消费模式的完整代码

(1)

using System;
using System.Diagnostics;
using System.Text;
using System.Threading;
using RabbitMQ.Client;
 
namespace Producer
{
    classProgram
    {
        staticvoidMain(string[] args)
        {
            Thread.Sleep(1000);
            varconnectionFactory = new ConnectionFactory();
            IConnection connection = connectionFactory.CreateConnection();
            IModel channel = connection.CreateModel();
 
           channel.ExchangeDeclare("direct-exchange-example",ExchangeType.Direct);
            stringvalue = DoSomethingInteresting();
            stringlogMessage = string.Format("{0}:{1}",TraceEventType.Information,value);
 
            byte[]message = Encoding.UTF8.GetBytes(logMessage);
           channel.BasicPublish("direct-exchange-example","",null,message);
 
           channel.Close();
           connection.Close();
        }
 
        staticstringDoSomethingInteresting()
        {
            returnGuid.NewGuid().ToString();
        }
    }
}

  (2)

using System;
using System.Text;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
 
namespace Consumer
{
    classProgram
    {
        staticvoidMain(string[] args)
        {
            varconnectionFactory = new ConnectionFactory();
            IConnection connection = connectionFactory.CreateConnection();
            IModel channel = connection.CreateModel();
 
           channel.ExchangeDeclare("direct-exchange-example",ExchangeType.Direct);
           channel.QueueDeclare("logs",false,false,true,null);
           channel.QueueBind("logs","direct-exchange-example","");
 
            varconsumer = new QueueingBasicConsumer(channel);
           channel.BasicConsume("logs",true,consumer);
 
            vareventArgs = (BasicDeliverEventArgs)consumer.Queue.Dequeue();
 
            stringmessage = Encoding.UTF8.GetString(eventArgs.Body);
            Console.WriteLine(message);
 
           channel.Close();
           connection.Close();
            Console.ReadLine();
        }
    }
}

  转载:

    http://blog.csdn.net/qq_30507287/article/details/52176603