rabbmitMQ的安装这边就不赘述了.这里有用docker安装的链接,需要的兄弟可以了解一下docker安装rabbmitMQ
使用springboot来操作rabbmitMQ还是挺方便的,这边就把常用的rabbmitMQ功能记录一下.以后可以拿来复习
搭建好的rabbmitMQ登录进去之后是这样,其中amq开头的是其默认的交换机,我们可以不用管他
我这边搭建了个项目,在此只拿出关于rabbmitMQ相关的内容展示,有三个模块,分别是消费者,生产者,和公共模块
接下来.主要是说三个交换机,这也是项目中经常用到的,direct,topic,fanout这三种
公共模块中主要是有常量. 这边先把这些常量拿出来,因为一下都会用到
/**
* 交换机名称
*/
public class ExchangeConstanct {
public final static String DIRECT="direct";
public final static String TOPIC="topic";
public final static String FANOUT="fanout";
}
/**
* 队列名称
*/
public class QueueNameConstanct {
public final static String DIRECT="direct";
public final static String TOPIC="topic";
public final static String TOPIC1="topic.*";
public final static String TOPIC2="topic.*.*";
public final static String TOPICALL="topic.#";
public final static String FANOUT="fanout";
public final static String FANOUT2="fanout2";
}
/**
* 路由键 接收方
*/
public class RoutingKeyReceiver {
public final static String DIRECT="direct";
public final static String TOPIC="topic";//以topic全词匹配
public final static String TOPIC1="topic.*";//topic开头 一个模糊匹配
public final static String TOPIC2="topic.*.*";//topic开头 两个模糊匹配
public final static String TOPICALL="topic.#";//topic开头 0或者多个模糊匹配
}
/**
* 路由键 发送方
*/
public class RoutingKeySend {
public final static String DIRECT="direct";
public final static String TOPIC="topic";
public final static String TOPIC1="topic.a";
public final static String TOPIC2="topic.a.b";
public final static String TOPIC3="topic.a.b.c";
}
在生产者和消费者中添加连接rabbmitMQ的配置
spring:
rabbitmq:
host: 192.168.234.128
port: 5672
username: guest
password: guest
消费方和生产者都需要加入的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
接下来就开始了,生产者只定义交换机,发送消息,消费者负责创建队列,并绑定交换机
第一种:direct类型的交换机
这个交换机的特点是 点对点的,也就是说完全根据路由键来进行匹配,
比如A队列通过A路由键绑定到了C交换机上,B队列通过B路由键绑定到C交换机上,那么当生产者通过A路由键发送消息到C交换机的时候,只有A队列的监听者能收到,B队列的监听者是收不到的
生产者:创建Direct类型的交换机
@Configuration
public class DirectRabbitConfig {
@Bean //创建交换机
public DirectExchange directExchange() {
return new DirectExchange(ExchangeConstanct.DIRECT, true, false);
}
}
添加controller进行测试
@RestController
public class DirectProdController {
@Autowired
private AmqpTemplate amqpTemplate;
@PostMapping("directProd")
public JsonResult<String> directProd(String msg) {
amqpTemplate.convertAndSend(ExchangeConstanct.DIRECT, RoutingKeySend.DIRECT, msg);
return JsonResult.success(msg);
}
}
消费方:创建队列,并且通过路由键将队列和交换机绑定
@Configuration
public class DirectRabbitConfig {
@Bean //创建队列
public Queue directQueue() {
return new Queue(QueueNameConstanct.DIRECT, true);
}
@Bean //创建交换机
public DirectExchange directExchange() {
return new DirectExchange(ExchangeConstanct.DIRECT, true, false);
}
@Bean //交换机和队列进行绑定
public Binding directBinding() {
return BindingBuilder.bind(directQueue()).to(directExchange()).with(RoutingKeyReceiver.DIRECT);
}
}
消费方创建监听
@Component
public class DirectReceiver {
@RabbitListener(queues = QueueNameConstanct.DIRECT)
public void directReceiver(String msg) {
System.out.println(msg + "DIRECT");
}
/* @RabbitHandler
这个注解可以和 @RabbitListener配合使用,将 @RabbitListener标注在类上,
@RabbitHandler标注在方法上,该注解可以根据参数类型进行匹配,
如果有两个或两个以上参数类型的方法相同的时候, 用这个注解会造成死循环,
抛出这个异常Listener method 'no match' threw exception ,死循环的抛, 切记
比如还有一个方法也是String类型的参数如下
public void directReceiver2(String msg){
System.out.println(msg+"str");
}*/
}
==================================================================================
第二种:fanout交换机
fanout交换机不需要指定路由键, 比如说A队列绑定了A交换机,B队列绑定了A交换机,C队列绑定了A交换机,那么当生产者通过A交换机发送消息的时候,A,B,C三个队列都能够收到
生产者: 创建Fanout类型交换机
@Configuration
public class FanoutRabbitConfig {
@Bean //创建交换机
public FanoutExchange fanoutExchange() {
return new FanoutExchange(ExchangeConstanct.FANOUT, true, false);
}
}
添加controller进行测试
@RestController
public class FanoutProdController {
@Autowired
private AmqpTemplate amqpTemplate;
@PostMapping("fanoutProd")
public JsonResult<String> fanoutProd(String msg) {
amqpTemplate.convertAndSend(ExchangeConstanct.FANOUT, null, msg);
return JsonResult.success(msg);
}
}
消费方:创建队列,并且将队列和交换机绑定,不需要路由键
@Configuration
public class FanoutRabbitConfig {
@Bean //创建队列
public Queue fanoutQueue() {
return new Queue(QueueNameConstanct.FANOUT, true);
}
@Bean //创建队列
public Queue fanoutQueue2() {
return new Queue(QueueNameConstanct.FANOUT2, true);
}
@Bean //创建交换机
public FanoutExchange fanoutExchange() {
return new FanoutExchange(ExchangeConstanct.FANOUT, true, false);
}
@Bean //交换机和队列进行绑定 不需要路由键 只要是监听了绑定的是这个种交换机的队列 都能收到消息
public Binding fanoutBinding() {
return BindingBuilder.bind(fanoutQueue()).to(fanoutExchange());
}
@Bean //交换机和队列进行绑定 不需要路由键 只要是监听了绑定的是这个种交换机的队列 都能收到消息
public Binding fanoutBinding2() {
return BindingBuilder.bind(fanoutQueue2()).to(fanoutExchange());
}
}
消费方创建监听
@Component
public class FanoutReceiver {
@RabbitListener(queues = QueueNameConstanct.FANOUT)
public void fanoutReceiver(String msg) {
System.out.println(msg + "FANOUT");
}
@RabbitListener(queues = QueueNameConstanct.FANOUT2)
public void fanoutReceiver2(String msg) {
System.out.println(msg + "FANOUT2");
}
}
============================================================================
第三种:topic类型的交换机
生产者:创建topic类型的交换机
@Configuration
public class TopicRabbitConfig {
@Bean //创建交换机
public TopicExchange topicExchange() {
return new TopicExchange(ExchangeConstanct.TOPIC, true, false);
}
}
添加controller进行测试
@RestController
public class TopicProdController {
@Autowired
private AmqpTemplate amqpTemplate;
@PostMapping("topicProd") //只匹配 路由键是topic的队列
public JsonResult<String> topicProd(String msg) {
amqpTemplate.convertAndSend(ExchangeConstanct.TOPIC, RoutingKeySend.TOPIC, msg);
return JsonResult.success(msg);
}
@PostMapping("topicProd1")//发送消息,绑定的路由键是topic.a
public JsonResult<String> topicProd1(String msg) {
amqpTemplate.convertAndSend(ExchangeConstanct.TOPIC,RoutingKeySend.TOPIC1, msg);
return JsonResult.success(msg);
}
@PostMapping("topicProd2")//发送消息,绑定的路由键是topic.a.b
public JsonResult<String> topicProd2(String msg) {
amqpTemplate.convertAndSend(ExchangeConstanct.TOPIC,RoutingKeySend.TOPIC2, msg);
return JsonResult.success(msg);
}
@PostMapping("topicProd3")//发送消息,绑定的路由键是topic.a.b.c
public JsonResult<String> topicProd3(String msg) {
amqpTemplate.convertAndSend(ExchangeConstanct.TOPIC,RoutingKeySend.TOPIC3, msg);
return JsonResult.success(msg);
}
}
消费方:创建队列,并且通过路由键将队列和交换机绑定,以下提到的 * 代表一个单词,# 代表大于等于0个单词
@Configuration
public class TopicRabbitConfig {
@Bean //创建队列全词匹配
public Queue topicQueue() {
return new Queue(QueueNameConstanct.TOPIC, true);
}
@Bean //创建队列 一个模糊匹配
public Queue topicQueue1() {
return new Queue(QueueNameConstanct.TOPIC1, true);
}
@Bean //创建队列 2个模糊匹配
public Queue topicQueue2() {
return new Queue(QueueNameConstanct.TOPIC2, true);
}
@Bean //创建队列 0个或者多个模糊匹配
public Queue topicQueueALL() {
return new Queue(QueueNameConstanct.TOPICALL, true);
}
@Bean //创建交换机
public TopicExchange topicExchange() {
return new TopicExchange(ExchangeConstanct.TOPIC, true, false);
}
@Bean //交换机和队列进行绑定,路由键等于topic的消息, topicQueue这个队列都能收到
//比如发送方发了消息,路由键为topic这个都可以收到
public Binding topicBinding() {
return BindingBuilder.bind(topicQueue()).to(topicExchange()).with(RoutingKeyReceiver.TOPIC);
}
@Bean //交换机和队列1进行绑定,路由键以topic.* 格式的消息, topicQueue1这个队列都能收到
// 比如发送方发了消息,路由键为topic.a或者topic.b等等... 这个都可以收到
public Binding topicBinding1() {
return BindingBuilder.bind(topicQueue1()).to(topicExchange()).with(RoutingKeyReceiver.TOPIC1);
}
@Bean //交换机和队列2进行绑定,路由键以topic.*.* 的格式的消息, topicQueue2这个队列都能收到
// 比如发送方发了消息,路由键为topic.a.b或者topic.b.c等等... 这个都可以收到
public Binding topicBinding2() {
return BindingBuilder.bind(topicQueue2()).to(topicExchange()).with(RoutingKeyReceiver.TOPIC2);
}
@Bean //交换机和队列all进行绑定,路由键以topic.# 格式的消息,也就是说,路由键以topic开头的, topicQueueALL这个队列都能收到
// 比如发送方发了消息,路由键为topic或者topic.a,或者topic.a.b,或者topic.a.b.c等等... 这个都可以收到
public Binding topicBindingAll() {
return BindingBuilder.bind(topicQueueALL()).to(topicExchange()).with(RoutingKeyReceiver.TOPICALL);
}
}
消费方创建监听
@Component
public class TopicReceiver {
//监听和路由键为topic绑定队列
@RabbitListener(queues = QueueNameConstanct.TOPIC)
public void topicReceiver(String msg) {
System.out.println(msg + "监听和路由键为topic绑定队列");
}
//监听和路由键为topic.*绑定队列
@RabbitListener(queues = QueueNameConstanct.TOPIC1)
public void topicReceiver1(String msg) {
System.out.println(msg + "监听和路由键为topic.*绑定队列");
}
//监听和路由键为topic.*.*绑定队列
@RabbitListener(queues = QueueNameConstanct.TOPIC2)
public void topicReceiver2(String msg) {
System.out.println(msg + "监听和路由键为topic.*.*绑定队列");
}
//监听和路由键为topic.#绑定队列
@RabbitListener(queues = QueueNameConstanct.TOPICALL)
public void topicReceiverAll(String msg) {
System.out.println(msg + "监听和路由键为topic.#绑定队列");
}
}
=================================================================================
总结一下:
生产方:创建交换机,发送消息
消费方:创建交换机,创建队列,绑定队列和交换机,创建接收监听
之所以生产者和消费者都创建交换机,主要是为了保证,生产者一方启动了之后,如果消费者没有启动,此时还没有交换机,发送消息会报错,内容如下:
2020-05-07 13:53:29.738 ERROR 17928 --- [68.234.128:5672] o.s.a.r.c.CachingConnectionFactory : Channel shutdown: channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no exchange 'direct' in vhost '/', class-id=60, method-id=40)
而消费方也创建是为了绑定用的,并且如果已经有了该名称的交换机的时候,也不会重复创建
测试结果,这边就不往上贴了,有兴趣可以自己试一下