百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分类 > 正文

快速尝鲜:RabbitMQ 搭建完就得用起来

ztj100 2024-11-23 00:04 19 浏览 0 评论

在项目真正开始之前我们先来简单介绍下 RabbitMQ 的工作流程:

  • 生产者往交换机中发送消息;
  • 交换机通过规则绑定队列,通过路由键将消息存储到队列中;
  • 消费者获取队列中的消息进行消费;

环境:SpringBoot 2.6.3、JDK 1.8

项目搭建

首先创建 SpringBoot 项目 rabbit-mq

  1. 引入依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

复制代码

  1. yml 文件配置
spring:
  rabbitmq:
    host: 127.0.0.1     //rabbitMQ服务地址
    port: 15672   //这个地方暂时先用我们之前配置的15672
    username: cheetah   //自己的账户名
    password: 123456    //自己的密码

复制代码

  1. 直连交换机

本项目以直连交换机为例,至于其他的交换机类型将在后文中给出详细介绍。

@Configuration
public class DirectRabbitConfig {


    /**
     * 定义交换机
     **/
    @Bean
    public DirectExchange directExchange(){
        /**
         * 交换机名称
         * 持久性标志:是否持久化,默认是 true 即声明一个持久的 exchange,该exchange将在服务器重启后继续运行
         * 自动删除标志:是否自动删除,默认为 false, 如果服务器想在 exchange不再使用时删除它,则设置为 true
         **/
        return new DirectExchange("directExchange", true, false);
    }


    /**
     * 定义队列
     **/
    @Bean
    public Queue directQueue(){
        /**
         * name:队列名称
         * durable:是否持久化,默认是 true,持久化队列,会被存储在磁盘上,当消息代理重启时仍然存在
         * exclusive:是否排他,默认为 false,true则表示声明了一个排他队列(该队列将仅由声明者连接使用),如果连接关闭,则队列被删除。此参考优先级高于durable
         * autoDelete:是否自动删除, 默认是 false,true则表示当队列不再使用时,服务器删除该队列
         **/
        return new Queue("directQueue",true);
    }


    /**
     * 队列和交换机绑定
     * 设置路由键:directRouting
     **/
    @Bean
    Binding bindingDirect(){
        return BindingBuilder.bind(directQueue()).to(directExchange()).with("directRouting");
    }




}

复制代码

  1. 消息发送
@RestController
public class SendMessageController {


    @Autowired
    private RabbitTemplate rabbitTemplate;


    @GetMapping("/sendMessage")
    public String sendMessage(){
        //将消息携带路由键值
        rabbitTemplate.convertAndSend("directExchange", "directRouting", "发送消息!");
        return "ok";
    }


}

复制代码

我们先启动程序,在浏览器访问下

http://127.0.0.1:9001/sendMessage

报错如下:

我们之前已经给该用户分配过权限了,如果之前未分配,直接在客户端中配置:

之所以访问不到,是因为我们使用的端口号不正确

所以我们需要将端口改为 5672 (如果是阿里云服务器实例,需要将该端口 开放权限

我们再来访问下

http://127.0.0.1:9001/sendMessage

请求返回"OK",控制台输出

客户端相关页面截图如下:

  1. 消息消费
@Component
@RabbitListener(queues = "directQueue")//监听队列名称
public class MQReciever {


    @RabbitHandler
    public void process(String message){
        System.out.println("接收到的消息是:"+ message);
    }
}

复制代码

启动项目,发现消息已经被消费。

为了防止消息丢失, RabbitMQ 增加了消息确认机制:生产者消息确认机制和消费者消息确认机制。

确认机制

一、生产者消息确认机制

  1. yml 中增加配置信息
spring:
  rabbitmq:
    #确认消息已发送到交换机(Exchange)
    publisher-confirm-type: correlated
    #确认消息已发送到队列(Queue)
    publisher-returns: true

复制代码

spring.rabbitmq.publisher-confirm 新版本已被弃用,现在使用 spring.rabbitmq.publisher-confirm-type = correlated 实现相同效果

  1. 增加回调
@Bean
public RabbitTemplate createRabbitTemplate(ConnectionFactory connectionFactory){
 RabbitTemplate rabbitTemplate = new RabbitTemplate();
 rabbitTemplate.setConnectionFactory(connectionFactory);
 //设置开启 Mandatory,才能触发回调函数,无论消息推送结果怎么样都强制调用回调函数
 rabbitTemplate.setMandatory(true);


 rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
  @Override
  public void confirm(CorrelationData correlationData, boolean ack, String cause) {
   System.out.println("ConfirmCallback:     "+"相关数据:"+correlationData);
   System.out.println("ConfirmCallback:     "+"确认情况:"+ack);
   System.out.println("ConfirmCallback:     "+"原因:"+cause);
  }
 });


 rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback(){
  @Override
  public void returnedMessage(ReturnedMessage returned) {
   System.out.println("ReturnCallback:     "+"消息:"+returned.getMessage());
   System.out.println("ReturnCallback:     "+"回应码:"+returned.getReplyCode());
   System.out.println("ReturnCallback:     "+"回应信息:"+returned.getReplyText());
   System.out.println("ReturnCallback:     "+"交换机:"+returned.getExchange());
   System.out.println("ReturnCallback:     "+"路由键:"+returned.getRoutingKey());
  }
 });
 return rabbitTemplate;
}

复制代码

  • confirm 机制是只保证消息到达 exchange ,并不保证消息可以路由到正确的地方 queue
  • 当前的 exchange 不存在或者指定的路由 key 路由不到才会触发 return 机制

大家可以自行演示以下情况的执行结果:

  • 不存在交换机和队列
  • 存在交换机,不存在队列
  • 消息推送成功

二、消费者消息的确认机制

默认情况下如果一个消息被消费者正确接收则会从队列中移除。如果一个队列没被任何消费者订阅,那么这个队列中的消息会被缓存,当有消费者订阅时则会立即发送,进而从队列中移除。

消费者消息的确认机制可以分为以下 3 种:

  1. 自动确认

AcknowledgeMode.NONE 默认为自动确认,不管消费者是否成功处理了消息,消息都会从队列中被移除。

  1. 根据情况确认

AcknowledgeMode.AUTO 根据方法的执行情况来决定是否确认还是拒绝(是否重新入队列)

  • 如果消息成功被消费(成功的意思是在消费的过程中没有抛出异常),则自动确认
  • 当抛出 AmqpRejectAndDontRequeueException 异常的时候,则消息会被拒绝,且消息不会重回队列
  • 当抛出 ImmediateAcknowledgeAmqpException 异常,则消费者会被确认
  • 其他的异常,则消息会被拒绝,并且该消息会重回队列,如果此时只有一个消费者监听该队列,则有发生死循环的风险,多消费端也会造成资源的极大浪费,这个在开发过程中一定要避免的。可以通过 setDefaultRequeueRejected (默认是 true )去设置

可能造成消息丢失,一般是需要我们在 try-catch 捕捉异常后, 打印日志 用于追踪数据,这样找出对应数据再做后续处理。

  1. 手动确认

AcknowledgeMode.MANUAL 对于手动确认,也是我们工作中最常用到的,它的用法如下:

/*
 * 肯定确认
 * deliveryTag:消息队列数据的唯一id
 * multiple:是否批量 
 * true :一次性确认所有小于等于deliveryTag的消息
 * false:对当前消息进行确认;
 */
channel.basicAck(long deliveryTag, boolean multiple); 

复制代码

/*
 * 否定确认
 * multiple:是否批量 
 *   true:一次性拒绝所有小于deliveryTag的消息
 *   false:对当前消息进行确认;
 * requeue:被拒绝的是否重新入列,
 *   true:就是将数据重新丢回队列里,那么下次还会消费这消息;
 *   false:就是拒绝处理该消息,服务器把该消息丢掉即可。 
 */
channel.basicNack(long deliveryTag, boolean multiple, boolean requeue);

复制代码

/*
 * 用于否定确认,但与basicNack相比有一个限制,一次只能拒绝单条消息
 */
channel.basicReject(long deliveryTag, boolean requeue);  

复制代码

手动确认

在 yml 配置中开启手动确认模式

spring:
  rabbitmq:
    listener:
      simple:
        acknowledge-mode: manual

复制代码

或者在代码中开启

@Configuration
public class MessageListenerConfig {


    @Autowired
    private CachingConnectionFactory connectionFactory;


    @Autowired
    private MQReciever mqReciever;//消息接收处理类


    @Bean
    public SimpleMessageListenerContainer simpleMessageListenerContainer(){
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
        //并发使用者的数量
        container.setConcurrentConsumers(1);
        //消费者人数上限
        container.setMaxConcurrentConsumers(1);
        container.setAcknowledgeMode(AcknowledgeMode.MANUAL); // RabbitMQ默认是自动确认,这里改为手动确认消息
        //设置一个队列,此处支持设置多个
        container.setQueueNames("directQueue");
        container.setMessageListener(mqReciever);
        return container;
    }
}

复制代码

消息消费类

@Component
@RabbitListener(queues = "directQueue")//监听队列名称
public class MQReciever implements ChannelAwareMessageListener {


    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {
            String msg = message.toString();
            String[] msgArray = msg.split("'");//可以点进Message里面看源码,单引号直接的数据就是我们的map消息数据
            System.out.println("消费的消息内容:"+msgArray[1]);
            System.out.println("消费的主题消息来自:"+message.getMessageProperties().getConsumerQueue());
            
            //业务处理
            ......
            
            channel.basicAck(deliveryTag, true);
            
        } catch (Exception e) {
            //拒绝重新入队列
            channel.basicReject(deliveryTag, false);   
            e.printStackTrace();
        }
    }
} 

原文 https://xie.infoq.cn/article/96df79f01c32a63129dfe7a29

相关推荐

sharding-jdbc实现`分库分表`与`读写分离`

一、前言本文将基于以下环境整合...

三分钟了解mysql中主键、外键、非空、唯一、默认约束是什么

在数据库中,数据表是数据库中最重要、最基本的操作对象,是数据存储的基本单位。数据表被定义为列的集合,数据在表中是按照行和列的格式来存储的。每一行代表一条唯一的记录,每一列代表记录中的一个域。...

MySQL8行级锁_mysql如何加行级锁

MySQL8行级锁版本:8.0.34基本概念...

mysql使用小技巧_mysql使用入门

1、MySQL中有许多很实用的函数,好好利用它们可以省去很多时间:group_concat()将取到的值用逗号连接,可以这么用:selectgroup_concat(distinctid)fr...

MySQL/MariaDB中如何支持全部的Unicode?

永远不要在MySQL中使用utf8,并且始终使用utf8mb4。utf8mb4介绍MySQL/MariaDB中,utf8字符集并不是对Unicode的真正实现,即不是真正的UTF-8编码,因...

聊聊 MySQL Server 可执行注释,你懂了吗?

前言MySQLServer当前支持如下3种注释风格:...

MySQL系列-源码编译安装(v5.7.34)

一、系统环境要求...

MySQL的锁就锁住我啦!与腾讯大佬的技术交谈,是我小看它了

对酒当歌,人生几何!朝朝暮暮,唯有己脱。苦苦寻觅找工作之间,殊不知今日之事乃我心之痛,难道是我不配拥有工作嘛。自面试后他所谓的等待都过去一段时日,可惜在下京东上的小金库都要见低啦。每每想到不由心中一...

MySQL字符问题_mysql中字符串的位置

中文写入乱码问题:我输入的中文编码是urf8的,建的库是urf8的,但是插入mysql总是乱码,一堆"???????????????????????"我用的是ibatis,终于找到原因了,我是这么解决...

深圳尚学堂:mysql基本sql语句大全(三)

数据开发-经典1.按姓氏笔画排序:Select*FromTableNameOrderByCustomerNameCollateChinese_PRC_Stroke_ci_as//从少...

MySQL进行行级锁的?一会next-key锁,一会间隙锁,一会记录锁?

大家好,是不是很多人都对MySQL加行级锁的规则搞的迷迷糊糊,一会是next-key锁,一会是间隙锁,一会又是记录锁。坦白说,确实还挺复杂的,但是好在我找点了点规律,也知道如何如何用命令分析加...

一文讲清怎么利用Python Django实现Excel数据表的导入导出功能

摘要:Python作为一门简单易学且功能强大的编程语言,广受程序员、数据分析师和AI工程师的青睐。本文系统讲解了如何使用Python的Django框架结合openpyxl库实现Excel...

用DataX实现两个MySQL实例间的数据同步

DataXDataX使用Java实现。如果可以实现数据库实例之间准实时的...

MySQL数据库知识_mysql数据库基础知识

MySQL是一种关系型数据库管理系统;那废话不多说,直接上自己以前学习整理文档:查看数据库命令:(1).查看存储过程状态:showprocedurestatus;(2).显示系统变量:show...

如何为MySQL中的JSON字段设置索引

背景MySQL在2015年中发布的5.7.8版本中首次引入了JSON数据类型。自此,它成了一种逃离严格列定义的方式,可以存储各种形状和大小的JSON文档,例如审计日志、配置信息、第三方数据包、用户自定...

取消回复欢迎 发表评论: