1. Work Queue
生产者发消息,启动多个消费者实例来消费消息,每个消费者仅消费部分信息,可达到负载均衡的效果。
2. 发布订阅模式
使用fanout类型交换器,routingKey忽略。每个消费者定义生成一个队列并绑定到同一个 Exchange,每个消费者都可以消费到完整的消息。消息广播给所有订阅该消息的消费者。
在RabbitMQ中,生产者不是将消息直接发送给消息队列,实际上生产者根本不知道一个消息被发 送到哪个队列。
生产者将消息发送给交换器。交换器非常简单,从生产者接收消息,将消息推送给消息队列。交换器必须清楚地知道要怎么处理接收到的消息。应该是追加到一个指定的队列,还是追加到多个队列,还是丢弃。规则就是交换器类型。
交换器的类型前面已经介绍过了: direct 、 topic 、 headers 和 fanout 四种类型。发布订阅使 用fanout。fanout 交换器从名字就可以看出来(用风扇吹出去),将所有收到的消息发送给它知道 的所有的队列。
3. 路由模式
使用 direct 类型的Exchange,发N条消费并使用不同的 routingKey ,消费者定义队列并将队 列、 routingKey 、Exchange绑定。此时使用 direct 模式Exchagne必须要 routingKey 完全匹配的 情况下消息才会转发到对应的队列中被消费。
例如:分布式系统中有很多应用,这些应用需要运维平台的监控,其中一个重要的信息就是服务器的日志 记录。
我们需要将不同日志级别的日志记录交给不同的应用处理。 如何解决? 使用direct交换器
如果要对不同的消息做不同的处理,此时不能使用 fanout 类型的交换器,因为它只会盲目的广播消息。
direct 交换器的路由算法很简单:只要消息的routingKey 和队列的 bindingKey 对应,消息就可以推送给该队列。
上图中的交换器 X 是 direct 类型的交换器,绑定的两个队列中,一个队列的 bindingKey 是 orange ,另一个队列的 bindingKey 是 black 和 green 。如此,则 routingKey 是 orange 的消息发送给队列Q1, routingKey 是 black 和 green 的消息发 送给Q2队列,其他消息丢弃。
上图中,我们使用 direct 类型的交换器 X ,建立了两个绑定:队列Q1根据 bindingKey 的值 black 绑定到交换器 X ,队列Q2根据 bindingKey 的值 black 绑定到交换器 X ;交换器 X 会将消息发送给队列Q1和队列Q2。交换器的行为跟 fanout 的行为类似,也是广播。
如下图所示:可以通过 direct 类型的交换器根据日志级别的不同,将消息发送给不同的队列。
4. 主题模式
使用 topic 类型的交换器,队列绑定到交换器、 bindingKey 时使用通配符,交换器将消息路由转发到具体队列时会根据消息 routingKey 模糊匹配,比较灵活。
上个模式中,我们通过 direct 类型的交换器做到了根据日志级别的不同,将消息发送给了不同队列的。
这里有一个限制,假如现在我不仅想根据日志级别划分日志消息,还想根据日志来源划分日志,怎么做?
比如,我想监听cron服务发送的 error 消息,又想监听从kern服务发送的所有消息。 此时可以使用RabbitMQ的主题模式( Topic )。
要想 topic 类型的交换器, routingKey 就不能随便写了,它必须得是点分单词。单词可以随便 写,生产中一般使用消息的特征。如:“stock.usd.nyse”,“nyse.vmw”,“quick.orange.rabbit”等。该 点分单词字符串最长255字节。
bindingKey 也必须是这种形式。 topic 类型的交换器背后原理跟 direct 类型的类似:只要队列 的 bindingKey 的值与消息的 routingKey 匹配,队列就可以收到该消息。有两个不同:
- ‘*’ 匹配一个单词
- ‘#’ 匹配0到多个单词
上图中,我们发送描述动物的消息。消息发送的时候指定的 routingKey 包含了三个词,两个点。 第一个单词表示动物的速度,第二个是颜色,第三个是物种:<speed>.<color>.<species>
。
创建三个绑定:Q1绑定到`*.orange.*` Q2绑定到`*.*.rabbit`和`lazy.#`。
1. Q1关注orange颜色动物的消息
2. Q2关注兔子的消息,以及所有懒的动物消息
如果不能匹配,就丢弃消息。
如果发送的消息 routingKey 是" lazy.orange.male.rabbit ",则会匹配最后一个绑定。
如果在 topic 类型的交换器中 bindingKey 使用 # ,则就是 fanout 类型交换器的行为。
如果在 topic 类型的交换器中 bindingKey 中不使用 * 和 # ,则就是 direct 类型交换器的行为。 EmitLogTopic.java