【RabbitMQ——SpringBoot整合】

1. fanout模式

Fanout 交换器(Exchange)是 RabbitMQ 中的一种消息路由模式。它是一种广播式的交换器,会将接收到的消息发送给所有绑定到该交换器的队列,而不考虑路由键(routing key)。这意味着无论消息发布时指定了什么路由键,Fanout 交换器都会忽略这些信息,并简单地将消息复制并分发给所有的绑定队列。

1.1 配置类

java 复制代码
@Configuration
public class FanoutRabbitConfig {

    // 1. 声明注册fanout模式的交换机
    @Bean
    public FanoutExchange fanoutOrderExchange() {
        //  return new DirectExchange("TestDirectExchange",true,true);
        return new FanoutExchange("fanout_order_exchange", true, false);
    }
    // 2. 申明队列sms.fanout.queue  email.fanout.queue duanxin.fanout.queue
    @Bean
    public Queue fanoutEmailQueue() {
        // durable:是否持久化,默认是false,持久化队列:会被存储在磁盘上,当消息代理重启时仍然存在,暂存队列:当前连接有效
        // exclusive:默认也是false,只能被当前创建的连接使用,而且当连接关闭后队列即被删除。此参考优先级高于durable
        // autoDelete:是否自动删除,当没有生产者或者消费者使用此队列,该队列会自动删除。
        //   return new Queue("TestDirectQueue",true,true,false);
        //一般设置一下队列的持久化就好,其余两个就是默认false
        return new Queue("email.fanout.queue", true);
    }
    @Bean
    public Queue fanoutSmsQueue() {
        return new Queue("sms.fanout.queue", true);
    }
    @Bean
    public Queue fanoutWeixinQueue() {
        return new Queue("weixin.fanout.queue", true);
    }
    // 3. 完成绑定关系
    @Bean
    public Binding fanoutBindingDirect1() {
        return BindingBuilder.bind(fanoutEmailQueue()).to(fanoutOrderExchange());
    }
    @Bean
    public Binding fanoutBindingDirect2() {
        return BindingBuilder.bind(fanoutSmsQueue()).to(fanoutOrderExchange());
    }
    @Bean
    public Binding fanoutBindingDirect3() {
        return BindingBuilder.bind(fanoutWeixinQueue()).to(fanoutOrderExchange());
    }

}

1.2 生产者

java 复制代码
public void makeOrder(Long userId, Long productId, int num) {
    // 1: 模拟用户下单
    String orderNumer = UUID.randomUUID().toString();
    // 2: 根据商品id productId 去查询商品的库存
    // int numstore = productSerivce.getProductNum(productId);
    // 3:判断库存是否充足
    // if(num >  numstore ){ return  "商品库存不足..."; }
    // 4: 下单逻辑
    // orderService.saveOrder(order);
    // 5: 下单成功要扣减库存
    // 6: 下单完成以后
    System.out.println("用户 " + userId + ",订单编号是:" + orderNumer);
    // 发送订单信息给RabbitMQ fanout
    String exchangeName = "fanout_order_exchange";
    String routeKey = "";
    rabbitTemplate.convertAndSend(exchangeName, routeKey, orderNumer);
}

1.3 消费者

java 复制代码
// bindings其实就是用来确定队列和交换机绑定关系
@RabbitListener(queues = {"email.fanout.queue"})
@Component
public class FanoutEmailService {
    // @RabbitHandler 代表此方法是一个消息接收的方法。该不要有返回值
    @RabbitHandler
    public void messagerevice(String message){
        // 此处省略发邮件的逻辑
        System.out.println("fanout-email-------------->" + message);
    }
}
java 复制代码
// bindings其实就是用来确定队列和交换机绑定关系
@RabbitListener(queues = {"sms.fanout.queue"})
@Component
public class FanoutSMSService {
    // @RabbitHandler 代表此方法是一个消息接收的方法。该不要有返回值
    @RabbitHandler
    public void messagerevice(String message){
        // 此处省略发邮件的逻辑
        System.out.println("fanout-sms-------------->" + message);
    }
}
java 复制代码
@RabbitListener(queues = {"weixin.fanout.queue"})
@Component
public class FanoutWeixinService {

    // @RabbitHandler 代表此方法是一个消息接收的方法。该不要有返回值
    @RabbitHandler
    public void messagerevice(String message){
        // 此处省略发邮件的逻辑
        System.out.println("fanout-weixin-------------->" + message);
    }
}

2. direct模式

Direct 交换器根据消息的路由键(routing key)和队列绑定时指定的路由键进行精确匹配来决定消息的分发。如果一个队列绑定了带有特定路由键的 Direct 交换器,那么只有当发布到该交换器的消息携带相同的路由键时,消息才会被发送到这个队列。

2.1 配置类

注意配置类中声明的交换机的类型,以及队列携带的路由key

java 复制代码
@Configuration
public class DirectRabbitConfig {

    // 1. 声明注册fanout模式的交换机
    @Bean
    public DirectExchange directOrderExchange() {
        return new DirectExchange("direct_order_exchange", true, false);
    }
    // 2. 申明队列sms.fanout.queue  email.fanout.queue duanxin.fanout.queue
    @Bean
    public Queue directEmailQueue() {
        // durable:是否持久化,默认是false,持久化队列:会被存储在磁盘上,当消息代理重启时仍然存在,暂存队列:当前连接有效
        // exclusive:默认也是false,只能被当前创建的连接使用,而且当连接关闭后队列即被删除。此参考优先级高于durable
        // autoDelete:是否自动删除,当没有生产者或者消费者使用此队列,该队列会自动删除。
        //   return new Queue("TestDirectQueue",true,true,false);
        //一般设置一下队列的持久化就好,其余两个就是默认false
        return new Queue("email.direct.queue", true);
    }
    @Bean
    public Queue directSmsQueue() {
        return new Queue("sms.direct.queue", true);
    }
    @Bean
    public Queue directWeixinQueue() {
        return new Queue("weixin.direct.queue", true);
    }
    // 3. 完成绑定关系
    @Bean
    public Binding directBindingDirect1() {
        return BindingBuilder.bind(directWeixinQueue()).to(directOrderExchange()).with("weixin");
    }
    @Bean
    public Binding directBindingDirect2() {
        return BindingBuilder.bind(directSmsQueue()).to(directOrderExchange()).with("sms");
    }
    @Bean
    public Binding directBindingDirect3() {
        return BindingBuilder.bind(directEmailQueue()).to(directOrderExchange()).with("email");
    }

}

2.2 生产者

java 复制代码
    public void makeOrderDirect(Long userId, Long productId, int num) {
        // 1: 模拟用户下单
        String orderNumer = UUID.randomUUID().toString();
        // 2: 根据商品id productId 去查询商品的库存
        // int numstore = productSerivce.getProductNum(productId);
        // 3:判断库存是否充足
        // if(num >  numstore ){ return  "商品库存不足..."; }
        // 4: 下单逻辑
        // orderService.saveOrder(order);
        // 5: 下单成功要扣减库存
        // 6: 下单完成以后
        System.out.println("用户 " + userId + ",订单编号是:" + orderNumer);
        String exchangeName = "direct_order_exchange";
        rabbitTemplate.convertAndSend(exchangeName, "email", orderNumer);
        rabbitTemplate.convertAndSend(exchangeName, "weixin", orderNumer);
    }

2.3 消费者

java 复制代码
// bindings其实就是用来确定队列和交换机绑定关系
@RabbitListener(queues = {"email.direct.queue"})
@Component
public class DirectEmailService {
    // @RabbitHandler 代表此方法是一个消息接收的方法。该不要有返回值
    @RabbitHandler
    public void messagerevice(String message){
        // 此处省略发邮件的逻辑
        System.out.println("direct-email-------------->" + message);
    }
}
java 复制代码
// bindings其实就是用来确定队列和交换机绑定关系
@RabbitListener(queues = {"sms.direct.queue"})
@Component
public class DirectSMSService {
    // @RabbitHandler 代表此方法是一个消息接收的方法。该不要有返回值
    @RabbitHandler
    public void messagerevice(String message){
        // 此处省略发邮件的逻辑
        System.out.println("direct-sms-------------->" + message);
    }
}
java 复制代码
@RabbitListener(queues = {"weixin.direct.queue"})
@Component
public class DirectWeixinService {

    // @RabbitHandler 代表此方法是一个消息接收的方法。该不要有返回值
    @RabbitHandler
    public void messagerevice(String message){
        // 此处省略发邮件的逻辑
        System.out.println("direct-weixin-------------->" + message);
    }
}

3. topic模式

Topic 交换器是 RabbitMQ 中用于更灵活的消息路由模式。它结合了 Direct 交换器的基于路由键匹配的特点,但允许使用通配符进行部分匹配。这使得 Topic 交换器非常适合于实现动态且复杂的路由逻辑。

3.1 配置类

无,采用注解的方式

3.2 生产者

java 复制代码
    public void makeOrderTopic(Long userId, Long productId, int num) {
        // 1: 模拟用户下单
        String orderNumer = UUID.randomUUID().toString();
        // 2: 根据商品id productId 去查询商品的库存
        // int numstore = productSerivce.getProductNum(productId);
        // 3:判断库存是否充足
        // if(num >  numstore ){ return  "商品库存不足..."; }
        // 4: 下单逻辑
        // orderService.saveOrder(order);
        // 5: 下单成功要扣减库存
        // 6: 下单完成以后
        System.out.println("用户 " + userId + ",订单编号是:" + orderNumer);
        String exchangeName = "topic_order_exchange";
//        String routeKey = "jack.weixin.sss";
        String routeKey = "com.email.duanxin";
        rabbitTemplate.convertAndSend(exchangeName, routeKey, orderNumer);
    }

3.3 消费者

java 复制代码
// bindings其实就是用来确定队列和交换机绑定关系
@RabbitListener(bindings =@QueueBinding(
        // email.fanout.queue 是队列名字,这个名字你可以自定随便定义。
        value = @Queue(value = "email.topic.queue",autoDelete = "false"),
        // order.fanout 交换机的名字 必须和生产者保持一致
        exchange = @Exchange(value = "topic_order_exchange",
                // 这里是确定的rabbitmq模式是:fanout 是以广播模式 、 发布订阅模式
                type = ExchangeTypes.TOPIC),
        key = "#.email.#"
))
@Component
public class TopicEmailService {
    // @RabbitHandler 代表此方法是一个消息接收的方法。该不要有返回值
    @RabbitHandler
    public void messagerevice(String message){
        // 此处省略发邮件的逻辑
        System.out.println("topic-email-------------->" + message);
    }
}
java 复制代码
// bindings其实就是用来确定队列和交换机绑定关系
@RabbitListener(bindings =@QueueBinding(
        // email.fanout.queue 是队列名字,这个名字你可以自定随便定义。
        value = @Queue(value = "sms.topic.queue",autoDelete = "false"),
        // order.fanout 交换机的名字 必须和生产者保持一致
        exchange = @Exchange(value = "topic_order_exchange",
                // 这里是确定的rabbitmq模式是:fanout 是以广播模式 、 发布订阅模式
                type = ExchangeTypes.TOPIC),
        key = "#.sms.#"
))
@Component
public class TopicSMSService {
    // @RabbitHandler 代表此方法是一个消息接收的方法。该不要有返回值
    @RabbitHandler
    public void messagerevice(String message){
        // 此处省略发邮件的逻辑
        System.out.println("topic-sms-------------->" + message);
    }
}
java 复制代码
@RabbitListener(bindings =@QueueBinding(
        // email.fanout.queue 是队列名字,这个名字你可以自定随便定义。
        value = @Queue(value = "weixin.topic.queue",autoDelete = "false"),
        // order.fanout 交换机的名字 必须和生产者保持一致
        exchange = @Exchange(value = "topic_order_exchange",
                // 这里是确定的rabbitmq模式是:fanout 是以广播模式 、 发布订阅模式
                type = ExchangeTypes.TOPIC),
        key = "#.weixin.#"
))
@Component
public class TopicWeixinService {

    // @RabbitHandler 代表此方法是一个消息接收的方法。该不要有返回值
    @RabbitHandler
    public void messagerevice(String message){
        // 此处省略发邮件的逻辑
        System.out.println("topic-weixin-------------->" + message);
    }
}

4. 配置类VS注解

RabbitMQ 是一个开源的消息代理和队列服务器,用来实现应用程序之间的消息传递。在使用 RabbitMQ 时,配置类和注解是两种常见的配置方式。每种方式都有其优点和缺点。

4.1 配置类

  • 优点:
  1. 灵活性: 使用配置类可以非常灵活地定义复杂的队列、交换器、绑定等。
  2. 可重用性: 配置类可以被多个组件或服务复用。
  3. 易于管理: 当项目变得复杂时,配置类可以让配置项更加集中,易于管理和维护。
  4. 明确性: 配置类使得配置逻辑清晰可见,便于理解业务逻辑。
  5. 测试友好: 可以更容易地为配置类编写单元测试。
  • 缺点:
  1. 代码量: 相比于注解,配置类通常需要更多的代码来设置相同的配置。
  2. 学习曲线: 对于初学者来说,可能需要一些时间来掌握如何正确使用配置类进行配置。
  3. 分散注意力: 开发者需要在业务逻辑和服务配置之间切换,可能会稍微分散对主要业务的关注。

4.2 注解

优点:

  1. 简洁: 使用注解可以直接在类或方法上声明配置,减少模板代码,使代码更简洁。
  2. 快速开发: 对于简单的应用场景,注解可以帮助开发者快速完成配置,提高开发效率。
  3. 直观: 注解直接与相关的类或方法关联,使得配置意图一目了然。
  4. 集成良好: 在Spring框架中,注解方式可以很好地与Spring的其他功能(如AOP)结合使用。
    缺点:
  5. 扩展性: 当需求变更或系统规模扩大时,注解可能不足以应对复杂的配置需求。
  6. 隐式配置: 过多使用注解可能导致配置变得隐晦,不那么显而易见,这可能不利于后续的维护。
  7. 限制性: 注解提供的是预设好的配置选项,对于非标准配置支持不够灵活。
  8. 调试困难: 如果出现配置错误,注解方式可能不容易定位问题所在。
相关推荐
⑩-6 小时前
为什么要用消息队列?使用场景?
java·rabbitmq
菜鸟‍8 小时前
【后端项目】苍穹外卖day01-开发环境搭建
java·开发语言·spring boot
indexsunny9 小时前
互联网大厂Java求职面试实战:核心技术与业务场景解析
java·spring boot·redis·微服务·kafka·互联网大厂·面试技巧
程序猿大波9 小时前
基于java,SpringBoot和Vue餐饮公司食堂管理系统设计
java·vue.js·spring boot
wuyaolong00710 小时前
Spring Boot 3.4 正式发布,结构化日志!
java·spring boot·后端
a56299161911 小时前
【springboot】Spring 官方抛弃了 Java 8!新idea如何创建java8项目
java·spring boot·spring
编程小风筝13 小时前
如何用redission实现springboot的分布式锁?
spring boot·分布式·后端
码喽7号14 小时前
Springboot学习六:MybatisPlus的多表查询以及分页查询
java·spring boot·学习
不吃香菜学java14 小时前
苍穹外卖-新增菜品需求分析
java·spring boot·spring·tomcat·maven·ssm