RabbitMq系列(一):服务器搭建
RabbitMq系列(二):最简单的例子
RabbitMq系列(三):工作队列
RabbitMq系列(四):消息确认和持久性
RabbitMq系列(五):公平派遣
RabbitMq系列(六):交换类型以及例子
RabbitMq系列(七):直接交换Direct exchange
RabbitMq系列(八):扇出交换Fanout Exchange
RabbitMq系列(九):主题交换Topic Exchange
前言
直接交换Direct exchange
例子1
例子2
扇出交换Fanout Exchange
例子1
主题交换Topic Exchange
例子1
例子2
例子3
例子4
标头交换Headers exchange
例子1
例子2
前面对RabbitMq进行应用和熟悉的过程中,我们对交换机( exchange ,官方比作邮局和邮箱,这里以下统称 交换机 )的使用总是一笔带过,对于交换机的作用以及相关信息都不了解,那么交换机是干什么用的呢?路由键( routingKey )和交换机有什么关系?交换机类型有哪些以及它们之间的区别?
生产者( producers,亦称作 发布者 publishers )将消息发送后,消息并不是直接发送到队列 ( queue ),而是发送到RabbitMq代理服务器第一站——交换机。交换机是由虚拟主机( virtual host )定义的消息代理,他一边接受生产者发送的消息,一边通过声明好的路由规则将消息副本分发到相应的队列。然后,要么代理主动将消息推送给订阅了队列的消费者( consumers ),要么消费者根据自己的需求主动去获取。
路由规则在消息发送过程中是至关重要的一环,在队列通过绑定( binding )动作绑定到交换机上面后,会产生一个类似于 link 的关系,消息来临时,交换机通过路由规则查找相应的队列并将消息发送到队列中。
路由规则主要由消费者和生产者的路由键( routingKey )、交换机类型( exchange type )来决定的,两个路由键( routingKey )就像是路由规则中的两个关键属性,而交换机类型( exchange type )则决定怎么使用路由键( routingKey )来产生规则,类似于方法。
其主要工作流程是:
交换机类型看起来有五种:
交换类型 | 默认 |
直接交换 Direct exchange | amq.direct |
扇出交换 Fanout exchange | amq.fanout |
主题交换 Topic exchange | amq.topic |
标头交换 Headers exchange | amq.headers |
默认交换 Default exchange | 空字符串 |
实际上最后一种,默认交换 Default exchange 是特殊的直接交换。其主要是在声明的阶段路由键为空字符串产生。默认交换是由消息服务器帮我们预先声明好的一种特殊的直接交换,队列使用默认交换可以直接产生一个同名的路由键,当有消息到达交换机后,由交换机根据同名的路由键将消息路由给队列。
下面我们针对主要的四种交换类型进行详细描述。
直接交换是一种依赖于路由键( routing-key )的交换方式,对于单对单发送消息来说,它是最理想的方案,应用这种方案,exchange type 必须定义成直接交换类型。
消息到达交换机过后,交换机会根据 binding-key 和 routing-key 是否完全匹配来决定消息的发送。如果 routing-key 不匹配任意一个 binding-key,消息会被释放。
direct.route.* 与 direct.route.one不完全匹配,所以当消息到达交换机 ack.exchange 的时候,消息不会被路由到任何队列,消息被释放
direct.route.# 与 direct.route.# 完全匹配,交换机 exchange.direct 将到达的消息发送到队列 queue.direct
扇出交换是忽略路由键的交换方式,它不关注路由键,它只关注当前绑定到交换机上面的队列。当消息到达交换机后,交换机会将消息路由到绑定在它上面的所有队列中。
例如当前有5个队列绑定在 ack.exchange 这个交换机上面,并且交换类型都是扇出交换,那么无论路由键怎样,交换机发送消息这5个队列都会收到。
扇出交换是最理想的广播消息模式,对于同一个消息的不同处理、公告更新等,它都是最理想的选择。
尽管direct.route.* 与 direct.route.one不完全匹配,但是当前为广播模式,所以忽略路由键。消息到达交换机 exchange.fanout 后会被发送到队列 queue.fanout
交换机使用主题交换会在消息到达后,对 binding-key 和 routing-key 进行模糊匹配来决定将消息发送给一个或者多个队列。主题交换就像是扇出交换和直接交换的折中,它主要针对有选择性的处理消息的情况,类似于数据权限的权限筛选。
主题交换除了一般的完全匹配(如: act.direct.route 与 act.direct.route ),还支持 # 和 * 的模糊匹配。
# 代表匹配任意零个或者多个字符
* 代表匹配任意一个字符
主题交换是最灵活的交换方式,所以它适用于很多场景,但是更多的还是特定情况的特殊处理方案的应用。
当消息到达交换机过后,交换机会将消息的 routing-key 来匹配队列 binding-key,这样看起来就跟某字符串匹配正则表达式一样,通过匹配的一个或者多个队列交换机会将消息路由到其中。
topic.route.one 符合 topic.route.* 的匹配, * 匹配任意字符,这里匹配 one,消息到达交换机 exchange.topic 后,交换机会将消息路由到队列 queue.topic
# 匹配零个或者多个字符,这里匹配 route.* ,所以 topic.# 完全匹配 topic.route.*,消息到达交换机 exchange.topic 后,交换机会将消息路由到队列 queue.topic
# 匹配零个或者多个字符,这里匹配零个字符,所以 topic.#.cn 完全匹配 topic.cn,消息到达交换机 exchange.topic 后,交换机会将消息路由到队列 queue.topic
one 不匹配 # ,one 只能与 one 匹配,所以 topic.one 不匹配 topic.#,消息到达交换机 exchange.topic 后,消息被释放,
匹配的时候,请注意,谁匹配谁
标头交换是一个忽略路由键的交换模式,类型为 Headers 的交换机在消息到达后,会将消息所带 headers 中 key/value 参数与绑定队列所带 key/value 参数进行比较,基于不同的 x-match 类型不一样的比较规则。
x-match 为 Headers 交换类型中特有的参数,其同样可以作为队列绑定参数,但是,x-match 不作为匹配的参数, x- 开头的参数会被交换机定义为不可匹配参数,需要注意的是,生产者发送消息的时候也能添加 x-match 作为参数,但是交换机进行参数匹配主要是基于消费者的 x-match
x-match 的value有两个,any 和 all
any 代表匹配参数中的任意一组
all 代表匹配参数中的所有组,x-match除外
key/value 这样才能被称为一组,例如 type = zip
因为是 Headers 交换模式,routingKey 在这里无效,所以消息到达后匹配参数,这里看 consumer 的 args 和 producer 的 headers,args 和 headers 都有x-match,那么究竟谁来定规则喃?
队列要不要这个消息当然由定义队列规则的消费者来定,x-match=all,所以这里的规则是匹配除 x-match 外的所有参数,一个age = 32,另一个 age=321,所以匹配失败,消息被释放
根据例子1,这里 x-match=any,所以这里的规则是匹配任意一个参数,一个age = 32,另一个也是 age=32,所以匹配成功,消息到达交换机 exchange.header 后,交换机会将消息发送到队列 queue.header