rabbitMQ基础使用学习

本文介绍RabbitMQ的基础概念,如消息、生产者、交换器等,并详细讲解如何使用Spring Boot整合RabbitMQ,包括安装配置、消息收发、保证消息一致性的方法。

一、mq的作用,为什么要用mq

 MQ是一种非常常见的上下游“逻辑解耦+物理解耦”的消息通信服务

MQ的不足是:

1)系统更复杂,多了一个MQ组件

2)消息传递路径更长,延时会增加

3)消息可靠性和重复性互为矛盾,消息不丢不重难以同时保证

4)上游无法知道下游的执行结果,这一点是很致命的

二、如何安装mq

使用docker安装:docker run -d  --name rabbitmq -p 5671:5671 -p 5672:5672 -p 4369:4369 -p 25672:25672 -p 15671:15671 -p 15672:15672 rabbitmq:management

解释:

       rabbitmq:management:安装可视化界面

        -d 安装最新版本rabbitmq

       --name 容器名字

       -p 容器映射到主机的端口号

           端口号解释:4369=erlang发现(rabbitmq是erlang写的)、25672=集群端口号、(5672、5671)=AMQP端口、15672=web管理后台端口

三、rabbitmq的可视化界面操作

概念:
1、message:消息是由消息体和消息头组成的,消息体是不透明的,消息头是由一些列参数属性组成的,这些属性包括:routing-key(路由键)、priority(相对于其他消息的优先权)、delivery-mode(指出该消息可能需要持久性储存)
2、publisher:消息生产者,是一个向交换器发送消息的客户端应用程序
3、exchange:交换器,用来接收生产者发送的消息,并将这些消息路由给服务器中的队列。交换器四种类型:direct、fanout、topic、header;direct是点对点,fanout是和交换机绑定的队列都可以收到消息,topic是通过routing-key(路由键,可以使用通配符,#)进行消息投递,
4、queue:队列,存储消息的容器

基本操作:

 1、添加交换机

       

 2、添加绑定:

3、添加队列:

 4、添加用户

四、spring boot与rabbitmq的整合

1、添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

2、添加配置

#主机地址
spring.rabbitmq.host=ip
#虚拟主机
spring.rabbitmq.virtual-host=/
#端口号
spring.rabbitmq.port=5672

3、开启自动注入,添加@EnableRabbit 注解

4、添加序列化配置

@Configuration
public class RabbitConfig {

    @Bean
    public Jackson2JsonMessageConverter createMessageConverter() {
        return new Jackson2JsonMessageConverter();
    }
}

五、交换机、队列、绑定的代码实现

添加注入的bean

    @Autowired
    private AmqpAdmin amqpAdmin;
    @Autowired
    private RabbitTemplate rabbitTemplate;

1、创建交换机

    @Test
    public void createExchange() {
        /**
         * String name, 交换机的名字
         * boolean durable,是否持久化
         * boolean autoDelete,是否自动删除
         * Map<String, Object> arguments,需要添加的参数
         */
        DirectExchange directExchange = new DirectExchange("hello-java-exchange", true, false);
        amqpAdmin.declareExchange(directExchange);
    }

2、创建队列

    @Test
    public void createQueue(){
        /**
         * String name,队列名
         * boolean durable,是否持久化
         * boolean exclusive,如果为true,声明这个队列只能给当前连接使用,其他连接不能使用
         * boolean autoDelete, 是否自动删除
         * @Nullable Map<String, Object> arguments:参数
         */
        Queue queue = new Queue("hello-java-queue", true, false, false);
        amqpAdmin.declareQueue(queue);
    }

3、创建绑定

@Test
    public void createBinding() {
        /**
         * String destination,目的地,是队列或者交换机的名字
         * Binding.DestinationType destinationType,目的地类型,分为队列或者交换机
         * String exchange,交换机
         * String routingKey,路由键
         * @Nullable Map<String, Object> arguments:参数
         */
        Binding binding = new Binding("hello-java-queue", Binding.DestinationType.QUEUE, "hello-java-exchange", "hello.java", null);
        amqpAdmin.declareBinding(binding);
    }

六、接收发送消息代码实现

1、发送消息

@Test
    public void  sendMessage() {
        /**
         * String exchange,
         * String routingKey,
         * Object message
         * 如果使用对象发送消息必须实现序列化接口,Serializable
         * 如果我们没有配置rabbit的序列化属性:
         * 如果配置了属性
         * if (this.messageConverter != null) {
         * 			template.setMessageConverter(this.messageConverter);
         *                }
         * 如果没有配置初始化一个默认
         * 	protected void initDefaultStrategies() {
         * 		setMessageConverter(new SimpleMessageConverter());
         *        }
         *    对象序列化之后:rO0ABXNyACBjb20uZXhhbXBsZS5kZW1vLnRlc3QuVGVzdFVzZXJWb6zWLkCl6yQHAgACSQADYWdlTAAEbmFtZXQAEkxqYXZhL2xhbmcvU3RyaW5nO3hwAAAAA3QABGphdmE=
         *
         * 让发送的对象数据转成一个json
         * ObjectProvider标识这个属性从容器中获取
         * 将Jackson2JsonMessageConverter注入容器中
         */
        for (int i = 0; i < 10; i++) {
            TestUserVo vo = new TestUserVo();
            vo.setAge(i);
            vo.setName("java");
            rabbitTemplate.convertAndSend("hello-java-exchange", "hello.java", vo);
        }
    }

2、接收消息

接收消息可以使用@RabbitListener、@RabbitHandler这两个注解,listener可以标注在类和方法上,handler只能标注在方法上且存在listener下面,可以处理同一个队列下的不同消息实体。

    @RabbitListener(queues = {"hello-java-queue"})
    public void  test(Object message, TestUserVo vo, Channel channel){
        System.out.println("<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>"+vo.toString());
    }
@Component
@RabbitListener(queues = {"hello-java-queue"})
public class RabbitMqService {


    /**
     * 可以接收的参数
     * @param message 原生消息体和头
     * @param vo 发送的消息的实体类型
     * @param channel 当前传输的通道
     *
     *                queue可以多人监听,一个人处理,然后删除消息
     *                只有当一个消息处理完,才会发送下一个消息
     */
    @RabbitHandler
    public void  test1(Message message, TestUserVo vo, Channel channel){
        System.out.println("<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>"+vo.toString());
    }
    @RabbitHandler
    public void  test2(Message message, Student student, Channel channel){
        System.out.println("<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>"+student.toString());
    }
}

七、消息一致性

通过三步保证消息一致性:

1、消息发送的确认模式和退回模式

当发送者将消息发送到消息服务器,就会回调confirmCallback。当消息未正确到达队列就会回调returnConfirm

开启步骤:添加配置

# 开启发送确认
spring.rabbitmq.publisher-confirm-type=correlated
# 开启发送端消息抵达队列确认
spring.rabbitmq.publisher-returns=true
# 只要抵达队列,以异步发送优先回调我们这个returnConfirm
spring.rabbitmq.template.mandatory=true

设置确认回调

 @PostConstruct
    public void initRabbitTemplate() {
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            /**
             *只要消息到达broker就会回调
             * @param correlationData 当前消息的唯一关联数据或者称为消息的唯一id
             * @param ack 消息是否成功收到
             * @param cause 失败的原因
             */
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {
               
            }
        });
        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            /**
             * 只有消息没有达到queue才会回调
             * @param message 投递失败的消息信息
             * @param replyCode 回复的状态码
             * @param replyText 回复的文本内容
             * @param exchange 当前消息的交换机
             * @param routingKey 当前消息的路由键
             */
            @Override
            public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
               

            }
        });
    }

2、消息消费的ask机制

    消费端确认消息被成功消费,此时broker才可以删除消息
    消费者获取到消息,成功处理,可以回复ack给broker
    basic.ack 用于确认消息,broker将移除消息
    basic.nack 用于否定确认,可以指定broker是否丢弃消息,可以批量
    basic.reject 否定确认,不支持批量    
    默认是自动确认,只要接收到消息,客户端会自动确认,服务端会移除这个消息
    出现的问题:出现消息丢失
    修改为手动确认:只要我们没有告诉mq确认收到消息,消息就一致是unacked状态,即使消费端宕机,消息也不会丢失,会重新变为ready,下一个消费者会重新收到这个消息。

开启步骤:

1、添加手动确认消息配置:

# 开启手动回复
spring.rabbitmq.listener.simple.acknowledge-mode=manual

2、消息收到后手动确认

 @RabbitListener(queues = {"hello-java-queue"})
    public void  test1(Message message, TestUserVo vo, Channel channel) throws IOException {
        System.out.println("<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>"+vo.toString());
//        int a =10/ 0;
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),true);
//        channel.basicNack(message.getMessageProperties().getDeliveryTag(),true, true);
    }

开启手动确认后,可以防止消息丢失,只有确认的消息才会被删除,没有确认的消息是unacked状态。

 

 
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值