消息队列 - RabbitMQ

一、概述:

1、大多数服务中可通过消息服务中间件来提高系统异步通讯、扩展解耦能力
2、消息服务中有两个概念
  • 消息代理
  • 目的地

当消息发送者发送消息以后,将由消息代理接管,消息代理保证消息传递到指定目的地。

3、消息队列两种形式目的地
  • 队列(queue):点对点消息通讯(point-to-point)
  • 主题(topic):发布(publish)/订阅(subscribe)消息通讯
4、点对点模式:
  • 消息发送者发送消息,消息代理将消息放入一个队列中,消息接收者在这个队列中获取消息内容,消息读取后移出队列
  • 消息只有唯一的发送者 可有多个接收者 (当有多个接收者时,只会交给一个接收者来进行接收消息。其他接收者无法获取到消息

在这里插入图片描述##### 5、发布订阅模式:

  • 发送者发送消息到主题,多个接收者监听者主题,那么就会在消息到达时同时接收到消息。(多个接收者均能收到消息)

在这里插入图片描述##### 6、JMS (Java Message service) java消息服务

  • 基于java 消息代理规范。ActiveMq、HornetMq是JMS实现
7、AMQP(Advanced Message Queuing Protocol)
  • 高级消息队列协议,也是消息代理的规范,兼容JMS
  • RabbitMQ是AMQP实现
8、spring支持
  • spring-jms 提供了对JMS 的支持
  • spring-rabbit 提供了对与AMQP 的支持
  • 需要connectionFactory 的实现来连接消息代理
  • 提供JMSTemplate、RabbitTemplate 来发送消息
  • @JmsListener (JMS)、@RabbitLisener (AMQP) 注解监听消息代理发布的消息
  • @EnableJMS、@EnableRabbit开启支持
9、Spring boot 自动配置
  • JmsAutoConfiguration
  • RabbitAutoConfiguration
10、市面MQ产品
  • ActiveMQ、RabbitMQ、RocketMQ、Kafka

二、RabbitMQ 概念

1、核心概念
  • Message : 消息,有消息头和消息体构成,消息体是不透明的,消息头有一系列可选属性组成,属性包括routing-key(路由键)、priority(相对于其他消息的优先权) 、delivery-mode(指出该消息可能需要持久性存储)

  • Publisher : 消息的生产者,也是一个向交换器发布消息的的客户端程序

  • Exchange : 交换器,用来接收生产者所产生的消息并将这些消息路由给服务器中的队列。

    • Exchange 有四种类型:不同类型的Exchange转发消息的策略有所区别
      • direct(默认)
      • fanout
      • topic
      • header
  • Queue :消息队列,用来保存消息直到发送给消费者。它是消息的容器,也是消息的终点。一个消息可投入一个或多个队列。消息一直在队列里面,等待消费者连接这个队列将其取走。

  • Binding :绑定,用于队列和交换机之间的关联,一个绑定就是基于路由键将交换器连接起来的路由规则,所以可以将交换机理解成一个由绑定构成的路由表,Exchange和Queue的绑定可以是多对多的关系

  • Connection :网络连接,比如一个TCP连接

  • Channel :信道,多路复用连接中的一条独立的双向数据流通道,信道是建立在真实的TCP连接内的虚拟连接,AMQP命令都是通过信道发送出去的,不管是发布消息,订阅队列还是接收消息,这些东西都是通过信道完成,因为对于操作系统来说建立和销毁TCP都是非常昂贵的开销,所以引用信道的概念,以服用一条TCP连接。

  • Virtual Host : 虚拟主机,表示一批交换机、消息队列和相关对象。虚拟主机是共享身份认证和加密环境的独立服务器域。每个vhost本质上就是一个mini版的RabbitMQ服务器,拥有自己的队列、交换器、绑定和权限机制。vhost是AMQP概念的基础,必须在连接时指定,RabbitMQ默认的vhost 是 /。

  • Broker :表示消息队列服务器实体。

三、springBoot 整合RabbitMQ

1、引入amqp场景
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
    <version>3.1.2</version>
</dependency>
2、RabbitAutoConfiguration自动配置
  • rabbitTemplate

  • amqpAdmin

  • RabbitConnectionFactoryCreator

  • rabbitMessagingTemplate

3、配置属性
@ConfigurationProperties(prefix = "spring.rabbitmq")
spring.rabbitmq.host=localhost   # 主机地址
spring.rabbitmq.port=5672        # 端口号
spring.rabbitmq.virtual-host=/   # 虚拟主机端口
4、添加注解
@SpringBootApplication
@EnableRabbit  // 开启rabbitMQ 功能
public class RabbitMqDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(RabbitMqDemoApplication.class, args);
    }
}
5、RabbitMQ 应用
  • 创建交换机
DirectExchange directExchange = new DirectExchange("hello-java-exchange",true,false);  
amqpAdmin.declareExchange(directExchange);
/**
* name 交换机名称
* durable 是否持久化
* autoDelete 是否自动删除
* arguments 自定义参数
*/
public DirectExchange(String name, boolean durable, boolean autoDelete, Map<String, Object> arguments) {
    super(name, durable, autoDelete, arguments);
}
  • 交换机创建
@Resource
AmqpAdmin amqpAdmin;

@Test
void createExchange() {
    DirectExchange directExchange = new DirectExchange("hello-java-exchange",true,false);
    amqpAdmin.declareExchange(directExchange);
    log.info("exchange {} 创建成功","hello-java-exchange");
}
  • 队列创建
/**
* name 交换机名称
* durable 是否持久化
* autoDelete 是否自动删除
* exclusive 如果我们正在声明一个独占队列(该队列将仅由解密器的连接使用),则为true 
* arguments 自定义参数
*/
public Queue(String name, boolean durable, boolean exclusive, boolean autoDelete,
             @Nullable Map<String,Object> arguments)
@Test
void createQueue() {
    amqpAdmin.declareQueue(new Queue("hello-java-queue",true,false,true));
    log.info("queue {} 创建成功", "hello-java-queue");
}
  • 创建绑定关系
/**
* lazyQueue 延迟队列
* destination 目的地
* destinationType 目的地类型
* exchange 交换机 
* routingKey 路由key
* arguments 参数
*/
public Binding(@Nullable Queue lazyQueue, @Nullable String destination, DestinationType destinationType,
       String exchange, @Nullable String routingKey, @Nullable Map<String, Object> arguments)
@Test
void createBinding() {
    amqpAdmin.declareBinding(new Binding("hello-java-queue", Binding.DestinationType.QUEUE,"hello-java-exchange","hello.java",null));
    log.info("binding {} 创建成功", "hello-java-binding");
}
  • 发送消息
@Resource
RabbitTemplate rabbitTemplate;

@Test
void sendMessage(){
    rabbitTemplate.convertAndSend("hello-java-exchange","hello.java","hello world");
    log.info("消息发送完成:{}","hello world");
}
  • 接收消息

    1、方法一:

@Component
public class RabbitMqService {
    @RabbitListener(queues = {"hello-java-queue"})
    public void receiveMessage(Message message){
        System.out.println(message);
    }
}

​ 2、方法二:

@Component
@RabbitListener(queues = {"hello-java-queue"})
public class RabbitMqService {
    // 参数可以自己变更
    @RabbitHandler
    public void receiveMessage(Object message){
        System.out.println(message);
    }
}
  • 配置收发消息序列化
@Configuration
public class RabbitConfig {
    @Bean
    public MessageConverter messageConverter() {
        return new Jackson2JsonMessageConverter();
    }
}

四、RabbitMQ消息确认机制 - 可靠可达

1、消息可靠机制

为保证消息不丢失,可靠抵达,可以使用事务消息,性能下降250倍,为此引入消息可靠机制。

2、生产者端确认

配置:

# NONE值是禁用发布确认模式,是默认值
# CORRELATED值是发布消息成功到交换器后会触发回调方法
# SIMPLE值经测试有两种效果,其一效果和CORRELATED值一样会触发回调方法,
# 其二在发布消息成功后使用rabbitTemplate调用waitForConfirms或waitForConfirmsOrDie方法等待broker节点返回发送结果,
# 根据返回结果来判定下一步的逻辑,要注意的点是waitForConfirmsOrDie方法如果返回false则会关闭channel,则接下来无法发送消息到broker;
spring.rabbitmq.publisher-confirm-type=correlated
# 只要抵达队列,以异步发送优先回调我们的returnConfirm
spring.rabbitmq.template.mandatory=true
# 开启发送端确认
spring.rabbitmq.publisher-returns=true
spring.main.allow-circular-references = true
  • publisher:

    • confirmCallBack确认模式
      • 在创建connectionFactory的时候设置pulisherConfirms(true)选项,开启confirmcallBack.
      • 消息只要被broker接收到就会执行confirmcallback,如果是cluser模式,需要所有broker接收到才会调用confirmcallback。
      • 被broker接收到只能表示message已经到达服务器,并不能保证消息一定会传递到目标queue里,所以需要用到returnCallback.
        @PostConstruct //RabbitConfig对象创建完成以后,执行这个方法
        public void initRabbitTemplate(){
            rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
                /**
                 * 只要消息抵达 Broker 就 ack = true
                 * @param correlationData 当前消息的唯一关联数据(这个是消息的唯一id)
                 * @param ack  是都成功收到
                 * @param cause 失败原因
                 */
                @Override
                public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                    System.out.println("confirm....correlationData【"+correlationData+"】==>ack【"+ack+"】==>"+cause);
                }
            });
        }
    
    • returnCallBack 到达了交换机 但是未投递到queue退回模式
        @PostConstruct //RabbitConfig对象创建完成以后,执行这个方法
        public void initRabbitTemplate() {
            //设置消息抵达队列的确认回调
            rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {
                /**
                * message 投递失败消息详情
                * replyCode 回复的状态码
                * replyText 回复的状态文本
                * exchange  当时发送给哪个交换机
                * routingKey 当时使用的路由键
                */
                @Override
                public void returnedMessage(@Nonnull ReturnedMessage returned) {
                    System.out.println("returnMessage:" + returned.getMessage()+ returned.getReplyText());
                }
            });
        }
    
3、消费端确认
  • consumer ack机制
#手动提交ack
spring.rabbitmq.listener.simple.acknowledge-mode=manual

手动确认模式,如果没有明确告诉MQ收到消息,没有ack,消息就一直是unacked状态,即使服务器宕机了。消息不会丢失。重新变为ready,重新启动服务器后,消息会再次发送。

// channel内按顺序自增的
long deliveryTag = message.getMessageProperties().getDeliveryTag();
// 签收货物 deliveryTag(第几条消息)  multiple= false 非批量模式
channel.basicAck(deliveryTag,false);

拒绝签收消息

// deliveryTag(第几条消息)  multiple= false 非批量模式 requeue = true 拒收货物,会重新进行发送
// requeue = false 拒收货物 不进入队列 不会进行重新发送。
channel.basicNack(deliveryTag,false,true);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值