AMQP的实现-RabbitMQ

消息队列常用应用场景:

  1. 异步处理(如发短信邮件等)
  2. 应用解耦(rpc系统升级如参数变动需要修改源码重新部署)
  3. 流量削峰(秒杀业务)

消息服务中两个重要概念:

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

消息队列主要有两种形式的目的地:

  1. 队列(queue):点对点消息通信(point-to-point)
    • 消息发送者发送消息,消息代理将其放入一个队列中,消息接收者从队列中获取消息内容,消息读取后被移出队列。
    • 消息只有唯一的发送者和接收者,但不是说只能有一个接收者。
  2. 主题(topic):发布(publish)/ 订阅(subscribe)消息通信
    • 发布者发送消息到主题,多个订阅者(接收者)订阅(监听)这个主题,那么就会在消息到达时同时收到消息。

消息服务的两个规范

JMS(Java Message Service)Java消息服务

  • 基于JVM消息代理的规范。ActiveMQ、HornetMQ是JMS实现。

  • javaApi 不支持跨语言。

AMQP(Advanced Message Queuing Protocol)

  • 高级消息队列协议,也是一个消息代理的规范,兼容JMS
  • RabbitMQ是AMQP的实现。

RabbitMQ概念

RabbitMQ简介:RabbitMQ是一个由erlang语言开发的AMQP的开源实现。

核心概念:

  • Message

    消息,消息是不具名的,它由消息头和消息体组成。消息体不是透明的,而消息头则有一系列的可选属性组成,这些属性包括routing-key(路由键)、priority(相对于其他消息的优先权)、delivery-mode(指出该消息可能要持久性存储)等。

  • Publisher

    消息的生产者,也是一个向交换机发布消息的客户端应用程序。

  • Exchange

    交换机,用来接收生产者发送的消息并将这些消息路由给服务器中的队列,Exchange由4种类型:direct(默认)、fanout、topic和headers,不同类型的Exchange转发消息的策略有所区别。

  • Consumer

    消息的消费者,表示一个从消息队列中取得消息的客户端应用程序。

  • Virtual Host

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

  • Broker

    表示消息队列服务器实体

  • Queue

    消息队列,用来保存消息直到发送给消费者,它是消息的容器,也是消息的终点。一个消息可投入1个或多个队列。消息一直在队列里面,等待消费者连接到这个队列将其取走。

  • Bingding

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

  • Connection

    网络连接,比如一个TCP连接。

  • Channel

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

RabbitMQ运行机制

  • AMQP 中的消息路由

    AMQP中消息的路由过程和Java开发者熟悉的JMS存在一些差别,AMQP中增加了 ExchangeBinding 的角色。生产者把消息发布到Exchange上,消息最终到达队列并被消费者接收,而Bindigng决定交换机的消息应该发送到哪个队列。

  • Exchange类型

    Exchange分发消息时根据类型的不同分发策略有区别,目前共四种类型:direct、fanout、topic、headers。headers匹配AMQP消息的header而不是路由键,header交换机和direct交换机完成一致,但是性能差很多,目前几乎用不到了。

    • Direct Exchange:消息中的路由键(routing key) 如果和 Binding 中的binding key 一致,交换器就将消息发送到对应的队列中。路由键和队列名完全匹配,如果一个队列绑定到交换机要求路由键为“dog”,则转发routing key 标记为"dog"的消息,不会转发"dog.puppy",也不会转发"dog.guard"等等。它是完全匹配、单播的模式。
    • Fanout Exchange:每个发送到fanout类型交换机的消息都会分到所有绑定的队列上去。fanout交换机不处理路由键,只是简单的将队列绑定到交换机上,每个发送到交换机的消息都会被转发到与该交换机绑定的所有队列上。很像子网广播,每台子网内的主机都获得了一份复制的消息。fanout类型转发消息是最快的。
    • Topic Exchange:topic交换机通过模式匹配分配消息的路由键属性,将路由键和某个模式进行匹配,此时队列需要绑定到一个模式上,它将路由键和绑定键的字符串切分成单词,这些单词之间用点(”.“)隔开。它同样也会识别两个通配符;符号”#“和符号"✳"。"#"匹配 0个或多个单词,"✳"匹配一个单词。

RabbitMQ的使用

  1. 引入amqp场景; RabbitAutoConfiguration 就会自动生效

    	<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
    
  2. RabbitAutoConfiguration 给容器中自动配置了RabbitTemplate、Amqpadmin、CachingConnectionFactory、RabbitMessagingTemplate;所有属性都是spring.rabbitmq,@ConfigurationProperties(prefix = "spring.rabbitmq")

  3. 给配置文件中配置rabbitmq的连接信息等,配置内容可以覆盖 类 RabbitProperties的默认信息。

  4. 启动类开启@EnableRabbit

  5. 监听消息:使用@RabbitListener(类+方法 监听那些队列即可) @RabbitHandler(方法 可区分不同的消息);

RabbitMQ消息确认机制-可靠抵达

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

  • publisher confirmCallback 确认模式

    定制RabbitTemplate:

    ​ 1、spring.rabbitmq.publisher-confirms = true

    ​ 2、设置确认回调 rabbitTemplate.setConfirmCallBack(xxx...)

    spring.rabbitmq.publisher-confirms = true 开启发送端确认。

    在创建connectionFactory的时候设置PublisherConfirms(true)选项,开启confirmcallback

    CorrelationData:用来表示当前消息唯一性。

    消息只要被broker接收到就会执行confirmCallback,如果是cluster模式,需要所有broker接收到才会调用confirmcallback。

    被broker接收到只能表示message已经到达服务器,并不能保证消息一定回被投递到目标queue里。所以需要用到returnCallback。

  • publisher returnCallback 未投递到queue退回模式

    定制RabbitTemplate:

    spring.rabbitmq.publisher-returns = true 开启发送端抵达队列确认。

    spring.rabbitmq.template.mandatory = true 只要抵达队列,以异步方式优先回调returnConfirm

    设置消息抵达队列的确认回调:rabbitTemplate.setReturnCallback(xxx) 只要消息没有投递到指定队列,就触发失败回调。

    confirm 模式只能保证消息到达broker,不能保证消息准确投递到目标queue里。在有些业务场景下,我们需要保证消息一定要投递到目标queue里,此时就需要用到return 退回模式。

  • consumer ack机制

    消费者获取到消息,成功处理,可以回复Ack给Broker

    — basic.ack用于肯定确认;broker将移除此消息

    — basic.nack用于否定确认;可以指定broker是否丢弃此消息,可以批量

    — basic.reject用于否定确认;同上,但是不能批量

    默认,消息被消费者收到,就会从broker的queue中移除

    queue无消费者,消息依然会被存储,直到消费者消费。

    消费者收到消息,默认会自动Ack。但是如果无法确定此消息是否被处理完成,或者成功处理。我们可以开启手动Ack模式( spring.rabbitmq.listener.simple.acknowledge-mode = manual )

    — 消息处理成功,ack(),接受下一个消息,此消息broker就会移除

    — 消息处理失败,nack()/reject(),重新发送给其他人进行处理,或者容错处理后Ack

    — 消息一直没有调用ack/nack方法,broker认为此消息正在被处理,不会投递给别人,此时客户端断开,消息不会被broker移除,会投递给别人。

posted @   fjhnb  阅读(386)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示