公司网站更换域名滕州网站建设哪家好

张小明 2026/1/11 16:29:32
公司网站更换域名,滕州网站建设哪家好,红色风格网站,猪八戒 网站开发支付今天我们来聊一个消息队列问题#xff0c;“如何在 Kafka 中实现延迟消息#xff1f;” 这其实是一道非常见功底的题目。为什么这么说#xff1f;因为 Kafka 原生并不支持延迟消息#xff0c;这是它的基因决定的——它是一个追加写的日志系统#xff08;Append-only Log“如何在 Kafka 中实现延迟消息”这其实是一道非常见功底的题目。为什么这么说因为 Kafka 原生并不支持延迟消息这是它的基因决定的——它是一个追加写的日志系统Append-only Log设计之初就是为了追求极致的顺序读写和吞吐量而不是为了灵活的随机读取。要在这样一个要求“实时性”和“顺序性”的系统上“魔改”出延迟功能就如同给一辆 F1 赛车装上倒挡考验的不仅仅是你对 Kafka 原理的理解更是你面对业务痛点时的架构设计能力、方案权衡能力以及对分布式系统一致性、可用性的深度思考。今天我就不做简单的知识搬运工了。接下来我就结合我过往在电商交易核心系统的实战经验带你把这个话题彻底拆解开来。我们要聊的不仅仅是一个功能的实现更是一场关于存储选型、分布式一致性、高并发优化的架构思维风暴。1. 什么是延迟消息在切入正题之前我们必须先搞清楚一个核心问题什么是延迟队列很多同学容易把“延迟队列”和“延迟消息”混为一谈。从数据结构的角度看延迟队列Delay Queue是一种特殊的队列里面的元素带有过期时间时间未到谁也拿不走。Java JDK 里的DelayQueue就是典型代表它基于堆排序保证队头永远是最先过期的元素。但在分布式架构的语境下我们讨论的“延迟消息”通常是指基于消息中间件的一种特殊能力生产者把消息发出去后不希望下游立刻消费而是希望消息在中间件里“暂存”一段时间比如 30 分钟倒计时结束后自动投递给消费者。最经典的场景莫过于电商里的“订单超时取消”。用户下单锁库存但迟迟不支付。我们不能让库存一直被占着也不能每秒钟去轮询数据库那是对数据库的 DDoS 攻击。最优解就是发一条 30 分钟的延迟消息时间一到消费者收到消息去检查订单状态没付钱就直接关单回库。2. 其他 MQ 是怎么解决的在面试中如果你上来就死磕 Kafka可能会显得视野不够开阔。一个成熟的架构师通常会先横向对比看看业界其他方案是怎么做的。如果你的公司技术栈允许选择原生支持延迟消息的 MQ 往往是成本最低的。2.1 RabbitMQ 的“插件黑科技”RabbitMQ 社区提供了一个非常有名的插件rabbitmq_delayed_message_exchange。很多中小厂喜欢用它因为简单开箱即用。这个插件的实现思路非常“野路子”。它并没有修改队列的 FIFO 特性而是自定义了一种特殊的 Exchange交换机。当你把消息发给这个 Exchange 时它并不会像普通交换机那样查路由表、找队列、投递消息。相反它会把消息“扣留”下来。它利用了 Erlang 语言内置的一个名为Mnesia的数据库。这里需要稍微展开一下 Mnesia 的机制你可以把 Mnesia 想象成一个轻量级的、嵌入式的数据库它支持 RAM内存和 Disc磁盘两种存储模式。当消息带有x-delay头时插件会将消息体序列化后存储在 Exchange 所在节点的 Mnesia 表中。只有当消息上的延迟时间到了这个插件的定时器触发才会把消息从 Mnesia 里捞出来正式投递到真正的队列中这时候消费者才能看到。但作为架构师你必须看到它的致命弱点这决定了它能不能上核心生产环境数据安全隐患消息在被投递前是存在节点本地的 Mnesia 里的。RabbitMQ 的高可用队列Mirrored Queues保护的是队列里的数据而不是 Exchange 里的数据。如果这个节点还没来得及把 Mnesia 的数据刷到磁盘就宕机了或者磁盘损坏了那这些还没到期的消息就彻底丢了。性能天花板Mnesia 并不是为海量数据设计的。它在处理大量并发写入和过期扫描时性能远不如专业的存储引擎。如果你的业务量巨大延迟消息堆积成山这个插件的性能会急剧下降甚至拖垮整个 RabbitMQ 节点。所以高并发、大数据量场景下严禁使用此方案。2.2 RabbitMQ 的“死信”玩法除了插件RabbitMQ 还有一个经典的“土办法”就是利用 TTL生存时间配合死信队列DLX。这个逻辑其实是把“死信”变成了“活信”。我们创建一个没有任何消费者的队列姑且叫它delay_queue。我们给这个队列——或者给发到这个队列的消息——设置一个过期时间比如 5 分钟。消息进了这个队列因为没人消费只能干等着。5 分钟一到消息“死”了。这时候RabbitMQ 的死信机制DLX就会触发。它会把这条过期的消息自动转发给一个预先配置好的死信交换机Dead Letter Exchange这个交换机再把消息路由到一个正常的业务队列biz_queue里。消费者守在biz_queue这一头拿到消息的时候刚好过去了 5 分钟。这个方案虽然避免了插件的单点风险但它太死板了。TTL 通常是设置在队列上的。如果你既有 3 分钟的延迟又有 10 分钟的延迟你就得建两个队列。如果业务方说“我要一个随机延迟时间比如 3 分 25 秒”那这个方案就直接宣告破产因为你不可能为每一秒都建一个队列。3. 面试前的战略准备在正式深入 Kafka 方案之前我建议大家先梳理一下自己公司的业务背景。这一步至关重要它决定了你选择方案的合理性。你得搞清楚几个问题你们公司真的有延迟消息的刚需吗现有的中间件是 Kafka 吗能不能换延迟消息的量级是多少QPS 是几百还是几万延迟的时间跨度是固定的如 15 分钟还是随机的面试的时候话术可以这样引导“在介绍我的项目时虽然我们用的是 Kafka但涉及到订单超时这种业务由于 Kafka 原生不支持我们做了一套架构方案……”或者反过来“当时选型时考虑到 Kafka 在延迟消息上的短板而我们的业务又强依赖这个功能所以我们权衡之后选了 RocketMQ……”4. 基础方案从简单到原生接下来我们进入实战环节。针对 Kafka我有几套不同维度的解决方案丰俭由人。4.1 基于定时任务这是最简单、最直观也是最容易被忽视的方案。既然 Kafka 存不住延迟消息那我们就找个能存的地方。比如业务方产生了一个“20 分钟后发送”的请求我们不要直接发给 Kafka而是把它注册到一个支持持久化的定时任务平台比如 Quartz 集群或者由 XXL-JOB 调度的服务。这个定时任务系统的逻辑很简单它就是一个倒计时器。当 20 分钟时间一到调度器触发任务把这条消息作为普通的生产消息发送给 Kafka。这时候下游的消费者就能正常处理了。怎么向面试官推销这个方案你可以坦诚地说“在业务量不大、对延迟精度要求不苛刻的初期阶段我们采用了最稳妥的定时任务方案。它的好处是架构简单利用了现有的任务调度中间件开发成本极低。”然后话锋一转指出缺点“但这个方案的短板也很明显那就是并发能力不足。绝大多数定时任务系统是为‘低频、批处理’设计的比如每天凌晨跑报表。如果你让它去承载每秒几万单的实时延迟触发数据库和调度线程池瞬间就会被打爆。所以对于高并发场景我们必须寻求更原生的解决方案。”4.2 基于分区的“时间桶”策略如果不想依赖外部系统想在 Kafka 内部闭环解决我们可以利用 Kafka 的分区Partition机制来做文章。这个方案的核心思想是把不同的分区当成不同的‘时间桶’来用。我们可以创建一个专门的delay_topic。在这个 Topic 下我们人为地约定分区的含义分区 0专门存放延迟1 分钟的消息分区 1专门存放延迟5 分钟的消息分区 2专门存放延迟30 分钟的消息。业务发送端在发消息时需要根据业务逻辑比如是短信提醒还是超时关单判断需要的延迟时间然后把消息路由到对应的分区里。而在消费端我们需要实现一个特殊的“转发服务”。这个服务组建一个消费者组组内的每个消费者只负责一个特定的分区。比如消费者 A它被分配了分区 230 分钟延迟。当它拉取到一条消息时它会解析消息体发现这是个 30 分钟延迟的消息。然后它会看一眼当前时间如果还没到点它就原地等待如果时间到了它就把这条消息转发给真正的业务 Topic。思考发散为什么是用“不同分区”而不是“不同 Topic”这是一个很好的架构思考题。除了分区方案我们当然也可以创建 delay_1m_topic、delay_5m_topic。用多 Topic 的好处是隔离性更好消费者逻辑更简单不需要手动分配分区。但缺点是如果延迟档位很多会产生大量的 Topic增加 Kafka Controller 的元数据管理负担。而用单 Topic 多分区管理起来更收敛但消费者逻辑稍显复杂。通常为了运维方便我们更倾向于单 Topic 多分区的模式。这个方案听起来很完美但在落地实施时有两个巨大的“深坑”这也是面试中能体现你技术深度的关键点。4.2.1 难点 一重平衡问题大家知道Kafka 的消费者模型里有一个核心机制心跳与活跃检测。消费者需要定期向 Coordinator 发送心跳heartbeat.interval.ms证明自己还活着。更重要的是消费者必须在 max.poll.interval.ms 配置的时间内完成消息的处理并再次发起 poll 请求。在我们的场景里消费者 A 拉到消息后因为时间没到它可能会选择休眠Sleep来等待。如果这个延迟时间是 30 分钟而 Kafka 默认的 poll 间隔上限只有 5 分钟问题就大了。Coordinator 会认为这个消费者“死”了因为迟迟没有发起下一次 poll于是触发Rebalance。它会把分区 2 剥夺尝试分配给组里的其他消费者。而那个“假死”的消费者 A 醒来后发现自己负责的分区已经没了之前的等待全部白费整个消费者组也会陷入不断的重平衡震荡中。Pause Resume为了解决这个问题我们在代码层面绝对不能傻傻地Thread.sleep。我们需要利用 Kafka Consumer 提供的流控 API。这里我给出一个简单的伪代码逻辑大家可以感受一下// 伪代码示例安全的延迟消费循环 while (true) { // 1. 拉取消息 ConsumerRecordsString, String records consumer.poll(Duration.ofMillis(100)); for (ConsumerRecord record : records) { long delayTime parseDelayTime(record); long waitTime delayTime - System.currentTimeMillis(); if (waitTime 0) { // 2. 关键一步暂停拉取 // 告诉 Kafka 客户端暂停这几个分区的 fetch但心跳线程继续跑 // 这样 Coordinator 就不会认为我们“死”了 consumer.pause(consumer.assignment()); // 3. 安全休眠 // 这里的 sleep 不会影响心跳发送因为心跳通常由后台线程处理 try { Thread.sleep(waitTime); } catch (InterruptedException e) { // 处理中断逻辑 } // 4. 恢复拉取 consumer.resume(consumer.assignment()); } // 5. 转发消息 forwardToBizTopic(record); } }这一步consumer.pause()非常关键。它仅仅是暂停了数据的获取但后台的心跳线程依然在勤恳地工作告诉 Coordinator “我还活着别踢我”。这个知识点非常隐蔽是区分普通开发者和高级玩家的分水岭。4.2.2 难点二一致性问题第二个坑在于数据处理的顺序。我们的转发服务要做两件事转发消息给业务 Topic提交 Offset给 Kafka。这中间存在一个原子性问题。路径 A先提交 Offset再转发。如果 Offset 提交成功了但在转发的一瞬间服务宕机了。重启后因为 Offset 已经提交这条消息就不会再被拉取。结果就是消息丢了。这在金融或订单场景下是绝对不可接受的。路径 B先转发再提交 Offset。如果转发成功了但在提交 Offset 之前宕机了。重启后消费者会重新拉取这条消息并再次转发。结果就是消息重复。业务下游会收到两条一模一样的订单取消请求。架构师的判断在分布式系统中宁可重复不可丢失At Least Once。所以我们必须选择路径 B。这也引出了一个必须强调的结论下游业务消费者必须实现幂等性。无论是为了应对这里的重复转发还是应对网络抖动造成的重发幂等设计都是系统健壮性的基石。5. 高阶方案基于 MySQL 的极致优化如果面试官继续追问“我的业务场景很复杂延迟时间是随机的不支持固定分区而且 QPS 极高你这几套方案都扛不住怎么办”这时候前面的方案确实都捉襟见肘了。我们需要引入更强大的存储引擎——数据库来构建一个通用的、企业级的延迟消息服务。这个方案的架构更为宏大我们引入了几个核心角色Delay Topic入口这是所有延迟消息的蓄水池。业务方不管延迟多久先把消息发到这里。Delay Consumer搬运工它负责从 Kafka 拉取消息解析出元数据尤其是执行时间然后把消息写入到后端存储MySQL中。MySQL持久化存储这是整个方案的心脏承载所有“未到期”的消息。Delay Sender调度员这是一个扫描服务它不断轮询数据库查找那些“执行时间 当前时间”且状态为“未发送”的消息。一旦捞到就把它发给真正的biz_topic并更新数据库状态。这个架构的逻辑很清晰但它的死穴也很明显MySQL 的性能瓶颈。在高并发场景下海量的写入请求加上高频的轮询扫描分分钟就能把 MySQL 打挂。所以这个方案的成败全在于如何对 MySQL 进行极致的性能压榨。这也是你展示架构功力的时刻。5.1 分区表与“表轮转”策略延迟消息有一个非常鲜明的特点时效性极强。我们只关心“未来”的数据一旦消息被投递它就成了毫无价值的“历史尘埃”。针对这个特点我们可以利用 MySQL 的分区表特性。我们可以按时间维度比如按小时、按天对表进行物理分区。这样在扫描时我们只需要扫当前时间片的分区效率极高。而对于过期的历史分区我们可以直接使用DROP PARTITION这比逐行DELETE快无数倍还不会产生磁盘碎片。更进一步我们可以采用“表轮转Table Rotation”的策略或者叫“双 Buffer”策略。比如我们设定延迟不超过 24 小时。我们可以准备两张表tab_0 和 tab_1。今天奇数日所有的写操作都进tab_0调度员也只扫tab_0。明天偶数日立刻切换到tab_1。在切换的瞬间tab_0里的数据理论上都已经过期并发走了。我们可以直接对tab_0执行TRUNCATE操作。为什么是 TRUNCATE 而不是 DELETE从数据库底层来看DELETE 是 DML 操作它会一行行删除数据并且为了保证事务回滚会产生大量的 Undo Log同时还会造成索引碎片甚至可能导致页分裂。而 TRUNCATE 是 DDL 操作它直接丢弃表的数据文件重新创建一个新的速度快零负担。这是一把重剑瞬间清空百万级数据为后天做准备。5.2 分库分表与“轮询写入”如果单表优化到了极致还是扛不住每秒几万的并发写入那就必须祭出分布式数据库的法宝——分库分表。通常的分库分表我们是按业务 ID比如 OrderId取模来路由的。但在延迟消息这个场景下这样做有隐患。想象一下如果某个大客户Big Key发了海量延迟消息比如双十一某个大主播的直播间订单按 ID 取模会导致所有数据都压在某一张表上造成严重的数据倾斜这张表会成为整个系统的短板导致 CPU 飙升。在这里我们要采用一种反常规的策略轮询写入Round-Robin。我们不关心消息属于哪个业务只关心把数据库的 IO 打满且打匀。假设我们分了 32 张表。第 1 条消息无脑写表 1第 2 条消息无脑写表 2……第 32 条消息写表 32第 33 条消息又回到表 1。这样做的好处是惊人的无论业务流量如何波动这 32 张表的负载永远是绝对均匀的没有任何热点。我们的扫描服务Delay Sender只需要开启多线程并发地去扫这 32 张表吞吐量直接翻倍。5.3 消息有序性的回归采用了轮询写入后虽然性能上去了但带来了一个副作用乱序。同一个订单的“创建延迟”和“取消延迟”可能落到了不同的表里扫描时可能会先扫到后面的消息。如果业务方强依赖消息顺序这就不行了。为了解决这个问题我们需要在架构上做一点妥协。我们可以在分库分表算法上做文章确保同一个 biz_topic 的消息或者同一个 Partition Key 的消息总是落到同一张物理表里。虽然这可能会牺牲一点负载均衡如果某个 Topic 特别热但它保证了局部有序性。在写入 Kafka 的 delay_topic 时我们也按照同样的规则选择分区这样就形成了一条从 Kafka 分区 - 数据库表 - 下游 Topic 的有序通道。5.4 批量操作与并发控制最后千万别忘了数据库操作的基本准则能批量绝不单条。批量写入Delay Consumer 在拉取到 Kafka 消息后不要来一条插一条。在内存里攒个几百毫秒或者攒够 100 条拼成一个巨大的INSERT INTO ... VALUES (...), (...)...语句一次性推给 MySQL。这能极大减少网络 RTT 和数据库事务开销。批量更新与乐观锁Delay Sender 在扫描时为了避免多个调度线程抢同一个任务通常会使用乐观锁。-- 调度员扫描时先抢占任务 UPDATE delay_table SET status 2, sender_ip 192.168.1.1 WHERE execute_time NOW() AND status 0 LIMIT 100; -- 然后查出刚刚抢到的任务进行发送 SELECT * FROM delay_table WHERE status 2 AND sender_ip 192.168.1.1;这种方式利用了数据库的行锁既保证了并发安全又利用批量更新提升了性能。6. 小结回顾我们的方案演进之路我们从最简单的定时任务聊到了 Kafka 原生的分区策略最后演进到了基于 MySQL 分库分表的高可用架构。你会发现技术方案没有绝对的银弹。如果你的业务是起步阶段量小图快定时任务就是最好的选择。如果你的业务量中等且延迟时间固定Kafka 分区方案性价比最高但一定要小心 Rebalance 和一致性的坑。如果你面对的是海量并发、随机延迟的复杂场景那么基于 MySQL 的分库分表 轮询写入 表轮转才是你应该拿出来的架构利剑。在面试中能把这些方案的演进路径、取舍理由以及底层原理如 Rebalance 机制、Truncate 原理讲得头头是道你给面试官留下的印象绝对是深刻的。希望这篇文章能帮你把“延迟消息”这个知识点彻底吃透下次在面试或架构设计时能胸有成竹地打出这一套组合拳
版权声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

汽车销售网站建设wordpress悬浮音乐播放

STM32F103开发第一步:Keil5芯片库添加实战指南 你是不是刚买了块“蓝丸”(Blue Pill)开发板,满心欢喜地打开Keil5,准备写第一个LED闪烁程序,结果新建工程时—— 搜遍全网都找不到STM32F103这个型号&#…

张小明 2026/1/11 16:03:02 网站建设

北京网站建设V芯ee8888e怎么给网站做动图

Keil5芯片包下载与安装全攻略:从零构建ARM Cortex-M开发环境 你是不是也遇到过这样的场景?刚装好Keil MDK,信心满满地新建工程,结果在“Select Device”界面怎么也搜不到自己的STM32芯片;或者编译时弹出一连串错误&am…

张小明 2026/1/10 13:41:55 网站建设

网站建设模拟淘宝店可以做团购的网站

VoxCPM-1.5-TTS-WEB-UI:让高性能语音合成真正触手可及 你有没有遇到过这样的情况:想做个有声读物项目,或者给数字人配个自然的嗓音,结果发现主流TTS模型要么音质像“机器人念经”,要么部署起来要装十几个依赖、调三天…

张小明 2026/1/10 13:41:55 网站建设

网站建设流程有哪些天津做app和网站的公司

服务集成模式:编排与聚合报告的深度解析 1. 编排模式概述 在服务交互中,服务总线模式实现了服务间的解耦通信,降低了服务交互的技术壁垒。然而,业务流程的构建成为了新的挑战。业务流程指的是为实现业务目标,服务间相关消息的传递序列。例如,简单的购物车场景就需要客户…

张小明 2026/1/10 13:41:57 网站建设

网站正在维护模板小企业网站建设包含哪些

第一章:实在智能 Open-AutoGLM 到底有多强实在智能推出的 Open-AutoGLM 是一款面向自动化场景的大语言模型框架,深度融合了自然语言理解与任务执行能力,专为复杂业务流程的智能化改造而设计。该模型不仅具备传统大模型的语言生成优势&#xf…

张小明 2026/1/10 13:41:59 网站建设

自己开网站工作室网站设计与程序方向

SSH X11转发显示PyTorch可视化图形界面 在深度学习项目开发中,一个常见的场景是:你手头只有一台轻薄笔记本,却需要训练一个庞大的神经网络模型。显然,本地算力捉襟见肘,于是自然想到使用远程GPU服务器——但问题来了&…

张小明 2026/1/10 13:42:00 网站建设