JAVA面试如何保证消息不被重复消费?如何保证消息消费的幂等性?

狂野女子


其实这是很常见的一个问题,这俩问题基本可以连起来问。既然是消费消息,那肯定要考虑会不会重复消费?能不能避免重复消费?或者重复消费了也别造成系统异常可以吗?这个是 MQ 领域的基本问题,其实本质上还是问你使用消息队列如何保证幂等性,这个是你架构里要考虑的一个问题。

面试题剖析

回答这个问题,首先你别听到重复消息这个事儿,就一无所知吧,你先大概说一说可能会有哪些重复消费的问题。

首先,比如 RabbitMQ、RocketMQ、Kafka,都有可能会出现消息重复消费的问题,正常。因为这问题通常不是 MQ 自己保证的,是由我们开发来保证的。挑一个 Kafka 来举个例子,说说怎么重复消费吧。

Kafka 实际上有个 offset 的概念,就是每个消息写进去,都有一个 offset,代表消息的序号,然后consumer 消费了数据之后,每隔一段时间(定时定期),会把自己消费过的消息的 offset 提交一下,表示“我已经消费过了,下次我要是重启啥的,你就让我继续从上次消费到的 offset 来继续消费吧”。

但是凡事总有意外,比如我们之前生产经常遇到的,就是你有时候重启系统,看你怎么重启了,如果碰到点着急的,直接 kill 进程了,再重启。这会导致 consumer 有些消息处理了,但是没来得及提交 offset,尴尬了。重启之后,少数消息会再次消费一次。


夜玫瑰163


今天,让我们一起看看保证 MQ 的幂等性有哪些方案。


01. 幂等性的概念

先说说什么是幂等性。

当用户对同一操作请求了一次或者多次(消息发送或接收了多次),最终的结果是一致的,并不会因为多次请求产生副作用;比如同一个订单支付了两次,最后应该只扣客户一次钱。

  • 查询和删除:查询具有天然的幂等性,在数据不变的前提下,相同查询条件查询一次和查询多次的结果都是一样的;删除也一样,相同的条件删除一次和删除多次,可能删除的数据量不一样,但是数据库中的数据不会因为执行了多次删除而不同。

  • 新增和修改:如果不做幂等性处理,可能就会产生问题;执行多次新增操作,可能会导致一模一样的数据产生了多条(主键自动生成);修改操作,如果只是把某些字段更新成固定的值,不会有幂等性问题,但是如果新值要在旧值上做处理做计算,如增加多少、减少多少,那么多次执行的结果就会有差异。

02. MQ 消息出现非幂等的情况

1. 生产者发送消息给 MQ,为了保证消息可达,通常会使用超时重传机制,但是如果生产者的消息已经发出去,但是由于网络原因未收到确认信息,那么可能会进行重发,最终造成了消息的重复发送。

2. 消费者消费的过程也类似,消费者接受到 MQ 的消息,但是 MQ 未收到确认信息,那么该条消息可能会重新发送给其他消费者,或者网络恢复再次发送给消费者,最终造成重复消费。


03. 解决方法

1. 唯一索引

使用唯一索引,可以有效的防止新增脏数据:当表中存在唯一索引的时候,并发新增相同数据的时候就会报错,不过这在单库单表的时候才有效,如果项目数据量很大,采用了分库分表的策略,就不能再通过数据库的唯一性索引来解决幂等性的问题了。


2. 全局唯一 ID

每条消息,都携带一个全局的唯一 ID,消费者接收到消息,在执行前判断这个 ID 是否已经在本地存在,如果不存在,则执行交易后记录 ID(存到数据库或Redis中,表示该交易已经执行);如果已经存在,表示消息已经消费掉了,不能再次执行。

许多分布式架构中,生成全局唯一 ID 都会被作为一个基础的微服务,当然这个服务的可靠性要求极高,或者可以使用全局唯一 ID 算法,由每个应用自己生成。不过总的来说,引入全局唯一 ID 这个方案,实现起来还是非常繁琐的。

3. 业务状态

保证消息的幂等性,也可以结合业务流程考虑,比如很多业务流程每一步都是有状态的,比如网上购物可能会有:订单创建、付款、发货,那么付款之前保单状态为“待付款”,付款之后可以将保单的状态修改为“待发货”;那么如果发起重复扣款的话,第二次扣款的时候保单状态已经变化了,就会扣款失败。

4. 去重表

如果业务中有唯一性的标识时,可以使用去重表,把这个唯一性的表示保存到去重表中,如果重复插入,那么会被校验住。

比如上面的场景,一个订单只会付款一次,那么在付款的时候,把订单号作为唯一性的标识,保存到去重表中,可以保证付款操作只会发生一次;这个方法也用到了唯一 ID,不过和全局唯一 ID 不同,这个唯一 ID 是针对具体业务的。

我将持续分享Java开发、架构设计、程序员职业发展等方面的见解,希望能得到你的关注。


会点代码的大叔


太复杂了简短文字难以说明问题,比如说:“Kafka 实际上有个 offset 的概念,就是每个消息写进去,都有一个 offset,代表消息的序号,然后 consumer 消费了数据之后,每隔一段时间(定时定期),会把自己消费过的消息的 offset 提交一下,表示“我已经消费过了,下次我要是重启啥的,你就让我继续从上次消费到的 offset 来继续消费吧”。


来者不俗


这要从提安全性,同步设置,标注同步,注销同步设置,这是软件没置问题。如果做到标记与消费信息实现同步,就不会出现重复的问题了。


覃绍继


问答来了,我可说,面试等我不会重复消费,因为老人连什么什么都不认识,绝对不会走漏风声,慎重保密无重复。下次再答,谢谢!


某30094317


以上的这个问题我确实知道的很少,我无法回答对不起我让楼主失望了!


zhangyong7660



御v虹v宇v勋


这个问题太容易回答,根本就不知道这是什么问题,没法回答,只能说不知道。


雷神159299026


这个问题比较犀利,因为我不是这个专业人员,希望有专业人员帮你答复


心心月亮湾


这个问题提的好,我感觉都快崩溃了。原因很简单,我不懂,从中实在找不出乐趣来。


分享到:


相關文章: