RabbitMQ

 

一、安装Erlang,设置系统环境变量

Erlang_HOME:安装路径

二、安装RabbitMQ

三、配置

1、使用RabbitMQ管理插件:"C:\Program Files\RabbitMQ Server\rabbitmq_server-3.6.5\sbin\rabbitmq-plugins.bat" enable rabbitmq_management

2、启动服务:net stop RabbitMQ && net start RabbitMQ
3、创建用户、密码、绑定角色
在C:\Program Files\RabbitMQ Server\rabbitmq_server-3.6.5\sbin>操作命令
查看用户及用户角色:
rabbitmqctl.bat list_users
新增一个用户:rabbitmqctl.bat add_user username password

 修改角色:rabbitmqctl.bat set_user_tags username administrator

除了上面的administrator 还有 monitoring、policymaker、management、自定义名称 ,对应上面介绍到的不同的角色

 4、修改密码:rabbitmqctl change_password userName newPassword

5、删除用户:rabbitmqctl.bat delete_user username

四、http://localhost:15672 客户端

http://www.cnblogs.com/ericli-ericli/p/5902270.html

 

 

生产者

using RabbitMQ.Client;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace client
{
    class Program
    {
        static void Main(string[] args)
        {
            new Thread(Write).Start();
            new Thread(Write).Start();
            new Thread(Write).Start();
        }
        /// <summary>
        /// 生产者
        /// </summary>
        public static void Write()
        {
            var factory = new ConnectionFactory() {
                HostName="192.168.56.2",UserName="mqt",Password="123456"
            };
            using (var connection = factory.CreateConnection())//创建一个connection通过socket和服务器链接(需要传入服务器IP、用户名、密码等)
            {
                using (var channel = connection.CreateModel())//创建channel
                {
                    channel.QueueDeclare(queue:"writeLog",durable:false,exclusive:false,autoDelete:false,arguments:null);//声明队列
                    for(int i = 0; i < 80; i++)
                    {
                        Thread.Sleep(2000);
                        string message = i.ToString();
                        var body = Encoding.UTF8.GetBytes(message);//消息内容字节数组,注意编码
                        channel.BasicPublish(exchange:"",routingKey: "writeLog",basicProperties:null,body:body);//推送一个消息
                        Console.WriteLine("Program Sent{0}",message);
                    }
                }
            }
        }
    }
}

 

消费者

using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace service
{
    class Program
    {
        /// <summary>
        /// 消费者
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {//消费者随时从队列里获取消息,所以一直监听
            var factory = new ConnectionFactory() {
                HostName="192.168.56.2",UserName="mqt",Password="123456",VirtualHost="/"
            };
            using (var connection = factory.CreateConnection())
            {
                using (var channel = connection.CreateModel())
                {
                    channel.QueueDeclare(queue: "writeLog",
                                     durable: false,
                                     exclusive: false,
                                     autoDelete: false,
                                     arguments: null);//声明队列
                    var consumer = new EventingBasicConsumer(channel);
                    consumer.Received += (model, ea) => {
                        var body = ea.Body;
                        var message = Encoding.UTF8.GetString(body);
                        ExcuteWriteFile(message);
                        Console.WriteLine("Receiver Received{0}",message);
                    };

                    channel.BasicConsume(queue: "writeLog",
                                     noAck: true,
                                     consumer: consumer);

                    Console.WriteLine(" Press [enter] to exit.");
                    Console.ReadLine();
                }
            }
            
        }
        public static void ExcuteWriteFile(string i)
        {
            string fileName = DateTime.Now.ToString("yyyyMMdd") + ".txt";
            string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "log\\");
            if (!Directory.Exists(path))
            {
                Directory.CreateDirectory(path);
            }
            using (FileStream fs=new FileStream(path+ fileName, FileMode.Append))
            {
                using (StreamWriter sw=new StreamWriter(fs, Encoding.Unicode))
                {
                    sw.Write(i);
                }
            }
        }
    }
}

 二、http://www.cnblogs.com/ericli-ericli/p/5924608.html

RabbitMQ是一个在AMQP(高级消息队列协议)标准基础上完整的,可服用的企业消息系统。

AMQP的四个总要概念:

1、虚拟主机(virtual host)或(vhost

2、交换机(exchange

3、队列(queue

4、绑定器(bind

什么是虚拟主机?一组交换机、队列和绑定器 被称为 虚拟主机(vhost)。

为什么要用虚拟主机?RabbitMQ server 可以说就是一个消息队列服务器实体(Broker),Broker当中可以有多个用户(增加用户的命令),而用户只能在虚拟主机的粒度进行权限控制,所以RabbitMQ中需要多个虚拟主机。每一个RabbitMQ服务器都有一个默认的虚拟主机“/”。

 

队列(queue)是消息载体,每个消息都会被投入到一个或多个队列。试图创建一个已经存在的队列,RabbitMQ会直接忽略这个请求。(接收消息的实体)

把消息放进队列前,我们还需要使用另一个东西:交换机。

 

交换机(exchange),它指定消息按什么规则,路由到哪个队列。它可以被理解成具有路由表的路由程序。(发送消息的实体)

交换机可以存在多个,每个交换机在自己独立的进程当中执行,因此增加多个交换机就是增加多个进程,可以充分利用服务器上的CPU核以便达到更高的效率。

交换机如何判断要把消息送到哪个队列?这是我们需要路由规则,也就需要绑定器了。

 

绑定器(bind)它的作用就是把exchange和queue按照路由规则绑定起来。(将交换器和队列连接起来,并且封装消息的路由信息)

 

每个消息都有一个称为路由关键字(routingKey)的属性,exchange根据这个关键字进行消息投递,其实就是一个简单的字符串。(绑定操作就可以理解成:将exchange将具有路由关键字 “X” 的消息投递到到名为“business”的队列当中去。)

从而一个绑定就可以概括为:一个基于路由键交换机队列连接起来的路由规则。

需要注意:由Exchange,Queue,RoutingKey三个,才能决定一个从Exchange到Queue的唯一的线路。

程序中连接与消息使用的两个关键概念

连接(Connection):与RabbitMQ Server建立的一个连接,由ConnectionFactory创建,每个connection只与一个物理的Server进行连接,此连接是基于Socket进行连接的。AMQP一般使用TCP。

通道 (Channel):消息通道(主要进行相关定义,发送消息,获取消息,事务处理等),在客户端的每个连接里,可建立多个channel,每个channel代表一个会话任务。

Channel在.net的客户端程序里应该是叫“Model”,采用IModel CreateModel()创建的,但是其他语言的客户端都叫Channel。

需要注意:一个Connection可以有多个Channel。

为什么设计中引入Channel概念?

一个比较普遍的需求:客户端程序有时候会是一个多线程程序,每一个线程都想要和RabbitMQ进行连接,但是又不想共享一个连接。

因为一个Connection就是一个TCP链接,RabbitMQ在设计的时候不希望与每一个客户端保持多个TCP连接,但这确实是有些客户端的需求,每一个Channel之间没有任何联系,是完全分离的。

建立在Connection基础上的一个Channel,相对于Connection来说,它是轻量级的。Channel可以在多线程中使用,但是在必须保证任何时候只有一个线程执行命令。

 

交换机定义参数type:  直接式交换机direct,topic 和 fanout。 
auto_delete: 当所有绑定队列都不再使用时,是否自动删除该交换机。

交换机类型: 

Fanout: 不处理路由键,将消息广播给绑定到该交换机的所有队列。 不论消息的路由关键字是什么,这条消息都会被路由到所有与该交换器绑定的队列中。

广播式交换器类型的工作方式如下:

不使用任何参数将消息队列与交换器绑定在一起。

发布者(直接式交换器类型描述中的producer变成了publisher,已经隐含了二种交换器类型的区别)向交换器发送一条消息。 消息被无条件的传递到所有和这个交换器绑定的消息队列中。
 

Direct: 处理路由键,对消息路径进行全文匹配。消息路由键 "sunshine" 只能匹配 "sunshine" 绑定,不匹配 "sunshine.warm" 这类绑定。 

通过精确匹配消息的路由关键字,将消息路由到零个或者多个队列中,绑定关键字用来将队列和交换器绑定到一起。这让我们可以构建经典的点对点队列消息传输模型,不过和任何已定义的交换器类型一样,当消息的路由关键字与多个绑定关键字匹配时,消息可能会被发送到多个队列中。

在direct模式下还可以实现多路绑定,即一个exchange和多个queue绑定时,具有同样的bindkey,如下图:

 

Topic: 处理路由键,按模式匹配路由键。模式符号 "#" 表示一个或多个单词,"*" 仅匹配一个单词。如 "wood.#" 可匹配 "wood.palm.redwood",但 "wood.*" 只匹配 "wood.deadwood"。 

主题式交换器类型提供了这样的路由机制:通过消息的路由关键字和绑定关键字的模式匹配,将消息路由到被绑定的队列中。这种路由器类型可以被用来支持经典的发布/订阅消息传输模型——使用主题名字空间作为消息寻址模式,将消息传递给那些部分或者全部匹配主题模式的多个消费者。

主题交换器类型的工作方式如下:

绑定关键字用零个或多个标记构成,每一个标记之间用“.”字符分隔。绑定关键字必须用这种形式明确说明,并支持通配符:“*”匹配一个词组,“#零个或多个词组
因此绑定关键字“*.dask.#”匹配路由关键字“class.dask”和“eur.dask.tab”,但是不匹配“dask.rho”。
这种交换器类型是可选的。

主题式的原理图解:

 

RabbitMQ消息持久化:

问题及方案描述

1.当有多个消费者同时收取消息,且每个消费者在接收消息的同时,还要处理其它的事情,且会消耗很长的时间。在此过程中可能会出现一些意外,比如消息接收到一半的时候,一个消费者死掉了。

这种情况要使用消息接收确认机制,可以执行上次宕机的消费者没有完成的事情。

2.在默认情况下,我们程序创建的消息队列以及存放在队列里面的消息,都是非持久化的。当RabbitMQ死掉了或者重启了,上次创建的队列、消息都不会保存。

这种情况可以使用RabbitMQ提供的消息队列的持久化机制。

相关理论描述

RabbitMQ支持消息的持久化,也就是数据写在磁盘上,为了数据安全考虑,我个人觉得大多数开发人员都会选择持久化。

队列和交换机有一个创建时候指定的标志durabledurable的唯一含义就是具有这个标志的队列和交换机会在重启之后重新建立,它不表示说在队列当中的消息会在重启后恢复。

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

1、exchange持久化,在声明时指定durable => true
2、queue持久化,在声明时指定durable => true
3、消息持久化,在投递时指定delivery_mode=> 2(1是非持久化)

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

注意:一旦创建了队列和交换机,就不能修改其标志了。例如,如果创建了一个non-durable的队列,然后想把它改变成durable的,唯一的办法就是删除这个队列然后重现创建

 

生产者

using RabbitMQ.Client;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace client
{
    class Producter
    {
        public void Test()
        {
            Producter.Write();
        }
        const string ExchangeName = "mqt.exchange";
        const string QueueName = "mqt.queue";
        static void Write()
        {
            var factory = new ConnectionFactory() {
                HostName = "192.168.56.2",
                UserName = "mqt",
                Password = "123456"
            };
            using (var connection = factory.CreateConnection())
            {
                using (var channel = connection.CreateModel())
                {
                    channel.ExchangeDeclare(ExchangeName, "direct", durable: true, autoDelete: false, arguments: null);//声明消息队列,且为可持久化的
                    channel.QueueDeclare(QueueName, durable: true, exclusive: false, autoDelete: false, arguments: null);//声明消息队列,且为可持久化的
                    channel.QueueBind(QueueName, ExchangeName, routingKey: QueueName);

                    string message = "Eric is very handsome";
                    var body = Encoding.UTF8.GetBytes(message);

                    //将队列设置为持久化之后,还需要将消息也设为可持久化的
                    var props = channel.CreateBasicProperties();
                    props.Persistent=true;

                    channel.BasicPublish(ExchangeName, routingKey: QueueName, basicProperties: props, body: body);

                    Console.WriteLine("Producter Sent: {0}", message);
                    Console.Read();
                }
            }
        }
    }
}

消费者

using RabbitMQ.Client;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace service
{
    class Recevice
    {
        public void Test()
        {
            Recevice.Read();
        }
        const string ExchangeName = "mqt.exchange";
        const string QueueName = "mqt.queue";
        public static void Read()
        {
            var factory = new ConnectionFactory()
            {
                HostName = "192.168.56.2",
                UserName = "mqt",
                Password = "123456",
                VirtualHost="/"
            };
            using (var connection = factory.CreateConnection())
            {
                using (var channel = connection.CreateModel())
                {
                    channel.ExchangeDeclare(ExchangeName, "direct", durable: true, autoDelete: false, arguments: null);//声明消息队列,且为可持久化的
                    channel.QueueDeclare(QueueName, durable: true, exclusive: false, autoDelete: false, arguments: null);//声明消息队列,且为可持久化的
                    channel.QueueBind(QueueName, ExchangeName, routingKey: QueueName);

                    BasicGetResult msgResponse = channel.BasicGet(QueueName, noAck: true);
                    //NoAck:true 告诉RabbitMQ立即从队列中删除消息,另一个非常受欢迎的方式是从队列中删除已经确认接收的消息,可以通过单独调用BasicAck 进行确认:
                    //BasicGetResult msgResponse = channel.BasicGet(QueueName, noAck:false);
                    var msgContent = Encoding.UTF8.GetString(msgResponse.Body);

                    Console.WriteLine("The received content:" + msgContent);

                    channel.BasicAck(msgResponse.DeliveryTag, multiple: false);
                    //使用BasicAck方式来告之是否从队列中移除该条消息
                    //需要额外注意,比如从队列中获取消息并用它来操作数据库或日志文件时,如果出现操作失败时,则该条消息应该保留在队列中,只到操作成功时才从队列中移除。
                    Console.Read();
                }
            }
        }
    }
}

 

  1. //RabbitMQ消息模型的核心思想就是,生产者不把消息直接发送给队列。实际上,生产者在很多情况下都不知道消息是否会被发送到一个队列中。取而代之的是,生产者将消息发送到交换区。交换区是一个非常简单的东西,它一端接受生产者的消息,另一端将他们推送到队列中。交换区必须要明确的指导如何处理它接受到的消息。是放到一个队列中,还是放到多个队列中,亦或是被丢弃。这些规则可以通过交换区的类型来定义。  
  2.                           //可用的交换区类型有:direct,topic,headers,fanout。  
  3.                           //Exchange:用于接收消息生产者发送的消息,有三种类型的exchange:direct, fanout,topic,不同类型实现了不同的路由算法;  
  4.                           //RoutingKey:是RabbitMQ实现路由分发到各个队列的规则,并结合Binging提供于Exchange使用将消息推送入队列;  
  5.                           //Queue:是消息队列,可以根据需要定义多个队列,设置队列的属性,比如:消息移除、消息缓存、回调机制等设置,实现与Consumer通信;  
posted @ 2017-06-26 20:00  wjl910  阅读(179)  评论(0)    收藏  举报