消息队列核心技术(持续整理)

消息队列的应用场景

1.异步处理(用户注册后,需要发注册邮件和注册短信。)
2.应用解耦(用户下单后,订单系统需要通知库存系统。)
3.流量削锋(秒杀活动,一般会因为流量过大,导致流量暴增,应用挂掉。为解决这个问题,一般需要在应用前端加入消息队列。)
4.消息通讯(消息队列一般都内置了高效的通信机制,因此也可以用在纯的消息通讯。比如实现点对点消息队列,或者聊天室等)

使用了消息队列会有什么缺点

  • 系统可用性降低。引入消息队列之后,如果消息队列挂了,可能会影响到业务系统的可用性。
  • 系统复杂性增加。加入了消息队列,要多考虑很多方面的问题,比如:一致性问题、如何保证消息不被重复消费、如何保证消息可靠性传输等

如何保证消息不重复消费

1.最简单的解决方案是每条消费记录有个消费状态字段,根据这个消费状态字段来是否消费或者使用一个集中式的表,来存储所有消息的消费状态,从而避免重复消费
2.采用消息幂等消费 (幂等性就是一个数据或者一个请求,给你重复来了多次,你得确保对应的数据是不会改变的,不能出错。)
3.分布式锁。生产者发送每条数据的时候,里面加一个全局唯一的 id,类似订单 id 之类的东西,然后你这里消费到了之后,先根据这个 id 去比如 Redis 里查一下,之前消费过吗?如果没有消费过,你就处理,然后这个 id 写 Redis。如果消费过了,那你就别处理了,保证别重复处理相同的消息即可。
4.唯一键防重。基于数据库的唯一键来保证重复数据不会重复插入多条。因为有唯一键约束了,重复数据插入只会报错,不会导致数据库中出现脏数据。
5.先查后写。要写数据库前,先根据主键查一下,如果这数据都有了,你就别插入了,update一下好了。
6.关闭重试机制。如果把重试机制关掉的话不显示,虽然解决了重复消费的问题,但是可能会造成丢失消息,不建议这么做。

kafka重复消费的问题

kafka有一个叫做offset的概念,就是每个消息写进去,都有一个offset代表他的序号,然后consumer消费了数据之后,每隔一段时间,会把自己消费过的消息的offset提交一下,代表我已经消费过了,下次就算重启,kafka就会让消费者从上次消费到的offset来继续消费。如果consumer消费了数据,还没来得及发送自己已经消费的消息的offset就挂了,那么重启之后就会收到重复的数据。

消息堆积怎么处理
消息堆积原因

主要有两种原因:
 第一种:消费方消费的太慢或消费方出现异常。
 第二种:生产方生产的太快,其实就是消费的速度赶不上生产的速度,生产和消费速度不匹配导致的。

1.后台定时任务每隔72小时,删除旧的没有使用过的消息信息
2.根据不同的业务实现不同的丢弃任务,选择不同的策略淘汰任务,例如FIFO/LRU等
3.消息定时转移,或者对某些重要的TAG型(支付型)消息真正落库 

消息丢失怎么处理

RabbitMQ
生产者弄丢了数据
1.可以选择用rabbitmq提供的事务功能,在生产者发送数据之前开启rabbitmq事务,但是问题是,开始rabbitmq事务机制,基本上吞吐量会下来,因为太耗性能。
2.可以开启confirm模式,在生产者那里设置开启confirm模式之后,你每次写的消息都会分配一个唯一的id,然后如果写入了rabbitmq中,rabbitmq会给你回传一个ack消息,告诉你说这个消息ok了。如果rabbitmq没能处理这个消息,会回调你一个nack接口,告诉你这个消息接收失败,你可以重试。而且你可以结合这个机制自己在内存里维护每个消息id的状态,如果超过一定时间还没接收到这个消息的回调,那么你可以重发。
3.事务机制和cnofirm机制最大的不同在于,事务机制是同步的,你提交一个事务之后会阻塞在那儿,但是confirm机制是异步的,你发送个消息之后就可以发送下一个消息,然后那个消息rabbitmq接收了之后会异步回调你一个接口通知你这个消息接收到了。
rabbitmq弄丢了数据
1.为了防止rabbitmq自己弄丢了数据,这个你必须开启rabbitmq的持久化,就是消息写入之后会持久化到磁盘
消费端弄丢了数据
2.这个时候得用rabbitmq提供的ack机制,简单来说,就是你关闭rabbitmq自动ack,可以通过一个api来调用就行,然后每次你自己代码里确保处理完的时候,再程序里ack一把。这样的话,如果你还没处理完,不就没有ack?那rabbitmq就认为你还没处理完,这个时候rabbitmq会把这个消费分配给别的consumer去处理,消息是不会丢的。
Kafka
消费者弄丢了数据
大家都知道kafka会自动提交offset,那么只要关闭自动提交offset,在处理完之后自己手动提交offset,就可以保证数据不会丢。但是此时确实还是会重复消费,比如你刚处理完,还没提交offset,结果自己挂了,此时肯定会重复消费一次,自己保证幂等性就好了。
Kafka弄丢了数据
比较常见的一个场景,就是kafka某个broker宕机,然后重新选举partiton的leader时。大家想想,要是此时其他的follower刚好还有些数据没有同步,结果此时leader挂了,然后选举某个follower成leader之后,他不就少了一些数据?这就丢了一些数据啊。
所以此时一般是要求起码设置如下4个参数:
1.给这个topic设置replication.factor参数:这个值必须大于1,要求每个partition必须有至少2个副本
2.在kafka服务端设置min.insync.replicas参数:这个值必须大于1,这个是要求一个leader至少感知到有至少一个follower还跟自己保持联系,没掉队,这样才能确保leader挂     了还有一个follower吧
3.在producer端设置acks=all:这个是要求每条数据,必须是写入所有replica之后,才能认为是写成功了
4.在producer端设置retries=MAX(很大很大很大的一个值,无限次重试的意思):这个是要求一旦写入失败,就无限重
试,卡在这里了
生产环境就按照上述要求配置的,这样配置之后,至少在kafka broker端就可以保证在leader所在broker发生故
障,进行leader切换时,数据不会丢失
生产者弄丢了数据
按照上述的思路设置了ack=all,一定不会丢,要求是,你的leader接收到消息,所有的follower都同步到了消息之后,才认
为本次写成功了。如果没满足这个条件,生产者会自动不断的重试,重试无限次。

如何保证消息不丢失

消息丢失的原因

情况一:消息的生产者没有成功发送到MQ Broker

情况二:消息发送给MQ Broker后,Broker宕机导致内存中的消息数据丢失

情况三:消费者消费了消息,但是没有处理完毕就出现异常导致丢失

确保消息不丢失方案

  1. 消息发送者发送给MQ Broker后,MQ Broker给生产者确认收到
  2. MQ收到消息后进行消息持久化
  3. 消费者收到消息处理完毕后手动进行ack确认
  4. MQ收到消费者ack确认后删除持久化的消息

发送方可靠发送

MQ进行消息持久化

消费方消费完进行ack确认,MQ收到消费方的ack确认在删除本地消息

如何保证消息不被重读消费?如何保证消息消费的幂等性?

  • 对重复消息产生的原因思考
  • 对保证消息幂等性的方案

重读消息产生的原因

发送重读消息
当一条消息已被成功发送到服务端,此时出现了网络闪断,导致服务端对客户端的应答失败。如果此时生产者意识到消息发送失败并尝试再次发送消息,消费者后续会收到两条相同的消息

消费时消息重复
消息消费的场景下,消息已经投递到消费者并完成业务处理,当消费方给MQ服务端反馈应答的时候网络闪断,为了保证消息至少被消费一次,MQ服务端将在网络恢复后再次尝试投递之前之前已被消费方处理过的消息,此时消费者就会收到两条相同的消息。

消息幂等性
1.消息发送者发送消息时携带一个全局唯一的消息ID
2.消费者获取消费后根据id在redis/db中查询是否存在消费记录
3.如果没有消费过就正常消费,消费完毕后写入redis/db
4.如果消息消费过就直接舍弃

如何保证消息消费的顺序性

1、考察是否理解什么是顺序消息

2、考察消费者是否是思考过确保消息顺序消费的方案

消息有序指的是可以按照消息的发送顺序来消费。

什么是消息的顺序消费?

  • 消费方按照消息发送的顺序进行消费,分为全局消费消息和局部消费消息
  • 最常见的是局部顺序消费

如何保证消息的顺序消费?

  • 全局顺序消费,生产者:MQ:消费者=1:1:1
  • 局部顺序消息:
  1. 生产者将同一组消息发送到单个队列
  2. 多个消费者并行对消息进行消费
  3. Queue通过分段锁保证消息消费的顺序性

消息队列实现最终一致性

1.事务机制表示消息队列中的消息是否拥有状态,从而决定消费者是否消费该条消息。RoketMQ的事务机制是将消息标记为Prepared状态或者Confirmed状态。处于Prepared状态的消息对consumer不可见。而Kafka通过Transaction Marker将消息标记为Uncommited或Commited状态。Consumer通过配置isolation-
level为read_committed或read_uncommitted来决定对哪种类型的消息可见。
2.如果消息队列不支持事务消息,那么我们的解决方案是,新增一张message表,并开启一个定时任务扫描这张message表,将所有状态为prepared的message发送给消息队列,发送成功后,将message状态置为confirmed。此时插入order和插入message的逻辑处于同一个数据库事务,通过后台的定时程序不断扫描message表,因此一定能够保
证消息被成功投递到消息消费方。这个方案存在的一个问题是,有可能后台任务发送消息成功后宕机了,从而没有来得及将已发送的message状态置为confirmed。因此下一次扫描message表时,会重复发送该条消息。这就是at least once delivery。由于at least once delivery的特性,consumer有可能收到重复的数据。此时可以在consumer端建立一张message_consume表,来判断消息是否已经消费过,如果已经消费过,那么就直接丢弃该消息。 

如何保证消息的顺序性

1.单线程消费来保证消息的顺序性
2.对消息进行编号,消费者处理时根据编号判断顺序。 

消息的重发补偿策略

可靠消息服务定时查询状态为已发送并超时的消息
可靠消息将消息重新投递到 MQ 组件中
下游应用监听消息,在满足幂等性的条件下,重新执行业务。
下游应用通知可靠消息服务该消息已经成功消费。
通过消息状态确认和消息重发两个功能,可以确保上游应用、可靠消息服务和下游应用数据的最终一致性。 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

泡^泡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值