1.介绍
消息队列已经逐渐成为企业IT系统内部通信的核心手段。它具有低耦合、可靠投递、广播、流量控制、最终一致性等一系列功能,成为异步RPC的主要手段之一。
(1)解耦 -->耦合是指两个以上的模块相互依赖,其中任何模块出现问题可能导致整个系统瘫痪,解耦,就尽可能的降低模块的关联度(降低一个模块出现问题对其他模块的影响)。
(2)异步--->同步需要用户等待执行结果。异步可以用户发送请求后,在后台单独线程执行,用户无需等待。
(3)削峰--->将并行转换为串行的过程。(时间换稳定性)
2.MQ产品
- ActiveMQ
- RabbitMQ
- RocketMQ
- Kafka
2.1产品选择
- 中小型软件公司,建议选RabbitMQ:一方面,erlang语言天生具备高并发的特性,而且他的管理界面用起来十分方便。正所谓,成也萧何,败也萧何!他的弊端也在这里,虽然RabbitMQ是 开源的,然而国内有几个能定制化开发erlang的程序员呢?所幸RabbitMQ的社区十分活跃,可以解决开发过程中遇到的bug,这点对于中小型公司来说十分重要。不考虑rocketmq和kafka的原因是,一方面中小型软件公司不如互联网公司,数据量没那么大,选消息中间件,应首选功能比较完备的,所以kafka排除。不考虑rocketmq的原因是,rocketmq是阿里出品,如果阿里放弃维护rocketmq,中小型公司一般抽不出人来进行rocketmq的定制化开发,因此不推荐。
- 大型软件公司,根据具体使用在rocketMq和kafka之间二选一:一方面,大型软件公司,具备足够的资金搭建分布式环境,也具备足够大的数据量。针对rocketMQ,大型软件公司也可以抽出人手对rocketMQ进行定制化开发,毕竟国内有能力改JAVA源码的人,还是相当多的。至于kafka,根据业务场景选择,如果有日志采集功能,肯定是首选kafka了。具体该选哪个,看使用场景。
3.RabbitMQ安装
- 安装Erlang开发语言环境
- 解压rabbitmq_server压缩包
- 启动rabbitMQ服务
RabbitMQ安装教程_rabbitmq下载安装-CSDN博客
4.RabbitMQ命令
- rabbitmq-plugins enable rabbitmq_management 安装客户端插件【web访问】
- rabbitmq-server -detached 启动(后端启动)
- rabbitmqctl stop 停止
- rabbitmqctl add_user admin admin 新增用户
- rabbitmqctl set_user_tags admin administrator 给用户分配管理员权限
5.RabbitMQ基本介绍
组成
- 生产者producer:发送消息的一方
- 虚拟机Virtual host:虚拟主机,用于进行逻辑隔离,最上层的消息路由,yigeVirtual Host里面可以有若干个Exchange和Queue(类似于mysql中的数据库)
- 交换机Exchange:接收消息,根据规则转发消息到绑定的队列上
- 队列Queue:保存消息并将消息转发给消费者
- 消费者consumer:接收消息的一方
端口(默认):
通信端口(5672)
管理端口(15762)
集群服务端通信端口(25672)
通信协议
amqp:帧头,负载,帧尾
管理控制台
Overview:监控页(首页,概要)
Connections:查看当前连接情况(有多少程序当前正在连接rabbitmq)
Channels:通道(会话)
Exchanges:交换机,转发器
Queues:队列【存储消息】
Admin:管理端用户权限管理
6RabbitMQ工作模式
5.1 Work queues【工作队列】
一条消息只会被一个消费者接收【一个消息只能被消费一次】
多个消费端消费同一个队列中的消息,队列采用轮询的方式将消息是平均发送给消费者;
5.2 Publish/Subscribe【发布订阅】
同一个消息可以被多个消费者消费【一个消息可能会消费多次】
生产者把消息发送给交换机,交换机将消息转发到绑定队列
使用场景
RabbitMQ订阅模式适用于许多不同的分布式应用场景,特别是需要消息广播和多个订阅者的情况。
- 日志处理: 当应用程序生成大量日志信息时,订阅模式可用于广播日志消息给多个消费者,以便实时监视和分析日志。
- 通知和广播: 在需要向多个接收者发送通知或广告的情况下,订阅模式是一个理想的选择。例如,社交媒体平台可以使用订阅模式将用户发布的消息广播给他们的关注者。
- 分布式系统事件处理: 在分布式系统中,各个组件之间可能需要共享事件信息。订阅模式可以用来传播系统事件,以便不同组件可以根据需要采取行动。
- 实时数据更新: 当多个客户端需要接收实时数据更新时,订阅模式可以用来传递这些数据。例如,在线游戏可以使用订阅模式将玩家的位置和状态信息广播给其他玩家。
- 分布式监控和警报: 在监控系统中,多个监视器可以订阅不同的监控指标,并在发生异常或问题时接收警报通知。
- 数据分发: 当需要将数据分发给多个消费者进行处理时,订阅模式可以确保数据在多个节点之间传播,以实现并行处理。
- 发布-订阅架构: 订阅模式通常用于构建发布-订阅架构,其中消息生产者发布消息,而多个订阅者可以选择订阅自己感兴趣的消息主题。
- 广播事件触发: 在事件驱动的系统中,订阅模式可以用于广播事件触发,以通知所有订阅者事件已发生。
总的来说,RabbitMQ订阅模式适用于需要消息广播和多个订阅者的分布式应用场景。它提供了一种高效的方式来将消息传递给多个接收者,以满足实时通信和协作的需求。无论是用于日志处理、通知、实时数据更新还是其他广播需求,订阅模式都是一个强大的工具。
优点和缺点
RabbitMQ订阅模式是一种强大的消息传递模式,适用于需要消息广播给多个订阅者的分布式应用场景。然而,它也具有一些优点和缺点,以下是关于RabbitMQ订阅模式的主要优点和缺点:
优点
- 消息广播: 订阅模式允许消息生产者将消息广播给多个订阅者,确保消息能够被多个接收者同时处理。这对于实现消息广播和通知非常有用。
- 松耦合: 订阅模式可以实现生产者和消费者之间的松耦合,因为生产者不需要知道谁是消息的接收者,而消费者也不需要知道消息的来源。这有助于提高系统的可维护性和扩展性。
- 消息过滤: 消费者可以选择订阅自己感兴趣的消息主题,而忽略其他不相关的消息。这允许消息的定制化传递。
- 高可扩展性: 订阅模式非常适合处理大量的消息和多个订阅者。系统可以轻松地扩展以满足增加的消息处理需求。
- 实时通信: 订阅模式支持实时通信,因为消息可以立即广播给所有订阅者,从而实现快速响应和实时数据更新。
- 消息持久性: RabbitMQ允许消息的持久性,即使在消息代理重启后,也可以确保消息不会丢失。
缺点
- 复杂性: 配置和管理订阅模式可能比点对点通信更复杂。需要定义主题和绑定队列,以确保消息按预期传递。
- 消息顺序: RabbitMQ的订阅模式不保证消息的处理顺序与它们被发送的顺序完全一致。这对于依赖消息顺序的应用程序可能会带来挑战。
- 消息传递延迟: 在大规模的订阅模式中,可能会出现消息传递延迟,因为需要将消息传递给多个订阅者。这可能会影响实时性要求较高的应用程序。
- 数据一致性: 当有多个订阅者处理相同的消息时,需要小心处理数据一致性问题。确保多个订阅者的操作不会相互干扰或导致数据不一致。
总结
总的来说,RabbitMQ订阅模式是一种非常有用的消息传递模式,适用于需要消息广播和多个订阅者的分布式应用场景。虽然它具有一些复杂性和挑战,但通过适当的设计和配置,可以克服这些问题,实现高效的消息广播和通知。选择使用订阅模式还要考虑应用程序的需求和复杂性,以确保它是正确的消息传递模式。
5.3 Routing【路由模式】
路由模式很灵活,生产者发送消息的时候指定routingkey路由规则,交换机会根据路由规则将消息转发到匹配的队列。
使用场景
- 日志处理: 在日志处理应用中,不同类型的日志消息可能需要被路由到不同的处理器或存储位置。使用路由模式,可以根据日志级别、来源或其他属性将日志消息发送到特定队列。
- 事件通知: 当应用程序需要向特定用户或组件发送事件通知时,路由模式是一个理想的选择。例如,电子商务网站可以使用路由模式将订单状态更新通知发送给相关用户。
- 多租户系统: 在多租户环境中,每个租户可能需要独立处理其数据和事件。使用路由模式,可以将消息路由到特定租户的队列,以实现数据隔离和独立处理。
- 分布式监控: 在监控系统中,不同类型的监控数据可能需要被路由到不同的处理器或仪表板。路由模式可用于将监控数据分发给相关的监控组件。
- 消息过滤: 当需要根据消息内容或属性对消息进行筛选和分发时,路由模式可以帮助实现精确的消息过滤和路由。
- 任务分发: 在分布式任务处理系统中,不同类型的任务可能需要被分发给不同的工作者。使用路由模式,可以将任务路由到适当的工作者队列。
- 安全日志审计: 在安全应用中,日志审计数据通常需要被路由到专门的审计日志存储或审计工具,以进行安全审计和分析。
- 多语言支持: 如果应用程序支持多种语言或区域,并且需要将消息发送到特定语言或区域的接收者,路由模式可以根据消息的语言属性进行路由。
优点:
- 精确路由控制: 路由模式允许生产者根据消息的特定属性或条件,将消息发送到与之匹配的队列,从而实现精确的消息路由和分发。
- 多样化的路由规则: 你可以定义多个路由规则,将消息路由到不同的队列,以满足多种消息处理需求。这允许你根据需要配置不同的路由策略。
- 松耦合: 路由模式可以实现生产者和消费者之间的松耦合,因为生产者不需要知道消息的最终目的地,只需要设置正确的路由键。这提高了系统的可维护性和扩展性。
- 消息过滤: 路由模式允许消费者只关注它们感兴趣的消息,而忽略其他消息。这有助于减轻不必要的消息处理负担。
- 适用于多种应用场景: 路由模式适用于许多应用场景,包括日志处理、事件通知、分布式监控、多租户系统等等。
缺点:
- 复杂性: 配置和管理路由模式可能比其他消息传递模式更复杂,因为需要定义交换机(exchange)、队列和绑定关系。这可能增加了系统的复杂性。
- 路由键设计: 正确设计和管理路由键是关键,如果路由键不合理或不一致,可能导致消息无法正确路由到目标队列。
- 消息流量不均衡: 如果某些队列的消息流量比其他队列大得多,可能会导致不均衡的消息处理。需要考虑负载均衡策略。
- 性能开销: 当需要根据多个条件进行路由时,可能会引入性能开销。复杂的路由规则可能导致消息代理的性能下降。
- 消息顺序: 路由模式不保证消息的处理顺序与它们被发送的顺序完全一致。这对于依赖消息顺序的应用程序可能会带来挑战。
总结:
总的来说,RabbitMQ路由模式是一种非常有用的消息传递模式,特别适用于需要精确消息路由和多样化路由规则的分布式应用场景。虽然它具有一些复杂性和挑战,但通过正确的设计和配置,可以充分发挥其优点,实现精确的消息处理和分发。选择使用路由模式还要根据应用程序的需求和复杂性进行权衡。
5.4 Topics【通配符模式】
路由模式的升级,生产者发送消息指定routingkey,交换机根据routingkey动态匹配规则转发到符合条件的队列上。
与路由模式的区别就在于:路由规则变成动态的了,支持*匹配一个词,#匹配一个或者多个词
使用场景
RabbitMQ通配符模式是一种非常灵活的消息传递模式,适用于许多分布式应用场景,特别是需要根据消息内容和属性进行精确的路由和过滤的情况。以下是一些适合使用通配符模式的常见场景:
- 在日志处理应用中,不同类型的日志消息可能需要被路由到不同的处理器或存储位置。使用通配符模式,可以根据日志类型、来源、级别等属性将日志消息发送到特定的队列中。
- 事件通知: 当应用程序需要向特定用户、组件或区域发送事件通知时,通配符模式是一个理想的选择。例如,社交媒体平台可以使用通配符模式将不同类型的通知分发给关注者。
- 多租户系统: 在多租户环境中,每个租户可能需要独立处理其数据和事件。使用通配符模式,可以将消息路由到特定租户的队列,以实现数据隔离和独立处理。
- 分布式监控: 在监控系统中,不同类型的监控数据可能需要被路由到不同的处理器或仪表板。通配符模式可用于将监控数据分发给相关的监控组件。
- 消息过滤: 当需要根据消息内容或属性对消息进行精确的筛选和分发时,通配符模式可以帮助实现灵活的消息过滤和路由。
- 任务分发: 在分布式任务处理系统中,不同类型的任务可能需要被分发给不同的工作者。使用通配符模式,可以根据任务属性将任务路由到适当的工作者队列。
- 分布式系统通信: 在分布式系统中,不同的模块或微服务可能需要根据特定条件进行通信。通配符模式可以用于精确地指定通信路由。
- 实时数据分发: 当需要将实时数据分发给多个消费者进行处理时,通配符模式可以确保数据按照规则分发给不同的接收者。
总的来说,RabbitMQ通配符模式适用于需要灵活、精确的消息路由和过滤的分布式应用场景。它提供了强大的工具,允许根据消息的属性、条件或路由键将消息路由到特定的队列,以满足不同的消息传递需求。通配符模式的配置可能复杂,但通过合理的设计和规则定义,可以实现高效的消息处理和分发。选择使用通配符模式还要根据应用程序的需求和复杂性进行权衡。
优点和缺点
RabbitMQ通配符模式是一种强大的消息传递模式,允许根据消息的属性、条件或路由键进行精确的消息路由和过滤。然而,通配符模式也具有一些优点和缺点,以下是关于RabbitMQ通配符模式的主要优点和缺点:
优点
- 精确路由控制: 通配符模式允许生产者使用通配符形式的路由键来描述消息的特性,从而实现精确的消息路由和分发。
- 多样化的路由规则: 你可以定义多个通配符规则,将消息路由到不同的队列,以满足多种消息处理需求。这允许你根据需要配置不同的通配符规则。
- 灵活的过滤: 消费者可以使用通配符匹配来订阅感兴趣的消息,这意味着你可以根据消息的属性、条件或路由键进行消息过滤,只接收与订阅规则匹配的消息。
- 松耦合: 通配符模式可以实现生产者和消费者之间的松耦合,因为生产者不需要知道消息的最终目的地,只需设置正确的通配符路由键。这提高了系统的可维护性和扩展性。
- 适用于多种应用场景: 通配符模式适用于多种应用场景,包括日志处理、事件通知、消息过滤、多租户系统等等。
缺点
- 复杂性: 配置和管理通配符模式可能比其他消息传递模式更复杂,因为需要定义交换机(exchange)、队列和绑定关系,以及通配符规则。这可能增加了系统的复杂性。
- 路由键设计: 正确设计和管理通配符模式的路由键和绑定键是关键。不正确的通配符使用可能导致消息无法正确路由到目标队列。因此,需要仔细设计通配符规则,以确保消息的精确路由。
- 性能开销: 当需要根据多个条件进行路由时,可能会引入性能开销。复杂的通配符规则可能导致消息代理的性能下降。
- 消息流量不均衡: 如果某些队列的消息流量比其他队列大得多,可能会导致不均衡的消息处理。需要考虑负载均衡策略。
总的来说,RabbitMQ通配符模式是一种非常有用的消息传递模式,特别适用于需要灵活、精确的消息路由和过滤的分布式系统。虽然它具有一些复杂性和挑战,但通过正确的设计和配置,可以充分发挥其优点,实现高效的消息处理和分发。选择使用通配符模式还要根据应用程序的需求和复杂性进行权衡。
总结
RabbitMQ通配符模式是一种非常有用的消息传递模式,它允许根据消息的特性和条件进行精确的消息路由和过滤。通过使用通配符形式的路由键和绑定键,你可以定义复杂的消息匹配规则,以满足不同应用场景的需求。
需要注意的是,正确地设计和管理通配符模式的路由键和绑定键是关键。不正确的通配符使用可能导致消息无法正确路由到目标队列。因此,需要谨慎设计通配符规则,以确保消息的精确路由。
总的来说,RabbitMQ通配符模式为实现灵活的消息路由和过滤提供了一个强大的工具,适用于多种应用场景,包括日志处理、事件通知、消息过滤等等。根据应用程序的需求,可以灵活配置通配符规则,以满足不同的消息传递需求。
5.5 Header【单薄模式】
Headers类型的exchange使用的比较少,它也是忽略routingKey的一种路由方式。是使用Headers来匹配的。Headers是一个键值对,可以定义成Hashtable。发送者在发送的时候定义一些键值对,接收者也可以再绑定时候传入一些键值对,两者匹配的话,则对应的队列就可以收到消息。匹配有两种方式all和any。这两种方式是在接收端必须要用键值"x-mactch"来定义。all代表定义的多个键值对都要满足,而any则代码只要满足一个就可以了。fanout,direct,topic exchange的routingKey都需要要字符串形式的,而headers exchange则没有这个要求,因为键值对的值可以是任何类型。
headers 模式为:单薄模式,1对1
headers 匹配规则:any 、all
any: 只要在发布消息时携带的有一对键值对headers满足队列定义的多个参数的其中一个就能匹配上,注意这里是键值对的完全匹配,只匹配到键了,值却不一样是不行的;
all:在发布消息时携带的所有Entry必须和绑定在队列上的所有Entry完全匹配
缺点:
Headers 类型的交换器性能会很差
5.6 RPC【远程过程调用】
其过程简单理解为客户端去调用远程服务器的某个功能,将所需要的数据传递过去,远程服务器接受到数据后去实现该功能,并把该功能的结果再返回给客户端去处理,可以实现跨语言。
在RabbitMQ中,使用RPC通信方式的应用程序被称为RPC客户端,而提供服务的应用程序被称为RPC服务器。当RPC客户端发送请求时,消息属性要设置上reply_to的回调队列,且将一个唯一的标识符(correlation_id)作为消息的一部分发送给RPC服务器。RPC服务器在处理请求并生成响应时,将使用该唯一标识符将响应发送回RPC客户端。
- reply_to:当客户端发送一个RPC请求时,它需要告诉RPC服务器如何将响应消息发送回客户端。reply_to字段用于指定一个用于接收响应的队列名称,RPC服务器将在该队列上发送响应消息。如果客户端没有指定reply_to字段,则RPC服务器将无法发送响应消息。
- correlation_id:correlation_id字段用于将RPC请求和响应消息进行匹配。当客户端发送一个RPC请求时,它应该生成一个唯一的correlation_id值,并将其包含在请求消息中。当RPC服务器生成响应消息时,它应该使用相同的correlation_id值将响应消息发送回客户端。客户端将使用correlation_id值来将响应消息与先前发送的请求消息进行匹配。
缺点
虽然RabbitMQ的RPC通信方式可以实现客户端和服务端之间的解耦,实现远程调用,但也会涉及一些缺点:
- 性能开销:RPC通信方式需要在客户端和服务端之间进行多次网络通信,而每次网络通信都会带来一定的性能开销,因此在高并发场景下,RPC通信可能会影响系统的性能。
- 实现复杂度:RPC通信方式需要客户端和服务端共同定义一套RPC协议,包括消息格式、参数类型和返回值类型等,这需要一定的实现复杂度,尤其是在跨语言和跨平台的场景中。
- 依赖可靠的消息代理:RPC通信需要可靠的消息代理来实现消息传递和消息保证,而RabbitMQ等消息代理的可靠性需要进行专门的配置和管理,增加了运维成本。
- 安全性:由于RPC通信涉及到网络传输,可能会面临数据被劫持、篡改和窃听等安全风险,因此需要采取相应的安全措施,如加密、认证和授权等。
7.SpringBoot整合RabbitMQ
8.RabbitMQ高频面试题
1.如何保证Rabbit消息的可靠传输
丢失消息的情况
(1) 发送端消息没有发送成功,消息没有到达MQ。
消息已经到达队列,还没有被消费,MQ服务宕机了
(2)
(3) 消费端消费的过程中出错了,导致消息没有处理完成,队列中就把消息删除了
保证三个点:
(1) 发送端发送消息
(2) 队列中存储消息
(3) 接收端消费消息
保证以上过程是可靠的
发送端
(1)开启Confirm模式 (消息确认机制或者事务模式)
(2) 如果消息发送失败做补偿处理,记录日志重新发送等,确保消息最终发送成功
开启confirm确认模式:
(消息发送到交换机的结果回调)
参数:
spring.rabbitmq.publisher-confirm-type = correlated
回调函数: rabbitTemplate.setConfirmCallback((CorrelationData correlationData, boolean ack, String cause) -> {
//CorrelationData correlationData:可以封装业务ID信息,需要在发送消息时传入此参数,这里才能接收到,否则是null。
//boolean ack:消息发送的结果状态,发送成功是true,失败是false。
//String cause:发送失败的描述信息,如果发送成功是null。
if(!ack){ System.out.println(correlationData.getId()+":消息发送失败,失败原因:"+cause);
}
});
开启return消息机制:
(失败回调:交换机到队列的结果)
参数:
spring.rabbitmq.publisher-returns = true
回调函数:
rabbitTemplate.setReturnCallback((Message message, int replyCode, String replyText, String exchange, String routingKey)-> {
//Message message:发送的消息内容 + 发送消息的配置信息。
//int replyCode:状态码,发送成功是200。
//String replyText:发送消息失败的描述信息。
//String exchange:消息使用的交换机
//String routingKey:消息使用的路由键。 System.out.println(message.getMessageProperties().getDeliveryTag()+":消息发送失败,状态码:"+replyCode+"失败原因:"+replyText);
});
队列
(1)声明交换机,队列的时设置持久化参数
(2)发送消息的时候设置消息的持久化参数,队列消息持久化到磁盘上,即便是宕机,重启之后消息依旧还在.
(1)声明交换机的时候设置交换机持久化参数:durable=true
(2)声明队列的时候设置队列持久化参数:durable=true
(3)发送消息的时候设置消息持久化:MessageProperties.PERSISTENTTEXTPLAIN
接收端
(1)接收端开启手动应答模式。
(2)确定消息处理完成,消费端再向队列确认消费,删除消息
(3)消费端如果处理异常,记录日志,通过其他补偿机制处理,确保最终消息处理成功。
接收端开启手动应答模式:
spring.rabbitmq.listener.simple.acknowledge-mode = manual
消息处理完成后确认: channel.basicAck(message.getMessageProperties().getDeliveryTag(),true);
2.异常的消息改怎么处理
存日志【处理次数】,程序补偿【定时器】,通知人工【人工干预】
3.消息堆积改如何处理
在消息堆积发生时:
4.如何保证消息不被重复消费
重复消费的原因:
(1)发送端重复发送相同的内容。
(2)消费端手动应答,还没有应答之前,出现异常。导致消息还存在队列。
1,每次消费记录消息的唯一标识,通过每个消息的唯一标识判断是否消费过
2,通过消费端处理,每条消息分配唯一的id,消费端在消费的时候验证,过滤掉重复的消息。
3,使用乐观锁实现,保证接口的幂等性
5.下单以后,用户不支付该如何处理
方案1:
定时查询未支付的订单,判断是否超时,超时则关闭订单,返还库存。
方案2:
通过mq的死信队列实现延迟消费。下单成功之后,发送订单信息到队列,30分钟后消费端消费,判断是否支付,未支付则关闭订单,返还库存。
方案3:
基于Rabbitmq插件实现延迟队列