RabbitMQ Topic Exchange 按照规则进行消息路由,注意这里使用的Topic表达方式并不是正则表达式.在入门教程[点击链接]里面,简单描述了一下如何编写规则:
* (star) can substitute for exactly one word.
# (hash) can substitute for zero or more words.
上面这个说法还是容易让人误解的,我尝试解释一下:
. 用来将routing key 分割成若干部分(Part)
* 匹配一个完整的Part
# 匹配一个或者多个Part
验证上面的结论很简单,我们可以使用Client创建不同Exchange来验证这一点;还有一个更为简单的方法就是找到RabbitMQ这部分实现逻辑,顾名思义很容易找到..\rabbitmq-server-2.8.7\src\rabbit_exchange_type_topic.erl文件,最新版本的代码这部分逻辑已经写得比较复杂,回溯到更早的版本rabbitmq-server-2.2.0\src\rabbit_exchange_type_topic.erl,这个就清晰多了,看它的代码:
split_topic_key(Key) -> string:tokens(binary_to_list(Key), "."). topic_matches(PatternKey, RoutingKey) -> P = split_topic_key(PatternKey), R = split_topic_key(RoutingKey), topic_matches1(P, R). topic_matches1(["#"], _R) -> true; topic_matches1(["#" | PTail], R) -> last_topic_match(PTail, [], lists:reverse(R)); topic_matches1([], []) -> true; topic_matches1(["*" | PatRest], [_ | ValRest]) -> topic_matches1(PatRest, ValRest); topic_matches1([PatElement | PatRest], [ValElement | ValRest]) when PatElement == ValElement -> topic_matches1(PatRest, ValRest); topic_matches1(_, _) -> false. last_topic_match(P, R, []) -> topic_matches1(P, R); last_topic_match(P, R, [BacktrackNext | BacktrackList]) -> topic_matches1(P, R) or last_topic_match(P, [BacktrackNext | R], BacktrackList).
看代码,一切都明了了,看下面的测试代码:
rabbit_exchange_type_topic:topic_matches
检验一下是否真的理解了,尝试回答一下Tutorials文档最后的几个变态问题:
- Will "*" binding catch a message sent with an empty routing key?
- Will "#.*" catch a message with a string ".." as a key? Will it catch a message with a single word key?
- How different is "a.*.#" from "a.#"?
最后,小图一张,晚安!