RabbitMQ
一、核心概念和组件
1、Broker
- 一个RabbitMQ实例就是一个Broker
2、Virtual Host
- 虚拟主机。相当于Mysql的Database,一个Broker上可以存在多个vhost,vhost之间可以相互隔离。每个vhost都有自己的队列、交换机、绑定和权限机制。vhost必须在连接时指定,默认的vhost是/
3、Exchange
- 负责接收生产者发送的消息,并将消息路由到Queue。Exchange可以通过不同的类型和配置来实现不同的路由逻辑,常见的Exchange类型有:Direct、Topic、Headers、Fanout
4、Queue
- 负责存储消息,并将消息传递给消费者。Queue可以通过不同的配置来实现不同的消息处理逻辑,例如:消息持久化、消息优先级、消息抵消等
5、Binding
- 负责将Exchange和Queue连接起来,实现消息路由。Binging可以通过配置Exchange和Queue之间的关系,实现不同的路由逻辑
6、Message
- 实际发送的消息内容,可以是任何可以被序列化的数据,例如:字符串、对象、文件等
7、Connection
- 生产者/消费者与broker之间的TCP连接
8、Channel
- 管道,一条双向数据流通道。不管是发布消息、订阅队列还是接收消息,这些动作都是通过管道完成。因为对于操作系统来说,建立和销毁TCP都是非常昂贵的开销,所以引入管道的概念,以复用一条TCP连接
二、Exchange
<一>、fanout(广播)
- 路由规则非常简单,不需要处理RouteKey,会把所有发送到fanout Exchange的消息都会被转发到与该Exchange绑定(Binding)的所有Queue上
声明Exchange
java
channel.exchangeDeclare("exchange_fanout", "fanout");
//将消息发送给exchange
channel.basicPublish("exchange_fanout","",null,msg.getBytes);
声明临时队列(Temporary queues)
- 一个non_durable(不持久化的)、exclusive(单独的)、autodelete(随着消费者的消亡自动删除)、random(随机命名)的队列
java
String queueName = channel.queueDeclare().getQueue();
绑定(Bindings)
- 绑定queue和exchange
java
channel.queueBind("队列名","exchange名","");
<二>、Direct
- exchange和queue通过routeKey绑定,消息传递时,RouteKey必须完全匹配,才会被队列接收,否则该消息会被抛弃
声明Exchange
java
channel.exchangeDeclare("exchange_direct", "direct");
发布消息
- 发布消息时带上RouteKey
java
channel.basicPublish("exchange_direct","routeKey_direct",null,msg.getBytes);
绑定(Bindings)
- 绑定queue和exchange,可以带一个RouteKey参数,表示队列只对该routeKey的消息感兴趣
java
channel.queueBind(queueName,exchangeName,RouteKey);
<三>、topic
direct类型的Exchange路由规则是完全匹配binding Key与RouteKey,但这种严格的匹配方式在很多情况下不能满足实际业务需求,topic类型的exchange在匹配规则上进行了扩展,它约定:
- a)、routekey为一个句点号"."分隔的字符串,如"stock.usd.nyse"、"nyse.vmw"、"quick.orange.rabbit"
- b)、binding key与routekey一样也是句点号"."分隔的字符串
- c)、binding key可以存在两种特殊字符""与"#",用于做模糊匹配,其中'"'用于匹配一个单词,"#"用于匹配多个单词(可以是零个)
所有发送到Topic Exchange的消息被转发到所有关心RouteKey指定Topic的Queue上,声明多个RouteKey,同时声明多个Binding Key进行模糊匹配,比如"log.#"能够匹配到"log.info.oa",但是"log.",只会匹配到log.error
声明Exchange
java
channel.exchangeDeclare("exchange_topic", "topic", false, true, null);
声明RouteKey
java
private final static String ROUTING_KEY = "log.info";
发布消息
- 发布消息时带上RouteKey
java
channel.basicPublish("exchange_topic", ROUTING_KEY, null, msg.getBytes);
绑定(Bindings)
- 绑定queue和exchange,可以带一个RouteKey参数,表示队列只对该routeKey的消息感兴趣
- 此时消费者的RouteKey可以带上通配符
java
private final static String ROUTING_KEY = "log.#";
java
channel.queueBind(queueName,exchangeName,ROUTING_KEY );
<四>、headers
- headers类型的exchange不依赖于routekey与binding key的匹配规则来路由消息,而是根据发送的消息内容中的headers属性进行匹配
- 在绑定queue与exchange时指定一组键值对;当消息发送到Exchange时,rabbitmq会取到该消息的headers(也是一个键值对的形式),对比其中的键值对是否完全匹配Queue与Exchange绑定时指定的键值对;如果完全匹配则消息会路由到该Queue,否则不会路由到该Queue
fanout和headers类型都不需要路由键routeKey,交换时通过Headers头部来将消息映射到队列的,Hash结构中要求携带一个键"x-match",这个键的Value可以是Any或者All,这代表消息携带的Hash是需要全部匹配(all),还是仅匹配一个键Any就可以了,相比直连交换机,首部交换机的优势是匹配的规则不被限定为字符串(string)而是Object类型
- any:只要在发布消息时携带的有一对键值对headers满足队列定义的多个参数arguments的其中一个就能匹配上,注意这里是键值对的完全匹配,只匹配到键了,值却不一样是不行的
- all:在发布消息时携带的所有Entry必须和绑定在队列上的所有Entry完全匹配
生产者:
声明Exchange
java
channel.exchangeDeclare("exchange_topic", "headers");
声明键值对
java
Map<String, Object> heardersMap = new HashMap<String, Object>();
heardersMap.put("api", "login");
heardersMap.put("version", 1.0);
heardersMap.put("radom", UUID.randomUUID().toString());
BasicProperties.Builder properties = new BasicProperties().builder().headers(heardersMap);
发布消息
- 发布消息时带上键值对
java
channel.basicPublish(EXCHANGE_NAME, "", properties.build(), msg.getBytes());
消费者:
声明键值对
java
Map<String, Object> arguments = new HashMap<String, Object>();
arguments.put("x-match", "any");
arguments.put("api", "login");
arguments.put("version", 1.0);
arguments.put("dataType", "json");
绑定(Bindings)
- 绑定queue和exchange,无需路由键RouteKey,但是仍然不能写成null,需要写成空字符串
java
channel.queueBind(queueName, EXCHANGE_NAME, "", arguments);
java
// all:匹配失败,缺少{"dataType", "json"}
Map<String, Object> heardersMap = new HashMap<String, Object>();
heardersMap.put("api", "login");
heardersMap.put("version", 1.0);
// all:匹配成功,生产者多发送一个head没关系
Map<String, Object> heardersMap = new HashMap<String, Object>();
heardersMap.put("api", "login");
heardersMap.put("version", 1.0);
heardersMap.put("dataType", "json");
heardersMap.put("ext", false);
Map<String, Object> arguments = new HashMap<String, Object>();
arguments.put("x-match", "all");
arguments.put("api", "login");
arguments.put("version", 1.0);
arguments.put("dataType", "json");
//------------------------------------------
// any: 匹配成功,只要有一个键值对能满足队列的arguments即可
Map<String, Object> heardersMap = new HashMap<String, Object>();
heardersMap.put("api", "login");
Map<String, Object> arguments = new HashMap<String, Object>();
arguments.put("x-match", "any");
arguments.put("api", "login");
arguments.put("version", 1.0);
arguments.put("dataType", "json");
// any: 匹配失败,键值对中的key和value必须全部匹配上
Map<String, Object> heardersMap = new HashMap<String, Object>();
heardersMap.put("api", "regist");
Map<String, Object> arguments = new HashMap<String, Object>();
arguments.put("x-match", "any");
arguments.put("api", "login");
arguments.put("version", 1.0);
arguments.put("dataType", "json");