From: http://lostechies.com/derekgreer/2012/05/18/rabbitmq-for-windows-topic-exchanges/

RabbitMQ for Windows: Topic Exchanges

Posted by Derek Greer on May 18, 2012

This is the seventh installment to the series: RabbitMQ for Windows.  In the last installment, we walked through creating a fanout exchange example.  In this installment, we’ll be walking through a topic exchange example.

Topic exchanges are similar to direct exchanges in that they use a routing key to determine which queue a message should be delivered to, but they differ in that they provide the ability to match on portions of a routing key.  When publishing to a topic exchange, a routing key consisting of multiple words separated by periods (e.g. “word1.word2.word3”) will be matched against a pattern supplied by the binding queue.  Patterns may contain an asterisk (“*”) to matcha word[PunCha: 只有1个单词!这个和文件通配符是不一样的。] in a specific segment or a hash (“#”) to matchzero or more words.  As discussed earlier in the series, the topic exchange type can be useful for directing messages based on multiple categories or for routing messages originating from multiple sources.

To demonstrate topic exchanges, we’ll return to our logging example, but this time we’ll subscribe to a subset of the messages being published to demonstrate the flexibility of how routing keys are used by topic exchanges.  For this example, we’ll be modeling a scenario where a company may have multiple client installations, each of which may be used to service different sectors of a company’s business model (e.g. Business or Personal sectors).  We’ll use a routing key that specifies the sector and subscribe to messages published for the Personal sector only.

As with our previous examples, we’ll keep things simple by creating console applications for a Producer and a Consumer.  Let’s start by creating the Producer app and establishing a connection using the default settings:

using RabbitMQ.Client;

namespace Producer
{
  class Program
  {
    const long ClientId = 10843;     static void Main(string[] args)
    {
      var connectionFactory = new ConnectionFactory();
      IConnection connection = connectionFactory.CreateConnection();
    }
  }
}

Rather than just publishing messages directly from the Main() method as with our first logging example, let’s create a separate logger object this time.  Here the logger interface and implementation we’ll be using:

  interface ILogger
  {
    void Write(Sector sector, string entry, TraceEventType traceEventType);
  }   class RabbitLogger : ILogger, IDisposable
  {
    readonly long _clientId;
    readonly IModel _channel;
    bool _disposed;     public RabbitLogger(IConnection connection, long clientId)
    {
      // 每创建一个Logger,就创建一个Channel。      _clientId = clientId;
      _channel = connection.CreateModel();
      _channel.ExchangeDeclare("direct-exchange-example", ExchangeType.Topic, false, true, null);
    }     public void Dispose()
    {
      if (!_disposed)
      {
        if (_channel != null && _channel.IsOpen)
        {
            // 销毁这个Channel。
      _channel.Close();        
        }
   }

    // 标准的Disposable的实现
GC.SuppressFinalize(this);    
}

    public void Write(Sector sector, string entry, TraceEventType traceEventType)
    {
      byte[] message = Encoding.UTF8.GetBytes(entry);
     // RoutingKey: ClientID.Sector.EventType
  string routingKey = string.Format("{0}.{1}.{2}", _clientId, sector.ToString(), traceEventType.ToString());
      _channel.BasicPublish("topic-exchange-example", routingKey, null, message);
    }

    ~RabbitLogger()
    {
      Dispose();
    }
  }

In addition to an open IConnection, our RabbitLogger class is instantiated with a client Id.  We use this as part of the routing key.  Since each log can vary by sector, we pass a Sector enum as part of the Write() method.  Here’s our Sector enum:

  public enum Sector
  {
    Personal,
    Business
  }

Returning to our Main() method, we now need to instantiate our RabbitLogger and log messages with differing sectors.  As as way to ensure our client has an opportunity to subscribe to our messages and to help emulate a continual stream of log messages being published, let’s use the logger to publish a series of log messages every second for 10 seconds:

      TimeSpan time = TimeSpan.FromSeconds(10);
      var stopwatch = new Stopwatch();
      Console.WriteLine("Running for {0} seconds", time.ToString("ss"));
      stopwatch.Start();       while (stopwatch.Elapsed < time)
      {
       // 每个Loop都创建一个新的Channel,传入相同的ClientId
        using (var logger = new RabbitLogger(connection, ClientId))
        {
          Console.Write("Time to complete: {0} seconds\r", (time - stopwatch.Elapsed).ToString("ss"));
        // RK: 10843.Personal.Information
          logger.Write(Sector.Personal, "This is an information message", TraceEventType.Information);
        // RK: 10843.Business.Information
          logger.Write(Sector.Business, "This is an warning message", TraceEventType.Warning);
        // RK: 10843.Business.Information
          logger.Write(Sector.Business, "This is an error message", TraceEventType.Error);
          Thread.Sleep(1000);
        }
      }

This code prints out the time remaining just to give us a little feedback on the publishing progress.  Finally, we’ll close our our connection and prompt the user to exit the console application:

      connection.Close();
      Console.Write("                             \r");
      Console.WriteLine("Press any key to exit");
      Console.ReadKey();

Here’s the full Producer listing:

using System;
using System.Diagnostics;
using System.Text;
using System.Threading;
using RabbitMQ.Client; namespace Producer
{
  public enum Sector
  {
    Personal,
    Business
  }   interface ILogger
  {
    void Write(Sector sector, string entry, TraceEventType traceEventType);
  }   class RabbitLogger : ILogger, IDisposable
  {
    readonly long _clientId;
    readonly IModel _channel;
    bool _disposed;     public RabbitLogger(IConnection connection, long clientId)
    {
      _clientId = clientId;
      _channel = connection.CreateModel();
      _channel.ExchangeDeclare("direct-exchange-example", ExchangeType.Topic, false, true, null);
    }     public void Dispose()
    {
      if (!_disposed)
      {
        if (_channel != null && _channel.IsOpen)
        {
          _channel.Close();
        }
      }
      GC.SuppressFinalize(this);
    }     public void Write(Sector sector, string entry, TraceEventType traceEventType)
    {
      byte[] message = Encoding.UTF8.GetBytes(entry);
      string routingKey = string.Format("{0}.{1}.{2}", _clientId, sector.ToString(), traceEventType.ToString());
      _channel.BasicPublish("topic-exchange-example", routingKey, null, message);
    }     ~RabbitLogger()
    {
      Dispose();
    }
  }   class Program
  {
    const long ClientId = 10843;     static void Main(string[] args)
    {
      var connectionFactory = new ConnectionFactory();
      IConnection connection = connectionFactory.CreateConnection();       TimeSpan time = TimeSpan.FromSeconds(10);
      var stopwatch = new Stopwatch();
      Console.WriteLine("Running for {0} seconds", time.ToString("ss"));
      stopwatch.Start();       while (stopwatch.Elapsed < time)
      {
        using (var logger = new RabbitLogger(connection, ClientId))
        {
          Console.Write("Time to complete: {0} seconds\r", (time - stopwatch.Elapsed).ToString("ss"));
          logger.Write(Sector.Personal, "This is an information message", TraceEventType.Information);
          logger.Write(Sector.Business, "This is an warning message", TraceEventType.Warning);
          logger.Write(Sector.Business, "This is an error message", TraceEventType.Error);
          Thread.Sleep(1000);
        }
      }       connection.Close();
      Console.Write("                             \r");
      Console.WriteLine("Press any key to exit");
      Console.ReadKey();
    }
  }
}

For our Consumer app, we’ll pretty much be using the same code as with our fanout exchange example, but we’ll need to change the exchange type along with the exchange and queue names.  Additionally, we also need to provide a routing key that registers for logs in the Personal sector only.  The messages published by the Producer will be in the form: [client Id].[sector].[log severity], so we can use a routing key of “*.Personal.*” (or alternately “*.Personal.#”).  Here’s the full Consumer listing:

using System;
using System.IO;
using System.Text;
using RabbitMQ.Client;
using RabbitMQ.Client.Events; namespace Consumer
{
  class Program
  {
    static void Main(string[] args)
    {
      var connectionFactory = new ConnectionFactory();
      IConnection connection = connectionFactory.CreateConnection();
      IModel channel = connection.CreateModel();       channel.ExchangeDeclare("topic-exchange-example", ExchangeType.Topic, false, true, null);
      channel.QueueDeclare("log", false, false, true, null);
     // 只接受*.Personal.*的消息。也就是忽略*.Business.*的消息。(注意,因为没有建立*.Business.*对
     // 应的Queue,所以Producer产生的那些消息会丢失。
      channel.QueueBind("log", "topic-exchange-example", "*.Personal.*");
      var consumer = new QueueingBasicConsumer(channel);
      channel.BasicConsume("log", true, consumer);
      while (true)
      {
        try
        {
          var eventArgs = (BasicDeliverEventArgs) consumer.Queue.Dequeue();
          string message = Encoding.UTF8.GetString(eventArgs.Body);
          Console.WriteLine(string.Format("{0} - {1}", eventArgs.RoutingKey, message));
        }
        catch (EndOfStreamException)
        {
          // The consumer was cancelled, the model closed, or the connection went away.
          break;
        }
      }
      channel.Close();
      connection.Close();
    }
  }
}

Setting the solution to run both the Producer and Consumer on startup, we should see similar output to the following listings:

Producer

Running for 10 seconds
Time to complete: 06 seconds

Consumer

10843.Personal.Information - This is an information message
10843.Personal.Information - This is an information message
10843.Personal.Information - This is an information message
10843.Personal.Information - This is an information message
10843.Personal.Information - This is an information message
10843.Personal.Information - This is an information message
10843.Personal.Information - This is an information message

This concludes our topic exchange example.  Next time, we’ll walk through an example using the final exchange type: Header Exchanges.

RabbitMQ学习之:(八)Topic Exchange (转贴+我的评论)的更多相关文章

  1. rabbitmq学习(八) —— 可靠机制上的“可靠”

    接着上一篇,既然已经有了手动ack.confirm机制.return机制,还不够吗? 以下博文转自https://www.jianshu.com/p/6579e48d18ae和https://my.o ...

  2. (转)RabbitMQ学习之主题topic(java)

    http://blog.csdn.net/zhu_tianwei/article/details/40887775 参考:http://blog.csdn.NET/lmj623565791/artic ...

  3. rabbitMQ学习笔记(六) topic类型消息。

    上一节中使用了消息路由,消费者可以选择性的接收消息. 但是这样还是不够灵活. 比如某个消费者要订阅娱乐新闻消息 . 包括新浪.网易.腾讯的娱乐新闻.那么消费者就需要绑定三次,分别绑定这三个网站的消息类 ...

  4. RabbitMQ学习总结 第六篇:Topic类型的exchange

    目录 RabbitMQ学习总结 第一篇:理论篇 RabbitMQ学习总结 第二篇:快速入门HelloWorld RabbitMQ学习总结 第三篇:工作队列Work Queue RabbitMQ学习总结 ...

  5. RabbitMQ学习系列(四): 几种Exchange 模式

    上一篇,讲了RabbitMQ的具体用法,可以看看这篇文章:RabbitMQ学习系列(三): C# 如何使用 RabbitMQ.今天说些理论的东西,Exchange 的几种模式. AMQP协议中的核心思 ...

  6. RabbitMQ学习笔记(五) Topic

    更多的问题 Direct Exchange帮助我们解决了分类发布与订阅消息的问题,但是Direct Exchange的问题是,它所使用的routingKey是一个简单字符串,这决定了它只能按照一个条件 ...

  7. (八)RabbitMQ消息队列-通过Topic主题模式分发消息

    原文:(八)RabbitMQ消息队列-通过Topic主题模式分发消息 前两章我们讲了RabbitMQ的direct模式和fanout模式,本章介绍topic主题模式的应用.如果对direct模式下通过 ...

  8. RabbitMQ学习之:(六)Direct Exchange (转贴+我的评论)

    From: http://lostechies.com/derekgreer/2012/04/02/rabbitmq-for-windows-direct-exchanges/ RabbitMQ fo ...

  9. RabbitMQ学习笔记3-使用topic交换器

    topic的路由规则里使用[.]号分隔单词,使用[*]号匹配1个单词,使用[#]匹配多个.和多个*. 在下面的例子中: logger.*可以匹配logger.error和logger.warning, ...

随机推荐

  1. 【Day3】2.re模块使用案例

    课程目标 1.提取 2.匹配 3.替换 1.提取 import re ret = re.findall([正则表达式]) # 返回的类型是列表 2.匹配 import re ret = re.matc ...

  2. Makefile中代码写在同一行和分开几行写有什么区别?

    在Makefile中,写在同一行的代码就相当于是在Linux中的同一行命令行写的代码一样,是一次性执行的:如程序中如下编写Makefile代码(请注意,两个命令之间应该使用分号“:”来进行分隔): a ...

  3. 了解认识asp.net运行机制

    asp.net  运行机制 下面了解认识httpModule 要创建一个httpModule类 using System;using System.Collections.Generic;using ...

  4. HandlerMethodArgumentResolver完美解决 springmvc注入参数多传报错

    作为一个后端开发,能友好兼容前端参数传入错误等问题,在前端发布不小心多传一个参数导致系统错误的问题,一个广告系统是零容忍的,所以为了不犯错误,后端接收参数必须摒弃spring 的自动注入@Reques ...

  5. CP and Tucker Tensor Decomposition

    1.. 2..

  6. Android Vitals各性能指标介绍

    Android vitals 简介 谷歌推荐使用Android vitals来帮助开发者提高App的稳定性和性能表现. 作为这个方案的一部分, Play Console提供了Android Vital ...

  7. c#系统泛型委托

    Action<T> 无返回值的系统泛型委托 namespace ConsoleApp1 { public class UserInfo { public int Id { get; set ...

  8. WPF DevExpress ChartControl使用之PieChart

    饼状图要比XYDiagram要简单一点,大体上也是那些东西,没有了X.Y坐标轴,也就没有了第二坐标,要简单一点.PieChartControl.xaml <UserControl x:Class ...

  9. So easy RHCE

    1.将VGSRV  拉伸为100MB  VGSRV这个是逻辑卷的home分区,逻辑卷是可以随意拉伸的,但是需要注意的是拉伸之前必须使用umount卸载,否则系统会崩溃,虽然可以还原但是很麻烦,顺序不可 ...

  10. 长春理工大学第十四届程序设计竞赛H Arithmetic Sequence——使用特例

    题目 链接 题意:给定一个数X,输出一个等差数列,使得和为X. 分析 由等差数列的定义,可见一个数就是等差数列,两个数也是等差数列 #include<bits/stdc++.h> usin ...