springboot使用rabbmitMQ

本文详细介绍了如何使用SpringBoot操作RabbitMQ,包括直接、主题和扇形三种交换机的配置与使用方法,以及如何创建队列、绑定交换机和实现消息监听。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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)

而消费方也创建是为了绑定用的,并且如果已经有了该名称的交换机的时候,也不会重复创建

测试结果,这边就不往上贴了,有兴趣可以自己试一下

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值