RabbitMQ学习总结

MQ的概念
在这里插入图片描述
什么是MQ

MQ: message queue 消息队列。

MQ的优点

​ (1)异步提速

​ (2)降低耦合

​ (3)削峰填谷

MQ的缺点

​ (1)复杂度提高

​ (2)处理结果的前后一致性变得不同步

​ (3)高可用性变差

常见MQ的产品

ActiveMQ : JMS接口 JAVA

RabbitMQ: AMQP协议 erlang

RocketMQ: JMS JAVA

ZeroMQ: C语言写的

Kafka:大数据那边 海量数据

RabbitMQ的概述

1580712486943

RabbitMQ的结构

Producer:负责发送消息

Connection/Channel:建立生产者和消费者与RabbitMQ的连接

Exchange:交换机,接收并分发消息,不存储消息

Queue:存储消息 (先进先出)

RoutingKey:路由,建立交换机分发消息的规则。建立Exchange和Queue之间的关系

Consumer:消费消息

RabbitMQ的工作模式

简单模式

在这里插入图片描述

工作队列模式

在这里插入图片描述

消费者会以轮询的方式从队列中获取消息进行消费。

发布订阅模式

在这里插入图片描述

Exhcange: fanout

路由模式

在这里插入图片描述

Exhcange:direct

通配符模式

在这里插入图片描述

Exchange:topic

RabbitMQ的开发

方式一:原生API

//1.创建连接工厂 [五要素:host port username password virtualHost]

//2.获取一个新的连接

//3.获取通道

//4.声明队列

//5.声明交换机

//6.绑定交换机和队列之间的关系

//7.发送消息


//8.关闭连接资源

生产者

//1.创建连接工厂 [五要素:host port username password virtualHost]
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
connectionFactory.setVirtualHost("/");
//2.获取一个新的连接
Connection connection = connectionFactory.newConnection();

//3.获取通道
Channel channel = connection.createChannel();
//4.声明队列 queue   declare声明
channel.queueDeclare("queueName",true,false,false,null);

//5.声明交换机 exchange
channel.exchangeDeclare("exchangeName", BuiltinExchangeType.FANOUT);
//exchangeType:  fanout 发布订阅    direct  路由    topic 通配符

//6.绑定交换机和队列之间的关系
channel.queueBind("queueName","exchangeName","routingKey");
//7.发送消息
channel.basicPublish("exchangeName","routingKey",null,"body".getBytes());
//8.关闭连接资源
channel.close();
connection.close();

消费者

//1.创建连接工厂 [五要素:host port username

password virtualHost]
 ConnectionFactory connectionFactory = new ConnectionFactory();
 connectionFactory.setHost("127.0.0.1");
 connectionFactory.setPort(5672);
 connectionFactory.setUsername("guest");
 connectionFactory.setPassword("guest");
 connectionFactory.setVirtualHost("/");
 //2.获取一个新的连接
 Connection connection = connectionFactory.newConnection();

//3.获取通道
Channel channel = connection.createChannel();
//4.声明队列 queue   declare声明
channel.queueDeclare("queueName",true,false,false,null);

//5.声明交换机 exchange
channel.exchangeDeclare("exchangeName", BuiltinExchangeType.FANOUT);
//exchangeType:  fanout 发布订阅    direct  路由    topic 通配符

//6.绑定交换机和队列之间的关系
channel.queueBind("queueName","exchangeName","routingKey");
//7.接收消息
Consumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
//body就是获取的消息内容
//业务处理
}
};
channel.basicConsume("queueName",true,consumer);
//8.关闭连接资源  消费者是不能关闭连接
//channel.close();
//connection.close();

方式二:与Spring整合

生产者

(1) 导入对应的jar包

(2)添加配置文件

连接RabbitMQ

Queue

Exchange

绑定Queue和Exchange之间的关系

发送消息 RabbitTemplate.convertAndSend()

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:rabbit="http://www.springframework.org/schema/rabbit"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/rabbit
       http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">

    <!--建立和RabbitMQ的连接 -->
    <context:property-placeholder location="classpath:properties/rabbitmq.properties"/>
    <rabbit:connection-factory
        id="connectionFactory"
        host="${rabbitmq.host}"
        port="${rabbitmq.port}"
        username="${rabbitmq.username}"
        password="${rabbitmq.password}"
        virtual-host="${rabbitmq.virtual-host}"
        />
    <rabbit:admin connection-factory="connectionFactory"/>

    <!--声明队列-->
    <rabbit:queue id="queueName" name="queueName" auto-declare="true"/>

    <!--声明交换机-->
    <rabbit:fanout-exchange id="fanoutExchange" name="fanoutExchange">
        <!--绑定Queue和Exchange -->
        <rabbit:bindings>
            <rabbit:binding queue="queueName"></rabbit:binding>
        </rabbit:bindings>
    </rabbit:fanout-exchange>


    <rabbit:direct-exchange id="directExchange" name="directExchange">
        <!--绑定Queue和Exchange -->
        <rabbit:bindings>
            <rabbit:binding queue="queueName" key="routingKey"></rabbit:binding>
        </rabbit:bindings>
    </rabbit:direct-exchange>
    
    
    <rabbit:topic-exchange id="topicExchange" name="topicExchange">
        <!--绑定Queue和Exchange 
            #:零个或多个
            *: 一个
        -->
        <rabbit:bindings>
            <rabbit:binding queue="queueName" pattern=""></rabbit:binding>
        </rabbit:bindings>
    </rabbit:topic-exchange>

    


    <!--RabbitTemplate-->
    <rabbit:template connection-factory="connectionFactory" id="rabbitTemplate"/>
(3)注入对应Bean对象进行业务操作
@Autowired
private RabbitTemplate rabbitTemplate;

rabbitTemplate.convertAndSend("exchangeName","routingKey",body)

;
消费者

(1) 导入对应的jar包

(2)添加配置文件

连接RabbitMQ

监听队列操作

监听器类

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:rabbit="http://www.springframework.org/schema/rabbit"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/rabbit
       http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">

    <!--建立和RabbitMQ的连接 -->
    <context:property-placeholder location="classpath:properties/rabbitmq.properties"/>
    <rabbit:connection-factory
        id="connectionFactory"
        host="${rabbitmq.host}"
        port="${rabbitmq.port}"
        username="${rabbitmq.username}"
        password="${rabbitmq.password}"
        virtual-host="${rabbitmq.virtual-host}"
        />
    <rabbit:admin connection-factory="connectionFactory"/>
   <!-- 方式一-->
    <bean id="myListener" class="监听器类的类全名"/>
    <!--方式二
        注解  @Component
        包扫描,扫描到对应的注解
    -->
   <rabbit:listener-container connection-factory="connectionFactory" auto-declare="true">
       <!--
            ref指定的是Spring容器中的bean对象,监听器类
        -->
       <rabbit:listener ref="myListener" queue-names="queueName"/>
   </rabbit:listener-container>
</beans>

(3)编写业务操作

public class Xxxx  implements MessageListener{
	onMessage(Message message){
	
		//获取消息
		//业务操作
	}

}

方式三:与SpringBoot整合

生产者

(1)pom.xml添加起步依赖

(2)application.yml添加配置

(3)队列、交换机、绑定队列和交换机之间的关系的声明

(4)发送消息

(1)
spring-boot-starter-amqp
(2)
spring:
  rabbitmq:
    host: 172.16.98.133 # ip
    port: 5672
    username: guest
    password: guest
    virtual-host: /
(3)@Configuration @Bean
@Configuration
public class MyConfigure {
    //声明队列
    @Bean(name="queueName")
    public Queue queue(){
        return QueueBuilder.durable("queueName").build();
    }
    //声明交互机
    @Bean(name="exchangeName")
    public Exchange exchange(){
        //return ExchangeBuilder.fanoutExchange("exchangeName").build();
        //return ExchangeBuilder.directExchange("exchangeName").build();
        return  ExchangeBuilder.topicExchange("exchagenName").build();
    }
    //绑定关系
    @Bean
    public Binding binding(@Qualifier("queueName")Queue queue
                ,@Qualifier("exchangeName") Exchange exchange){
        //bind: 队列对象
        //to :交换机对象
        return BindingBuilder.bind(queue).to(exchange).with("routingKey").noargs();
    }
}
(4)发送消息
@Autowired
private RabbitTemplate rabbitTemplate; //思考 RabbitTemplate从哪里来???

rabbitTemplate.convertAndSend("exchangeName","routingKey",body);

消费者

(1)pom.xml添加起步依赖

(2)application.yml添加配置

(3)监听队列获取消息

(1)
spring-boot-starter-amqp
(2)
spring:
  rabbitmq:
    host: 172.16.98.133 # ip
    port: 5672
    username: guest
    password: guest
    virtual-host: /
(3)监听类
@Component
public class MyListener{
	@RabblitListener(queues="queueName")
	public void method(String message){
	
		//获取消息
		//对应业务操作
	}
}

RabbitMQ高级部分

消息的可靠性投递

在这里插入图片描述

<rabbit:connection-factory
    id="connectionFactory"
    host="${rabbitmq.host}"
    port="${rabbitmq.port}"
    username="${rabbitmq.username}"
    password="${rabbitmq.password}"
    virtual-host="${rabbitmq.virtual-host}"
    publisher-confirms="true"
    publisher-returns="true"
    />
RabbitTemplate类调用对应的方法来添加回调函数。
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                //ack true 消息已经正确发送到交换机  false 消息未能正确发送到交换机,
                //如果ack是false,couse就是未能正确发送到交换机的原因
            }
        });
//设置交换机处理失败消息的模式
        rabbitTemplate.setMandatory(true);

        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            @Override
            /**
             * message:消息对象
             * replyCode:错误码
             * replyText:错误信息
             * exchange:交换机
             * routingKey:路由
             */
            public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
                System.out.println("return 执行了...");
                System.out.println(message);
                System.out.println(replyCode);
                System.out.println(replyText);
                System.out.println(exchange);
                System.out.println(routingKey);
            }
        });
<!--
   acknowledge:控制的是消息接收的模式
       manual: 手动确认
       none: 自动确认 默认
           如果是自动确认,消息被消费者取走以后,队列中的消息就会被删除,不管消息有没有正确被消费
           如果是手动确认,就需要程序员在做业务处理的时候,根据实际情况来决定是否要将消息从队列中删除掉
            监听器类的编写
           public class MyListener implmenets MessageListener{
                onMessage(Message message){
                    //要想手动确认消息,需要用到Channel对象,现在这种方式无法获取到channel
                }
           }
           解决方案
        @Component
        public class AckListener implements ChannelAwareMessageListener{
        
            @Override
            public void onMessage(Message message, Channel channel) throws Exception {
                System.out.println(new String(message.getBody()));
                long deliveryTag = message.getMessageProperties().getDeliveryTag();
                try{
                    Thread.sleep(1000);
                    //处理业务逻辑
                    //System.out.println("处理出错误了");
                    //int i = 10/0;
                    System.out.println("处理业务逻辑");
        
                    channel.basicAck(deliveryTag,true);  //手动确认正确接收
                }catch (Exception e){
                    channel.basicNack(deliveryTag,true,true);//手动确认接收异常
                }
            }
        }
-->
<rabbit:listener-container connection-factory="connectionFactory" auto-declare="true"

                           acknowledge="manual">
    <rabbit:listener ref="ackListener" queue-names="queueName"/>
</rabbit:listener-container>

消费端限流

在这里插入图片描述

TTL

在这里插入图片描述

死信队列

当消息未正常消费后,需要将失败的消息存入到指定的队列中,该队列就是死信队列。

三种情况可以让消息进入死信队列:

​ (1)消息队列的长度查出,比如设置消息队列能存储100个消息,当队列中超过100个后,进入队列的消息就会被送入死信队列中

​ (2) 消息队列设置了TTL,当消息超时未被消费的消息进入死信队列

​ (3) 消费者在消费消息的时候,如果业务处理失败,被消费者通过channel.basicNack();进行拒收的消息也会进入到死信队列。

在这里插入图片描述

延迟队列

死信队列中的第二种情况,用来实现延迟队列的。

在这里插入图片描述

消息的补偿

消息的补偿就是确保消息100%被消费。

(1)记录生产者发送的消息数

(2)记录消费者消费消息数

(3)获取两个消息数的差值,差值代表的含义就是未被正确消费的消息,将这些消息重新进行消息的发送。

消息的幂等性

消息被消费者消费多次最终获取的结果要保持一致。

​ 判断消息是否被消费过,如果未被消费过,让消费者进行消费,如果已经消费过,不在让消费者进行消费了。

数据库Mysql表:

table

id name … version

1 x … 2

(1) update table set version = version+1 where version =1 … 更新成功,说明消息未被消费过,让消费者进行消费操作。

(2) update table set version = version+1 where version =1 … 更新失败 说明消息已经被消费过了,不在进行消费。

Redis的key-value

key是否存在来判断

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值