RabbitMq的Exchange(路由)官方文档:http://www.rabbitmq.com/getstarted.html(语言记得选Java)
路由概要说明:一个Exchange(路由)可以绑定多个Queue(队列),
根据不同的Exchange类型可以实现不同Exchange策略。
一、fanout
说明:把所有发送到该Exchange的消息路由到所有与它绑定的Queue中
代码如下:
package com.mq;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.net.URISyntaxException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.TimeoutException;
public class ProducerExchangeTest {
public static void main(String[] args) {
// 创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
// 连接
Connection connection = null;
// 通道
Channel channel = null;
try {
// amqp://用户名:密码@IP:端口号/虚拟服务器名称
factory.setUri("amqp://testuser:testuser@192.168.162.130:5672/my_test");
// 获取连接
connection = factory.newConnection();
System.out.println(connection);
// 创建通道
channel = connection.createChannel();
System.out.println(channel);
/**
* 创建一个队列
*
* queue 队列名称
* durable 是否申明持久队列,true:rabbitmq重启后继续存在
* exclusive 是否独占,ture:只有该连接能访问
* autoDelete 是否自动删除,true:服务器不再使用时,将自动删除该队列
* arguments 队列其它参数
*/
channel.queueDeclare("testQueue", true, false, false, null);
/**
* 创建路由
*
* exchange 路由名称
* type 路由类型,这里可以用string类型,我直接用了架包里自带的枚举类
* durable true:持久化操作,rabbitmq重启后,依然存在
* autoDelete true:自动删除,当没被使用时,自动删除该路由
* internal
* arguments 创建路由时的构造参数
*/
channel.exchangeDeclare("testExchange",BuiltinExchangeType.FANOUT,
true, false, false, null);
/**
* 队列绑定路由
*
* queue 队列名称
* exchange 路由名称
* routingKey 路由密钥
*/
channel.queueBind("testQueue","testExchange","");
String msg = "今天天气不错";
/**
* 推送消息
*
* exchange 路由
* routingKey 路由密钥
* props 消息的头信息
* body 消息主体
*/
channel.basicPublish("testExchange", "", null, msg.getBytes("UTF-8"));
} catch (URISyntaxException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
与之前的代码相比较,就加了两行代码,1创建Exchange,2将Exchange和Queue绑定。
运行以上代码之后,RabbitMq管理后台如下图所示:
testExchange内容如下:
testExchange已经成功绑定到testQueue。
一个Exchange可以绑定多个Queue,我这里只绑定了一个Queue。
二、direct
说明:把消息路由到bindingKey与routingKey完全匹配的Queue中
代码如下:
package com.mq;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.net.URISyntaxException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.TimeoutException;
public class ProducerExchangeTest {
public static void main(String[] args) {
// 创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
// 连接
Connection connection = null;
// 通道
Channel channel = null;
try {
// amqp://用户名:密码@IP:端口号/虚拟服务器名称
factory.setUri("amqp://testuser:testuser@192.168.162.130:5672/my_test");
// 获取连接
connection = factory.newConnection();
System.out.println(connection);
// 创建通道
channel = connection.createChannel();
System.out.println(channel);
/**
* 创建一个队列
*
* queue 队列名称
* durable 是否申明持久队列,true:rabbitmq重启后继续存在
* exclusive 是否独占,ture:只有该连接能访问
* autoDelete 是否自动删除,true:服务器不再使用时,将自动删除该队列
* arguments 队列其它参数
*/
channel.queueDeclare("queueDirect", true, false, false, null);
/**
* 创建路由
*
* exchange 路由名称
* type 路由类型,这里可以用string类型,我直接用了架包里自带的枚举类
* durable true:持久化操作,rabbitmq重启后,依然存在
* autoDelete true:自动删除,当没被使用时,自动删除该路由
* internal
* arguments 创建路由时的构造参数
*/
channel.exchangeDeclare("exchangeDirect",BuiltinExchangeType.DIRECT,
true, false, false, null);
/**
* 队列绑定路由
*
* queue 队列名称
* exchange 路由名称
* routingKey 路由密钥
*/
channel.queueBind("queueDirect","exchangeDirect","test.direct.my");
String msg = "今天天气不错";
/**
* 推送消息
*
* exchange 路由
* routingKey 路由密钥
* props 消息的头信息
* body 消息主体
*/
channel.basicPublish("exchangeDirect", "test.direct.my",
null, msg.getBytes("UTF-8"));
} catch (URISyntaxException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
创建Queue的方式不变,创建Channel时,将类型修改成direct,绑定和推送时,加上routingKey即可。
在推送消息的时候,把routingKey修改掉,比如:test.direct.*。
看看还能不能推送成功,显然queueDirect里并没有消息。
RabbitMq后台查看routingKey:
三、topic
说明:把消息路由到bindingKey与routingKey模糊匹配的Queue中
【*】表示匹配1个单词
【#】表示匹配0个或多个单词
(1)给张官方图例,方便理解
a.orange.rabbit能分发到Q1,Q2
a.orange.b只能分发到Q1
a.b.rabbit只能分发到Q2
lazy.a.b或者lazy.a只能分发到Q2
(2)下面是我自己写的,代码如下:
package com.mq;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.net.URISyntaxException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.TimeoutException;
public class ProducerExchangeTest {
public static void main(String[] args) {
// 创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
// 连接
Connection connection = null;
// 通道
Channel channel = null;
try {
// amqp://用户名:密码@IP:端口号/虚拟服务器名称
factory.setUri("amqp://testuser:testuser@192.168.162.130:5672/my_test");
// 获取连接
connection = factory.newConnection();
System.out.println(connection);
// 创建通道
channel = connection.createChannel();
System.out.println(channel);
/**
* 创建一个队列
*
* queue 队列名称
* durable 是否申明持久队列,true:rabbitmq重启后继续存在
* exclusive 是否独占,ture:只有该连接能访问
* autoDelete 是否自动删除,true:服务器不再使用时,将自动删除该队列
* arguments 队列其它参数
*/
channel.queueDeclare("queueTopic1", true, false, false, null);
/**
* 创建一个队列
*
* queue 队列名称
* durable 是否申明持久队列,true:rabbitmq重启后继续存在
* exclusive 是否独占,ture:只有该连接能访问
* autoDelete 是否自动删除,true:服务器不再使用时,将自动删除该队列
* arguments 队列其它参数
*/
channel.queueDeclare("queueTopic2", true, false, false, null);
/**
* 创建一个队列
*
* queue 队列名称
* durable 是否申明持久队列,true:rabbitmq重启后继续存在
* exclusive 是否独占,ture:只有该连接能访问
* autoDelete 是否自动删除,true:服务器不再使用时,将自动删除该队列
* arguments 队列其它参数
*/
channel.queueDeclare("queueTopic3", true, false, false, null);
/**
* 创建路由
*
* exchange 路由名称
* type 路由类型,这里可以用string类型,我直接用了架包里自带的枚举类
* durable true:持久化操作,rabbitmq重启后,依然存在
* autoDelete true:自动删除,当没被使用时,自动删除该路由
* internal
* arguments 创建路由时的构造参数
*/
channel.exchangeDeclare("exchangeTopic",BuiltinExchangeType.TOPIC,
true, false, false, null);
/**
* 队列绑定路由
*
* queue 队列名称
* exchange 路由名称
* routingKey 路由密钥
*/
channel.queueBind("queueTopic1","exchangeTopic","*.topic.*");
/**
* 队列绑定路由
*
* queue 队列名称
* exchange 路由名称
* routingKey 路由密钥
*/
channel.queueBind("queueTopic2","exchangeTopic","test.topic.*");
/**
* 队列绑定路由
*
* queue 队列名称
* exchange 路由名称
* routingKey 路由密钥
*/
channel.queueBind("queueTopic3","exchangeTopic","test.#");
String msg = "今天天气不错";
/**
* 推送消息
*
* queueTopic1,queueTopic2,queueTopic3队列能分发到
*/
channel.basicPublish("exchangeTopic", "test.topic.a",
null, msg.getBytes("UTF-8"));
/**
* 推送消息
*
* 只有queueTopic3队列能分发到
*/
// channel.basicPublish("exchangeTopic", "test.t.a",
// null, msg.getBytes("UTF-8"));
/**
* 推送消息
*
* 只有queueTopic1队列能分发到
*/
// channel.basicPublish("exchangeTopic", "a.topic.a",
// null, msg.getBytes("UTF-8"));
} catch (URISyntaxException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
我创建了3个队列,并分别绑定了不同的routingKey。通配符的使用,慢慢尝试吧,我代码里只列举了一部分。
四、headers
说明:headers类型的Exchange不依赖于routingKey与bindingKey的匹配规则来路由消息,
而是根据发送的消息内容中的headers属性进行匹配
注意:关于headers的官方说明,我找了几次都没找到,下面是我自己测试以及结合网络资料得出来的。
所以谨慎参考。
headers里有个key“x-match”,有两个取值:all(默认),any。
(1).all,匹配所有headers里的键值对,只有全部全部匹配才能成功推送消息。
匹配基准是Exchange绑定Queue时,写入的headers,我这里称之为bindHeaders。
推送消息时的headers,我这里称之为pushHeaders。
pushHeaders键值对要大于等于bindHeaders,才能成功推送。
如下图:
(2).any,就比较简单了,bindHeaders和pushHeaders只要有一个键值对匹配即可成功推送。
下面直接上代码:
package com.mq;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.net.URISyntaxException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;
public class ProducerExchangeTest {
public static void main(String[] args) {
// 创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
// 连接
Connection connection = null;
// 通道
Channel channel = null;
try {
// amqp://用户名:密码@IP:端口号/虚拟服务器名称
factory.setUri("amqp://testuser:testuser@192.168.162.130:5672/my_test");
// 获取连接
connection = factory.newConnection();
System.out.println(connection);
// 创建通道
channel = connection.createChannel();
System.out.println(channel);
/**
* 创建一个队列
*
* queue 队列名称
* durable 是否申明持久队列,true:rabbitmq重启后继续存在
* exclusive 是否独占,ture:只有该连接能访问
* autoDelete 是否自动删除,true:服务器不再使用时,将自动删除该队列
* arguments 队列其它参数
*/
channel.queueDeclare("queueHeaders", true, false, false, null);
/**
* 创建路由
*
* exchange 路由名称
* type 路由类型,这里可以用string类型,我直接用了架包里自带的枚举类
* durable true:持久化操作,rabbitmq重启后,依然存在
* autoDelete true:自动删除,当没被使用时,自动删除该路由
* internal
* arguments 创建路由时的构造参数
*/
channel.exchangeDeclare("exchangeHeaders",BuiltinExchangeType.HEADERS,
true, false, false, null);
// Exchange绑定Queue的头部
Map<String, Object> headers = new HashMap<>();
headers.put("x-match", "all"); // x-match:all(默认),any
headers.put("sign", "123456");
headers.put("a", "000");
/**
* 队列绑定路由
*
* queue 队列名称
* exchange 路由名称
* routingKey 路由密钥
*/
channel.queueBind("queueHeaders","exchangeHeaders","", headers);
String msg = "今天天气不错";
// 推送时的头部
Map<String, Object> pushHeaders = new HashMap<>();
pushHeaders.put("sign", "123456");
pushHeaders.put("a", "000");
AMQP.BasicProperties properties = new AMQP.BasicProperties().builder().headers(pushHeaders).build();
// 下面写法是错误的,不要踩坑
// AMQP.BasicProperties properties = new AMQP.BasicProperties();
// properties.builder().headers(pushHeaders).build();
/**
* 推送消息
*
* exchange 路由
* routingKey 路由密钥
* props 消息的头信息
* body 消息主体
*/
channel.basicPublish("exchangeHeaders", "",
properties, msg.getBytes("UTF-8"));
} catch (URISyntaxException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
我代码里只列举了all的用法,其它自行测试吧。运行之后,RabbitMq后台部分信息如下:
五、Exchange绑定Exchange
哈哈,没想到吧,路由还可以绑定路由,用法和绑定队列的用法类似。
下面我就贴一下简单的代码:
package com.mq;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.net.URISyntaxException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;
public class ProducerExchangeTest {
public static void main(String[] args) {
// 创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
// 连接
Connection connection = null;
// 通道
Channel channel = null;
try {
// amqp://用户名:密码@IP:端口号/虚拟服务器名称
factory.setUri("amqp://testuser:testuser@192.168.162.130:5672/my_test");
// 获取连接
connection = factory.newConnection();
System.out.println(connection);
// 创建通道
channel = connection.createChannel();
System.out.println(channel);
/**
* 创建路由
*
* exchange 路由名称
* type 路由类型,这里可以用string类型,我直接用了架包里自带的枚举类
* durable true:持久化操作,rabbitmq重启后,依然存在
* autoDelete true:自动删除,当没被使用时,自动删除该路由
* internal
* arguments 创建路由时的构造参数
*/
channel.exchangeDeclare("exchange1",BuiltinExchangeType.FANOUT,
true, false, false, null);
/**
* 创建路由
*
* exchange 路由名称
* type 路由类型,这里可以用string类型,我直接用了架包里自带的枚举类
* durable true:持久化操作,rabbitmq重启后,依然存在
* autoDelete true:自动删除,当没被使用时,自动删除该路由
* internal
* arguments 创建路由时的构造参数
*/
channel.exchangeDeclare("exchange2",BuiltinExchangeType.FANOUT,
true, false, false, null);
/**
* destination 目标路由
* source 来源路由
* routingKey 密钥
* argument 自定义参数
*
* 绑定方向:source->destination,即exchange2->exchange1
*/
channel.exchangeBind("exchange1", "exchange2", "", null);
} catch (URISyntaxException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
顺便也贴一下,运行成功之后的RabbitMq后台部分信息:
exchange2信息:
exchange1信息:
ok,结束。