1.消息中间件的核心设计思想:
采用异步通讯、自动补偿与重试、分布式事务、解决流量削峰问题、系统的解耦
2.消息中间件常用名词:
Broker 消息转发端,消息中间件Server端;
Message 发送的消息内容
roducer 生产者,向Server端投递消息;
Consumer 消费者,向Server端获取消息
MessageId 消息全局id 解决消息幂等性问题
3.主流的MQ对比分析
ActiveMQ: 基本淘汰(老项目使用) 够轻巧(源代码比RocketMQ多),支持持久化到数据库,
对队列数较多的情况支持不好。
RabbitMQ: 结合erlang语言本身的并发优势,支持很多的协议:AMQP,XMPP, SMTP, STOMP,
也正是如此,使的它变的非常重量级,更适合于企业级的开发。
RocketMQ: 阿里系下开源的一款分布式、队列模型的消息中间件,原名Metaq,3.0版本名称改为RocketMQ,
是阿里参照kafka设计思想使用java实现的一套mq,同时将阿里系内部多款mq产品
(Notify、metaq)进行整合,只维护核心功能,去除了所有其他运行时依赖,
保证核心功能最简化,在此基础上配合阿里上述其他开源产品实现不同场景下mq的架构,
目前主要多用于订单交易系统。
Kafka: Apache下的一个子项目,使用scala实现的一个高性能分布式Publish/Subscribe消息队列系统,
具有以下特性:高吞吐:在一台普通的服务器上既可以达到10W/s的吞吐速率;
高堆积:支持topic下消费者较长时间离线,消息堆积量大;
4.RabitMQ环境的基本安装
1.下载并安装erlang,下载地址:http://www.erlang.org/download
2.配置erlang环境变量信息
新增环境变量ERLANG_HOME=erlang的安装地址
将%ERLANG_HOME%\bin加入到path中
3.下载并安装RabbitMQ,下载地址:http://www.rabbitmq.com/download.html
注意: RabbitMQ 它依赖于Erlang,需要先安装Erlang。
5.Virtual Hosts:
像mysql有数据库的概念并且可以指定用户对库和表等操作的权限。那RabbitMQ呢?
RabbitMQ也有类似的权限管理。在RabbitMQ中可以虚拟消息服务器VirtualHost,每
个VirtualHost相当月一个相对独立的RabbitMQ服务器,每个VirtualHost之间是相互
隔离的。exchange、queue、message不能互通。
6.公平队列实现原理
Mq服务器端每次只会给消费者发送一条消息,如果消费者没有返回ack,就不会继续发送消
息。
7.如何保证消息不丢失?
1.生产者 确保我们的生产者将消息投递到MQ成功; 消息确认机制如果开启了消息持久化的机制,必须消息持久化成功才会应答给生产者
// 开启生产确认消息投递机制 channel.confirmSelect();
channel.waitForConfirms()==true 投递成功
2.消费者 确保我们的消费者消费消息成功 采用手动ack确认
3.MQ服务器端 需要将数据持久化到我们的硬盘
其他情况下: 硬盘坏了、持久化的过程断电了?
如何解决 最好通过表记录每次生产者投递消息,如果长期没有被消费,手动的补偿消费。
8.如果在生产者投递消息失败的情况,在那些场景?
1.MQ挂了 解决做心跳检测(heartbeat),自动重启,多次重启失败发邮件运维
2.Mq拒绝接受消息 (队列满了) 就采用手动补偿或者日志表记录下即可
9.Rabbitmq如何开启持久化的功能?
1.默认的情况下mq服务器端创建队列和交换机都是持久化的
2.如果是代码创建的话,将该值设置为durablet
10.Rabbitmq发布订阅的实现原理:
核心思想:
一个生产者投递消息,可以被多个不同的队列实现消费;
实现原理:
多个不同的队列绑定相同交换机,生产者只需要将消息投递到交换机之后,
在由交换机将消息转发到所有绑定的队列实现消费。
11.Direct exchange(直连<路由>交换机)
Fanout exchange(扇型<广播>交换机)
Topic exchange(主题交换机)
交换机核心作用:分发路由消息、中专
队列:容器存放多个不同消息 遵循先进先出的原则
消息:传递的参数
路由键:交换机根据这样的路由键的值,发送不同的队列中 匹配过程
扇型交换机主要的特征:只要队列绑定同一个交换机,生产者将消息投递到交换机中,交换机会将消息发送给所有绑定的队列进行存放消息。
Direct exchange(直连交换机):根据生产者投递不同的路由键,在交换机发送到队列实现匹配路邮件
Topic 主题交换机:根据路邮键的key实现模糊匹配到队列存放。
12. RabbitMQ如果产生了消息堆积如何处理?
产生的背景:如果没有及时的消费者消费消息,生产者一直不断往队列服务器存放消息
会导致消息堆积
两种场景:
1.没有消费者消费的情况下: 死信队列、设置消息有效期
相当于对我们的消息设置有效期,在规定的时间内如果没有消费的话,自动过期,
过期的时候会执行客户端回调监听的方法将消息存放到数据库记录,后期实现不补偿。
2.有一个消费者消费的情况:应该提高我们的消费者 消费实现集群
13. RabbitMQ如何彻底保证我们的消息不丢失?
1.MQ服务器端应该消息持久化到硬盘
2.生产者使用消息确认机制百分能够将消息投递到MQ成功
3.消费者使用手动acm机制确认消息百分百消费成功
如果队列容量满了,在继续投递可能会丢失 死信队列
死信队列:称做为备胎队列,消息中间件队列因为某种消费拒绝存放该消息,可以转移到死信队列中存放。
死信队列产生的背景:
1.生产者投递消息到MQ中,消息过期了,转入死信队列;
2.队列的已经达到最大长度(队列存放消息满了)MQ拒绝接受存放该消息。
3.消费者多次消费该消息失败的情况,也会存放死信。
死信队列不能够和正常队列存放在同一个服务器中,应该分开服务器存放
14.订单30分钟未支付,系统自动超时关闭有哪些实现方案?
1.基于任务调度实现,效率是非常低,耗服务器性能
2.基于redis过期key实现.用户下单的时候,生成一个令牌(有效期)30分钟,存放到我们redis;
redis.set(orderToken ,orderID) 下单时候存放到redis,并存储id入库,30分钟过期,
redis客户端监听,过期获取到orderId,拿orderId去查订单,没有支付则,订单关闭,库存增加
缺点:非常冗余 ,会在表中存放一个冗余字段
3.基于MQ的延迟队列实现(最佳) 死信队列
原理:当我们在下单的时候,往MQ投递一个消息设置有效期为30分钟,但该消息失效的时候(没有被消费的情况下),
执行我们客户端一个方法告诉我们该消息已经失效,这时候查询这笔订单是否有支付.
具体步骤:下单投放消息到A交换机(过期时间30分钟),消息到aa队列(绑定死信交换机),不设置aa队列的消费者.
30分钟后,过期消息投递到死信交换机,死信队列,又死信消费者消费,判断订单id
时候支付,支付->return 未支付->关闭订单,返还库存
4.redis的延迟队列 https://blog.csdn.net/zhangshengqiang168/article/details/100130523
15.RabbitMQ自动补偿机制触发:(多用于调用第三方接口)
1.当我们的消费者在处理我们的消息的时候,程序抛出异常情况下(默认无限次数重试)
2.应该对我们的消息重试设置间隔重试时间,比如消费失败最多只能重试5次,间隔3秒(防止重复消费,幂等问题)
16.如果重试5次,也就是15秒内重试还是失败情况下应该如何处理?
1.默认情况下,重试多次还是失败的话,会自动删除该消息(消息可能会丢失)
解决思路:
A:如果重试多次还是失败的情况下,最终存放到死信队列.
B:采用表日志记录,消费失败错误的日志记录 后期人工自动对消息实现补偿.
17.分布式事务产生的背景?
1.RPC通讯中每个服务都有自己独立的数据源,每个数据源都互不影响.
2.在单个项目中存在多个不同jdbc连接(多数据源)
18.如何基于我们的MQ解决我们的分布式事务的问题(最终一致性)
1.确保我们的生产者往我们的MQ投递消息一定要成功.(生产者消息确认机制confirm),实现重试.
2.确保我们的消费者能够消费成功(手动ack机制),如果消费失败情况下,MQ自动帮消费者重试.
3.确保我们的生产者第一事务先执行成功,如果执行失败采用补单队列.
来源:CSDN
作者:zhangshengqiang168
链接:https://blog.csdn.net/zhangshengqiang168/article/details/104753526