上一篇文章中,介绍了在window环境下安装erlang,rabbitmq-server,以免配置用户,权限,虚拟机等内容。
   
      今天将会介绍如果使用rabbitmq进行简单的消息入队,出队操作,因为本文演示的环境要用到上文中配置的环境,所以要运行本文sample,请先按上一篇中完成相应环境配置。
   
      首先,我们下载官方的.net客户端软件,链接:http://www.rabbitmq.com/dotnet.html
   
      下载并安装之后,将安装目录下的这两个DLL文件复制到我们示例项目中,并添加引用:

RabbitMQ.Client.dll //基于的发布订阅消息的功能类   
RabbitMQ.ServiceModel.dll //包括基于WCF方式发布订阅服务模型类

如下图:
   
   
       接着,我们创建两个类,一个是ProducerMQ.cs(用于产生消息),一个是CustmerMq.cs(用于消费消息),代码如下:
   
       首先是ProducerMQ:


public class ProducerMQ
{
    public static  void InitProducerMQ()
    {
        Uri uri = new Uri("amqp://10.0.4.85:5672/");
        string exchange = "ex1";
        string exchangeType = "direct";
        string routingKey = "m1";
        bool persistMode = true;
        ConnectionFactory cf = new ConnectionFactory();
      
        cf.UserName = "daizhj";
        cf.Password = "617595";
        cf.VirtualHost = "dnt_mq";
        cf.RequestedHeartbeat = 0;
        cf.Endpoint = new AmqpTcpEndpoint(uri);
        using (IConnection conn = cf.CreateConnection())
        {
            using (IModel ch = conn.CreateModel())
            {
                if (exchangeType != null)
                {
                    ch.ExchangeDeclare(exchange, exchangeType);//,true,true,false,false, true,null);
                    ch.QueueDeclare("q1", true);//true, true, true, false, false, null);
                    ch.QueueBind("q1", "ex1", "m1", false, null); 
                }
                IMapMessageBuilder b = new MapMessageBuilder(ch);
                IDictionary target = b.Headers;
                target["header"] = "hello world";
                IDictionary targetBody = b.Body;
                targetBody["body"] = "daizhj";
                if (persistMode)
                {
                    ((IBasicProperties)b.GetContentHeader()).DeliveryMode = 2;
                }
             
                ch.BasicPublish(exchange, routingKey,
                                           (IBasicProperties)b.GetContentHeader(),
                                           b.GetContentBody());             }
        }
    }
}

下面对上面代码进行说明:
    1.  定义要链接的rabbitmq-server地址(基于amqp协议):

Uri uri = new Uri("amqp://10.0.4.85:5672/");

2.  定义交换方式

string exchange = "ex1";
string exchangeType = "direct";
string routingKey = "m1";

说明:rabbitmq交换方式分为三种,分别是:
        Direct Exchange – 处理路由键。需要将一个队列绑定到交换机上,要求该消息与一个特定的路由键完全匹配。这是一个完整的匹配。如果一个队列绑定到该交换机上要求路由键 “dog”,则只有被标记为“dog”的消息才被转发,不会转发dog.puppy,也不会转发dog.guard,只会转发dog。 
        Fanout Exchange – 不处理路由键。你只需要简单的将队列绑定到交换机上。一个发送到交换机的消息都会被转发到与该交换机绑定的所有队列上。很像子网广播,每台子网内的主机都获得了一份复制的消息。Fanout交换机转发消息是最快的。 
        Topic Exchange – 将路由键和某模式进行匹配。此时队列需要绑定要一个模式上。符号“#”匹配一个或多个词,符号“*”匹配不多不少一个词。因此“audit.#”能够匹配到“audit.irs.corporate”,但是“audit.*” 只会匹配到“audit.irs”。
        更多内容参见:RabbitMQ 三种Exchange  
        
     3. 是否对消息队列持久化保存

bool persistMode = true;

4. 使用ConnectionFactory创建连接,虽然创建时指定了多个server address,但每个connection只与一个物理的server进行连接。


       ConnectionFactory cf = new ConnectionFactory();    
        //使用前文的配置环境信息  
        cf.UserName = "daizhj"; 
        cf.Password = "617595";
        cf.VirtualHost = "dnt_mq";
        cf.RequestedHeartbeat = 0;
        cf.Endpoint = new AmqpTcpEndpoint(uri);

5. 实例化IConnection对象,并设置交换方式


 using (IConnection conn = cf.CreateConnection())
            {
                using (IModel ch = conn.CreateModel())
                {
                    if (exchangeType != null)
                    {
                        ch.ExchangeDeclare(exchange, exchangeType);//,true,true,false,false, true,null);
                        ch.QueueDeclare("q1", true);//true, true, true, false, false, null);
                        ch.QueueBind("q1", "ex1", "m1", false, null); 
                    }
        ....

6. 构造消息实体对象并发布到消息队列上:


  IMapMessageBuilder b = new MapMessageBuilder(ch);
  IDictionary target = b.Headers;
  target["header"] = "hello world";
  IDictionary targetBody = b.Body;
  targetBody["body"] = "daizhj";
  if (persistMode)
  {
    ((IBasicProperties)b.GetContentHeader()).DeliveryMode = 2;
  }
  //简单发布方式
  ch.BasicPublish(exchange, routingKey,
          (IBasicProperties)b.GetContentHeader(),
          b.GetContentBody());

这样就完成了单条消息的发布。
    
    下面是CustmerMq.cs(用于消费消息)实例代码:


public class CustmerMq
    {
        public static int InitCustmerMq()
        {
            string exchange = "ex1";
            string exchangeType = "direct";
            string routingKey = "m1";             string serverAddress = "10.0.4.85:5672";
            ConnectionFactory cf = new ConnectionFactory();
            cf.Address = serverAddress;
            cf.UserName = "daizhj";
            cf.Password = "617595";
            cf.VirtualHost = "dnt_mq";
            cf.RequestedHeartbeat = 0;

可以看出上面的代码与 ProducerMQ的开头代码类似,下面使用ConnectionFactory来构造链接并接收队列消息:


 using (IConnection conn = cf.CreateConnection())
            {
                using (IModel ch = conn.CreateModel())
                {
                    //普通使用方式BasicGet
                    //noAck = true,不需要回复,接收到消息后,queue上的消息就会清除
                    //noAck = false,需要回复,接收到消息后,queue上的消息不会被清除,直到调用channel.basicAck(deliveryTag, false); queue上的消息才会被清除 而且,在当前连接断开以前,其它客户端将不能收到此queue上的消息
                    BasicGetResult res = ch.BasicGet("q1", false/*noAck*/);
                    if (res != null)
                    {
                        bool t = res.Redelivered;
                        t = true;
                        Console.WriteLine(System.Text.UTF8Encoding.UTF8.GetString(res.Body));
                        ch.BasicAck(res.DeliveryTag, false);
                    }
                    else
                    {
                        Console.WriteLine("No message!");
                    }  

上面代码比较简单,主要是使用BasicGetResult来进行简单的消息接收,并使用BasicAck方式来告之是否从队列中移除该条消息。这一点很重要,因为在某些应用场景下,比如从队列中获取消息并用它来操作数据库或日志文件时,如果出现操作失败时,则该条消息应该保留在队列中,只到操作成功时才从队列中移除。
  
      当然上面操作只是用于单条数据操作,如果要遍历队列中所有消息,则需要使用如下方式:


while (true)
  {
      BasicGetResult res = ch.BasicGet("q1", false/*noAck*/);
      if (res != null)
      {
          try
          {
               bool t = res.Redelivered;
                        t = true;
                        Console.WriteLine(System.Text.UTF8Encoding.UTF8.GetString(res.Body));
                        ch.BasicAck(res.DeliveryTag, false);
          }
          catch { }
      }
      else
          break;
  }

另外,在rabbitmq中,获取消息可以使用两种方式,一种是上面提到的主动获取,另一种是基于订阅模式,即让当前获取消息的线程阻塞,用于绑定到指定的队列上,当有新的消息入队之后,该阻塞线程会被运行,从队列中获取新入队的消息,形如:


 //第二种取法QueueingBasicConsumer基于订阅模式
 QueueingBasicConsumer consumer = new QueueingBasicConsumer(ch);
 ch.BasicConsume("q1", false, null, consumer);
 while (true)
 {
     try
     {
         BasicDeliverEventArgs e = (BasicDeliverEventArgs)consumer.Queue.Dequeue();
         IBasicProperties props = e.BasicProperties;
         byte[] body = e.Body;
         Console.WriteLine(System.Text.Encoding.UTF8.GetString(body));
         //ch.BasicAck(e.DeliveryTag, true);
         ProcessRemainMessage();                          
     }
     catch (EndOfStreamException ex) 
     {
         //The consumer was removed, either through channel or connection closure, or through the action of IModel.BasicCancel(). 
         Console.WriteLine(ex.ToString());
         break;
     }
 }

这样,就完成了一个简单的发布,消费消息的示例。在接下来的文章中,将会介绍如果基于WCF来发布RABBITMQ服务,敬请关注:)

NET下RabbitMQ实践[示例篇]的更多相关文章

  1. NET下RabbitMQ实践[配置篇]

    这个系列目前计划写四篇,分别是配置,示例,WCF发布,实战.当然不排除加餐情况.  介绍: rabbitMQ是一个在AMQP协议标准基础上完整的,可服用的企业消息系统.他遵循Mozilla Publi ...

  2. NET下RabbitMQ实践[实战篇]

    之前的文章中,介绍了如何将RabbitMQ以WCF方式进行发布.今天就介绍一下我们产品中如何使用RabbitMQ的!          在Discuz!NT企业版中,提供了对HTTP错误日志的记录功能 ...

  3. NET下RabbitMQ实践[WCF发布篇]

    在之前的两篇文章中,主要介绍了RabbitMQ环境配置,简单示例的编写.今天将会介绍如何使用WCF将RabbitMQ列队以服务的方式进行发布.          注:因为RabbitMQ的官方.net ...

  4. 实践详细篇-Windows下使用VS2015编译的Caffe训练mnist数据集

    上一篇记录的是学习caffe前的环境准备以及如何创建好自己需要的caffe版本.这一篇记录的是如何使用编译好的caffe做训练mnist数据集,步骤编号延用上一篇 <实践详细篇-Windows下 ...

  5. 实践详细篇-Windows下使用Caffe训练自己的Caffemodel数据集并进行图像分类

    三:使用Caffe训练Caffemodel并进行图像分类 上一篇记录的是如何使用别人训练好的MNIST数据做训练测试.上手操作一边后大致了解了配置文件属性.这一篇记录如何使用自己准备的图片素材做图像分 ...

  6. Python操作rabbitmq 实践笔记

    发布/订阅  系统 1.基本用法 生产者 import pika import sys username = 'wt' #指定远程rabbitmq的用户名密码 pwd = ' user_pwd = p ...

  7. Tree-Shaking性能优化实践 - 原理篇

    Tree-Shaking性能优化实践 - 原理篇   一. 什么是Tree-shaking 先来看一下Tree-shaking原始的本意 上图形象的解释了Tree-shaking 的本意,本文所说的前 ...

  8. 消息队列那么多,为什么建议深入了解下RabbitMQ?

    你为啥要在项目中选择xxx消息中间件? 提起消息队列,也许你的脑海里会不自觉地蹦出好多概念:JMS.Kafka.RocketMQ.AMQP.RabbitMQ.ActiveMQ.Pulsar.Redis ...

  9. Spring Boot 2.x 快速入门(下)HelloWorld示例详解

    上篇 Spring Boot 2.x 快速入门(上)HelloWorld示例 进行了Sprint Boot的快速入门,以实际的示例代码来练手,总比光看书要强很多嘛,最好的就是边看.边写.边记.边展示. ...

随机推荐

  1. EXPLAIN PLAN获取SQL语句执行计划

    一.获取SQL语句执行计划的方式 1. 使用explain plan 将执行计划加载到表plan_table,然后查询该表来获取预估的执行计划 2. 启用执行计划跟踪功能,即autotrace功能 3 ...

  2. ORA-15025: could not open disk 处理

    刚才下班回家的路上,接到客户的电话:"回家了吗?我们这边的一套RAC库有个节点有问题哦,一直刷异常,一下子就把磁盘弄满了,我现在停掉了那个节点了.从日志上看好像跟权限有关,现在还有个实例跑着 ...

  3. easy ui 表单ajax和from两种提交数据方法

    说明: ①ajax在表单提交时需要将所有表单的控件的数据一一获取并赋值传到后台 ②form在提交时,只要给控件加name属性,在提交时就可以将表单数据提交到后台,不需要一一获取再进行赋值. ajax ...

  4. setPreferredSize和setSize的区别及用法

    我以前很喜欢borderlayout的布局方式,每次想特别调整每个区域的大小,但是每次将一个panel放入到north或者其他4个区域时,总是达不到想要的效果,刚刚才发现原来setPreferredS ...

  5. OGRE1.8.1源码编译(VS2008)

    最近在学习图形学,想看看OGRE源码,于是就去OGRE官网下载源码,配置环境,这里记录一下.鉴于电脑太破,VS2010比较慢,所以使用VS2008. 准备工作: CMake2.8       下载地址 ...

  6. java第四课:数组

    1.数组声明时,必须有中括号,但不指定数组的元素个数2.初始化时,必须指定元素个数3.数组元素内容仅能用于声明时初始化,不能用于赋值.如:char[] week; week={'1','2','3'} ...

  7. iOS开发笔记--宏定义的黑魔法 - 宏菜鸟起飞手册

    宏定义在C系开发中可以说占有举足轻重的作用.底层框架自不必说,为了编译优化和方便,以及跨平台能力,宏被大量使用,可以说底层开发离开define将寸步难行.而在更高层级进行开发时,我们会将更多的重心放在 ...

  8. linux top动态查看进程信息

    来源<鸟哥的linux私房菜> top:动态观察程序的变化 [root@linux ~]# top [-d] | top [-bnp] 参数: -d :后面可以接秒数,就是整个程序画面更新 ...

  9. java 用JNA方法调用C++动态链接库

    JNA(Java Native Access)框架是一个开源的Java框架,是SUN公司主导开发的,建立在经典的JNI的基础之上的一个框架.非常强大.易用,功能上类似与.NET的P/Invoke.你只 ...

  10. QT插件开发方式(作者有RemOjbects文档翻译(48)篇)

    创建一个QT的库项目,删除自动生成的.h和.cpp文件,添加一个接口定义.h文件和一个接口实现类(一个.h一个.cpp).代码如下: 1.接口文件源码 #ifndef PLUGININTERFACE_ ...