RabbmitMQ-Publish/Subscribe

之前的学习中,我们了解的工作队列实现的是:一个消息只发送到一个消费者。

现在我们来学习一下新模式:发布/订阅模式

之前我们在原理中介绍了exchange,但好像并没有使用。而是直接往队列里发消息和取消息。现在我们使用rabbitmq的消息模型,利用交换机来转发消息。

首先我们来了解一下exchange的四种类型:fanout、direct、topic、headers

fanout:

 RabbmitMQ-Publish/Subscribe

  1、消息与队列匹配规则:该类型交换机会将收到的消息广播给所有与之绑定的队列

  2、该情况下,生产者只关系消息发送到哪个交换机,由交换机决定消息发送到哪些队列。而消费者只关系订阅哪个队列

direct:

  RabbmitMQ-Publish/Subscribe

  1、消息分发规则:消息会被推送至绑定键(binding key)和消息发布附带的路由键(routing key)完全匹配的消息

  2、图示说明:消息1附带路由键“error”、与绑定键“error”匹配,而队列Q4、Q5与交换机X间都存在绑定键“error”所以消息1被分发到Q4、Q5;消息2附带路由键“info”,而队列Q4与交换机间存在绑定建“info”,所以消息2被分发到队列Q4。

  3、分发到队列的消息不再带有发送者的任何信息,如果消息实体里包含了发送者的信息,那么消费者可以获取发送者信息

topic:

  RabbmitMQ-Publish/Subscribe

  1、消息分发规则:一个附带特殊的路由键(routing key)会被转发到绑定键(binding key)与之匹配的队列中

  2、routing key与binding key匹配规则: routing key必须是由点隔开的一系列的标识符组成。标识符可以是任何东西,但一般都与消息的某些特效有关。如:"stock.usd.nyse","nyse.vmw","quick.orange.rabbit"。上限为255字节。 绑定键与选择键形式一样。 两种特殊情况:*匹配一个标识符、#匹配0个或多个标识符

  3、图示说明:  消息1附带路由键“fast.orange.*”与绑定键“#”、“*.orange.*”匹配,所以消息1被分发给队列Q6、Q7;消息2附带路由键“lazy.orange.a.b”与绑定键“#”、“lazy.#”匹配,所以消息2被分发给队列Q6、Q8。

headers:

 RabbmitMQ-Publish/Subscribe

  1、消息分发规则: headers类型的交换机发消息不依赖routing key。而是使用发消息时basicProperties对象中的headers匹配。headers是一个键值对类型。发送者发消息时将这些键值对放在basicProperties对象的headers字段中。队列绑定交换机时绑定一些键值对。两者匹配时。队列就可以收到消息。

    匹配模式有两种:

     在队列绑定到交换机时用x-match来指定,all代表定义的多个键值对都要满足,any代表只要满足一个就可以。fanout,direct,topic中的routing 可以需要字符串,而headers没有这个要求。键值对可以是任何类型。

  2、图示说明:  消息1附带的键值对与Q9绑定键值对的color匹配、speed不匹配,但是Q9的x-match设置为any,因此只要有一项匹配消息1就可以被分发到Q9。消息1与Q10完全匹配,消息2与Q10部分匹配,由于Q10的x-match设置为all,所以只能接受到消息1。

案例:创建一个简单的日志系统,有两个程序组成:一个输出日志消息,一个接收并打印日志消息

  在该系统中,每一个cusumer都会接到所有消息,其中一个cusumer直接保存消息到磁盘,另一个cusumer输出消息

  在该案例中,我们使用fanout类型的exchange,通过 channel.exchangeDeclare("logs,"fanout")来设置参数

  在rabbitmq中,默认的exchange的名字是空字符串。

在coding之前我们还要了解一下些小知识:

  临时队列:

    当我们指定了特定名字的队列时,我们要让消费者指向同一个队列。当生产者消费者共用一个队列时。队列名字非常重要。 对于本案例的日志系统而言,我们想让每个消费者都接到所有的日志消息。而不是其中一部分。我们关心的是当前广播的消息而不是之前的。所以我们要做两件事情满足我们的要求:

  1、我们连接rabbitmq服务时,需要一个新鲜的空的队列。我们自定义随机名,更好的是让rabbitmq服务器为我们的队列随机起名

  2、当我们关闭消费者时,队列应该自动删除

  如何创建队列名呢?

    String queueName = channel.queueDeclare().getQueue();

    这样可以获取随机的队列名 调用无参的queueDeclare方法时,就创建一个非持久、独特的会自动删除的队列

  绑定:

   我们创建了一个fanout类型的exchange和一个队列。我们需要告诉exchange发送消息到我们的队列。exchange与队列之间的关系称为绑定(binding)。    

   channel.queueBind(queueName,"logs",""); 这样我们就将创建的队列和exchange建立了关系。

   exchange会将消息发送到这个队列 rabbitmqctl list_binding 查看所有已经存在的绑定关系

Coding一下,你就知道

producer:

RabbmitMQ-Publish/Subscribe

cusumer: RabbmitMQ-Publish/Subscribe

在这个模式下:

  对于producer而言,我们将消息发送给logs交换机,而不是无名的交换机。发消息时我们提供一个路由键routing key。但它的值会被fanout类型的交换机忽略。

  如果路由器还没有绑定队列,这些发送给路由器的消息将会丢失。但这对我们无所谓,如果还没有cusumer监听,我们可以安全地丢弃这些消息。

上一篇:Python中Json对象处理的jsonpath-rw


下一篇:星级评分原理 N次重写的分析