This commit is contained in:
by931
2022-09-06 22:30:37 +08:00
parent 66970f3e38
commit 3d6528675a
796 changed files with 3382 additions and 3382 deletions

View File

@@ -183,7 +183,7 @@ function hide_canvas() {
<p>这个问题在实际工作中很常见,既能考察候选者对于 MQ 中间件技术的掌握程度,又能很好地区分候选人的能力水平。接下来,我们就从这个问题出发,探讨你应该掌握的基础知识和答题思路,以及延伸的面试考点。</p>
<h3>案例背景</h3>
<p>以京东系统为例,用户在购买商品时,通常会选择用京豆抵扣一部分的金额,在这个过程中,交易服务和京豆服务通过 MQ 消息队列进行通信。在下单时,交易服务发送“扣减账户 X 100 个京豆”的消息给 MQ 消息队列,而京豆服务则在消费端消费这条命令,实现真正的扣减操作。</p>
<p><img src="assets/CgpVE2AICf-AIldlAACI4Qo9Nv4750.png" alt="1.png" /></p>
<p><img src="assets/CgpVE2AICf-AIldlAACI4Qo9Nv4750.png" alt="png" /></p>
<p>那在这个过程中你会遇到什么问题呢?</p>
<h3>案例分析</h3>
<p>要知道,在互联网面试中,引入 MQ 消息中间件最直接的目的是:做系统解耦合流量控制,追其根源还是为了解决互联网系统的高可用和高性能问题。</p>
@@ -204,7 +204,7 @@ function hide_canvas() {
<p><strong>候选人在回答时,要先让面试官知道你的分析思路,然后再提供解决方案:</strong> 网络中的数据传输不可靠,想要解决如何不丢消息的问题,首先要知道哪些环节可能丢消息,以及我们如何知道消息是否丢失了,最后才是解决方案(而不是上来就直接说自己的解决方案)。就好比“架构设计”“架构”体现了架构师的思考过程,而“设计”才是最后的解决方案,两者缺一不可。</p>
<h3>案例解答</h3>
<p>我们首先来看消息丢失的环节,一条消息从生产到消费完成这个过程,可以划分三个阶段,分别为消息生产阶段,消息存储阶段和消息消费阶段。</p>
<p><img src="assets/Cip5yGAICkGAI7vpAAEcjkYXvaA495.png" alt="2.png" /></p>
<p><img src="assets/Cip5yGAICkGAI7vpAAEcjkYXvaA495.png" alt="png" /></p>
<p>消息的生产、存储与消费</p>
<ul>
<li><strong>消息生产阶段:</strong> 从消息被生产出来,然后提交给 MQ 的过程中,只要能正常收到 MQ Broker 的 ack 确认响应,就表示发送成功,所以只要处理好返回值和异常,这个阶段是不会出现消息丢失的。</li>
@@ -219,13 +219,13 @@ function hide_canvas() {
<p><strong>回答完“如何确保消息不会丢失?”之后,面试官通常会追问“怎么解决消息被重复消费的问题?”</strong> 比如:在消息消费的过程中,如果出现失败的情况,通过补偿的机制发送方会执行重试,重试的过程就有可能产生重复的消息,那么如何解决这个问题?</p>
<p>这个问题其实可以换一种说法,就是如何解决消费端幂等性问题(幂等性,就是一条命令,任意多次执行所产生的影响均与一次执行的影响相同),只要消费端具备了幂等性,那么重复消费消息的问题也就解决了。</p>
<p>我们还是来看扣减京豆的例子,将账户 X 的金豆个数扣减 100 个,在这个例子中,我们可以通过改造业务逻辑,让它具备幂等性。</p>
<p><img src="assets/CgpVE2AICsCAYHgNAAF3z8OC8eg779.png" alt="3.png" /></p>
<p><img src="assets/CgpVE2AICsCAYHgNAAF3z8OC8eg779.png" alt="png" /></p>
<p>扣减京豆</p>
<p><strong>最简单的实现方案,就是在数据库中建一张消息日志表,</strong> 这个表有两个字段:消息 ID 和消息执行状态。这样,我们消费消息的逻辑可以变为:在消息日志表中增加一条消息记录,然后再根据消息记录,异步操作更新用户京豆余额。</p>
<p>因为我们每次都会在插入之前检查是否消息已存在,所以就不会出现一条消息被执行多次的情况,这样就实现了一个幂等的操作。当然,基于这个思路,不仅可以使用关系型数据库,也可以通过 Redis 来代替数据库实现唯一约束的方案。</p>
<p>在这里我多说一句,想要解决“消息丢失”和“消息重复消费”的问题,有一个前提条件就是要实现一个全局唯一 ID 生成的技术方案。这也是面试官喜欢考察的问题,你也要掌握。</p>
<p>在分布式系统中,全局唯一 ID 生成的实现方法有数据库自增主键、UUID、RedisTwitter-Snowflake 算法,我总结了几种方案的特点,你可以参考下。</p>
<p><img src="assets/Ciqc1GAIDXuAZ2iUAAIGj0vJThg862.png" alt="4.png" /></p>
<p><img src="assets/Ciqc1GAIDXuAZ2iUAAIGj0vJThg862.png" alt="png" /></p>
<p>我提醒你注意,无论哪种方法,如果你想同时满足简单、高可用和高性能,就要有取舍,所以你要站在实际的业务中,说明你的选型所考虑的平衡点是什么。我个人在业务中比较倾向于选择 Snowflake 算法,在项目中也进行了一定的改造,主要是让算法中的 ID 生成规则更加符合业务特点,以及优化诸如时钟回拨等问题。</p>
<p><strong>当然,除了“怎么解决消息被重复消费的问题?”之外,面试官还会问到你“消息积压”。</strong> 原因在于消息积压反映的是性能问题,解决消息积压问题,可以说明候选者有能力处理高并发场景下的消费能力问题。</p>
<p><strong>你在解答这个问题时,依旧要传递给面试官一个这样的思考过程:</strong> 如果出现积压,那一定是性能问题,想要解决消息从生产到消费上的性能问题,就首先要知道哪些环节可能出现消息积压,然后在考虑如何解决。</p>