消息中间件RabbitMQ学习笔记

1.简介

RabbitMQ是实现了高级消息队列协议(AMQP)的开源消息代理软件(亦称面向消息的中间件)。RabbitMQ服务器是用Erlang语言编写的,基于AMQP协议实现,支持多种场景,高性能,高可用,支持海量数据。
rabbitmq 的一些概念,需要了解的话 点这里

2.使用

1.依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
            <!--版本跟你项目springboot保持一致即可-->
        </dependency>

2.客户端

可以下载rabbitmq:management,可以通过它图形化使用rabbitmq,具体使用教程百度上挺详细的,我就不详解了

3.配置信息

1.application.properties 文件中加上以下配置信息
	spring.rabbitmq.host=你的IP
	spring.rabbitmq.port=5672
	spring.rabbitmq.virtual-host=/

2.启动类上或者配置类上加 @EnableRabbit

3.监听消息 :使用@RabbitListener 必须要有@EnableRabbit
   @RabbitListener:类+方法上 (监听那些队列)
   @RabbitHandler: 方法上 (重载区分不同类型的消息)
   
4.引入amqp场景 RabbitAutoConfiguration 就会自动生效
* 给容器中自动配置了RabbitTemplate , AmqpAdmin,CachingConnectionFactory 
* RabbitMessagingTemplate所有的属性都在这
       @EnableConfigurationProperties({RabbitProperties.class})

4. 代码实现

1.发送消息,创建 Exchange,Queue,Binding测试

@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class GulimallOrderApplicationTests {

    @Autowired
    AmqpAdmin amqpAdmin;
    @Autowired
    RabbitTemplate rabbitTemplate;

    @Test
    public void sendMessageTest(){
        OrderReturnReasonEntity reasonEntity = new OrderReturnReasonEntity();
        reasonEntity.setId(1L);
        reasonEntity.setCreateTime(new Date());
        reasonEntity.setName("哈哈");
        reasonEntity.setSort(1);

        //1.发送消息 如果发送的消息是个对象,我们会使用序列化机制,将对象写出去。对象必须实现序列化接口
        String message = "hello world";
        rabbitTemplate.convertAndSend("hello-java-exchange","hello.java", reasonEntity);
        log.info("消息发送完成{}",reasonEntity);

    }


    /*
    *1.如何创建 Exchange,Queue,Binding
    *   1) 使用AmqpAdmin
    *2.如何收发消息
    */
    @Test
    public void createExchange(){
        DirectExchange directExchange = new DirectExchange("hello-java-exchange",true,false);
        amqpAdmin.declareExchange(directExchange);
        log.info("Exchange[{}]创建成功",directExchange.getName());
    }

    @Test
    public void createQueue(){
        Queue queue = new Queue("hello-java-queue", true, false, false);
        amqpAdmin.declareQueue(queue);
        log.info("Queue[{}]创建成功",queue.getName());
    }

    @Test
    public void createBinding(){
        //String destination【目的地】,
        // Binding.DestinationType destinationType【目的地类型】
        // String exchange【交换机】,
        // String routingKey【路由键】
        // Map<String, Object> arguments)【自定义参数】
        //将exchange指定的交换机和destination
        Binding binding = new Binding("hello-java-queue",
                Binding.DestinationType.QUEUE,
                "hello-java-exchange",
                "hello.java",
                null);
        amqpAdmin.declareBinding(binding);
        log.info("Binding[{}]创建成功","hello-java-binding");
    }

2.rabbit 配置

@Configuration
public class MyRabbitConfig {

//    @Autowired
    RabbitTemplate rabbitTemplate;
//    public MyRabbitConfig(RabbitTemplate rabbitTemplate){
//        this.rabbitTemplate = rabbitTemplate;
//        initRabbitTemplate();
//    }

    //TODO
    @Primary
    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        this.rabbitTemplate = rabbitTemplate;
        rabbitTemplate.setMessageConverter(messageConverter());
        initRabbitTemplate();
        return rabbitTemplate;
    }

    @Bean
    public MessageConverter messageConverter(){
        return new Jackson2JsonMessageConverter();
    }

    /*
    * 定制RabbitTemplate
    * 1.服务端收到消息就回调
    *        1.spring.rabbitmq.publisher-confirms=true
    *        2.设置确认回调
    * 2.消息正确抵达队列就进行回调
    *         1.spring.rabbitmq.publisher-returns=true
    *         2.spring.rabbitmq.template.mandatory=true
    * 3.消费端确认(保证每个消息被正确消费,此时才可以broker删除这个消息)
    *       spring.rabbitmq.listener.simple.acknowledge-mode=manual
    *       1.默认是自动确认的,只要有消息接收到,客户端自动确认,服务端就会移除这个消息
    *           问题:
    *               我们收到很多消息,自动回复给服务器ack,只有一个消息处理成功,宕机了 会发生消息丢失
    *           消费者 手动确认模式:只要我们没有手动告诉MQ 消息已被接收 没有ack 消息就一直是unacked 即使Consumer宕机 消息也不会丢失
    *           会重新变为ready 下一次有新的Consumer进来 就发给他
     */
//    @PostConstruct  //MyRabbitConfig 对象创建完成以后执行这个方法
    public void initRabbitTemplate(){
        //设置确认回调
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            /**
            * @Description: confirm
            * @Param: correlationData 当前消息的唯一关联数据(这个消息的唯一id)
            * @Param: b 消息是否成功收到
            * @Param: cause 失败的原因
            */
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String s) {
                //1.做好消息确认机制(publisher consumer[手动ack])
                //2.每一个发送的消息都在数据库做好记录.定期将失败的消息再次发送一遍
                //服务器收到了
                System.out.println("confirm....correlationData["+correlationData+"]====>b["+ack+"]s=========>["+s+"]");
            }
        });

        //设置消息抵达队列确认回调
        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            /**
            * @Description: returnedMessage 只要消息投递失败就会触发这个回调
            * @Param: message 投递失败的消息详细信息
            * @Param: i 回复的状态码
            * @Param: s 回复的文本内容
            * @Param: s1 当时这个消息发给那个交换机
            * @Param: s2 当时这个消息用的那个路由键
            */
            @Override
            public void returnedMessage(Message message, int i, String s, String s1, String s2) {
                //报错误了 修改数据库
                System.out.println("Fail Message["+message+"]===>replyCode["+i+"]====>s"+s+"===>s1["+s1+"]=====>s2["+s2+"]");
            }
        });
    }

}

3队列 交换机等创建

@Configuration
public class MyMQConfig {



    /*
    * 容器中的 Binding , Queue , Exchange 都会自动创建(RabbitMQ没有的情况)
    * RabbitMQ 只要创建好了  就算属性发生变化也不会覆盖
    */
    @Bean
    public Queue orderDelayQueue(){
        Map<String,Object> arguments = new HashMap<>();
        arguments.put("x-dead-letter-exchange","order-event-exchange");
        arguments.put("x-dead-letter-routing-key","order.release.order");
        arguments.put("x-message-ttl",60000);
        Queue queue = new Queue("order.delay.queue", true, false, false,arguments);
        return queue;
    }

    @Bean
    public Queue orderReleaseOrderQueue(){
        Queue queue = new Queue("order.release.order.queue", true, false, false);
        return queue;
    }

    @Bean
    public Exchange orderEventExchange(){
        TopicExchange exchange = new TopicExchange("order-event-exchange",true,false);
        return exchange;
    }

//    @Bean
//    public Exchange orderReleaseOrderExchange(){
//        TopicExchange exchange = new TopicExchange("order-event-exchange",true,false);
//        return exchange;
//    }

    @Bean
    public Binding orderCreateOrderBinding(){
        return new Binding("order.delay.queue",
                Binding.DestinationType.QUEUE,
                "order-event-exchange",
                "order-create-order",
                null);
    }

    @Bean
    public Binding orderReleaseOrderBinding(){
        return new Binding("order.release.order.queue",
                Binding.DestinationType.QUEUE,
                "order-event-exchange",
                "order.release.order",
                null);
    }

    //订单释放直接和库存释放进行绑定
    @Bean
    public Binding orderReleaseOtherBingding(){
        return new Binding("stock.release.stock.queue",
                Binding.DestinationType.QUEUE,
                "order-event-exchange",
                "order.release.other.#",
                null);
    }

    @Bean
    public Queue orderSeckillOrderQueue(){
        Queue queue = new Queue("order.seckill.order.queue", true, false, false);
        return queue;
    }

    @Bean
    public Binding orderSeckillOrderQueueBingding(){
        return new Binding("order.seckill.order.queue",
                Binding.DestinationType.QUEUE,
                "order-event-exchange",
                "order.seckill.order",
                null);
    }

}

消费端代码举例


@Service
@RabbitListener(queues = "stock.release.stock.queue")
public class StockReleaseListener {

    @Autowired
    WareSkuService wareSkuService;

    /*
     *1.库存自动解锁
     * 下单成功后,锁定库存.接下来的业务调用失败,导致订单回滚
     *       之前锁定的库存就要自动解锁
     * 2.订单失败
     *   锁库存失败引起的
     *
     * 只要解锁库存的消息失败,一定要告诉服务器不要删,重试
     */
    @RabbitHandler
    public void handleStockLockedRelease(StockLockedTo to, Message message, Channel channel) throws IOException {
        System.out.println("收到解锁库存的消息");
        try {
            //当前消息是否被第二次及以后重新派发过来的
//            Boolean redelivered = message.getMessageProperties().getRedelivered();
            wareSkuService.unLockStock(to);
            // 手动确认
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        }catch (Exception e){
           //告诉服务器 消息未被成功处理  该消息会重新进入队列 变成ready 模式
            channel.basicReject(message.getMessageProperties().getDeliveryTag(), true);
        }

    }

    @RabbitHandler
    public void handleStockLockedRelease(OrderTo orderTo, Message message, Channel channel) throws IOException {
        System.out.println("订单关闭,准备解锁库存");
        try {
            wareSkuService.unLockStock(orderTo);
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        }catch (Exception e){
            channel.basicReject(message.getMessageProperties().getDeliveryTag(), true);
        }

    }


}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值