<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Kafka on 追风笔记</title><link>https://f91dba72.wangpeng.pages.dev/tags/kafka/</link><description>Recent content in Kafka on 追风笔记</description><generator>Hugo</generator><language>zh</language><lastBuildDate>Tue, 25 Aug 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://f91dba72.wangpeng.pages.dev/tags/kafka/index.xml" rel="self" type="application/rss+xml"/><item><title>Kafka 存储与消费模型</title><link>https://f91dba72.wangpeng.pages.dev/tech/kafka/storage-consumer/</link><pubDate>Tue, 25 Aug 2026 00:00:00 +0000</pubDate><guid>https://f91dba72.wangpeng.pages.dev/tech/kafka/storage-consumer/</guid><description>&lt;p&gt;Kafka 的高性能依赖于「分区日志 + 顺序写 + 批量拉取」的设计。理解存储与消费模型，才能合理配置并行度与避免重复消费。&lt;/p&gt;&#10;&lt;h2 id="topic-与-partition"&gt;Topic 与 Partition&#10;&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;topic&lt;/strong&gt; 是逻辑主题，&lt;strong&gt;partition&lt;/strong&gt; 是物理并行单位，消息只追加写入分区末尾。&lt;/li&gt;&#10;&lt;li&gt;每个分区是一个有序、不可变的日志，由多个 segment 文件组成，并维护 offset（分区内唯一递增）。&lt;/li&gt;&#10;&lt;li&gt;分区数决定了消费的并行上限：一个分区同一时刻只被 group 内一个消费者消费。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="offset-与消费位移"&gt;Offset 与消费位移&#10;&lt;/h2&gt;&#10;&lt;p&gt;消费者处理完消息后提交的位移称为 &lt;strong&gt;offset&lt;/strong&gt;，Kafka 将其保存在内部 topic &lt;code&gt;__consumer_offsets&lt;/code&gt; 中：&lt;/p&gt;&#10;&lt;div class="td-code td-code--untitled" id="td-code-de365f5c-fence-0" data-td-code data-td-code-auto-id&#10; data-td-language="text" data-td-line-count="4"&gt;&#10; &lt;div class="td-code__viewport" id="td-code-de365f5c-fence-0-viewport" data-td-code-viewport&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Producer -&amp;gt; [partition-0: 0,1,2,3 ...]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ^&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; | committed offset (group A)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Consumer group A 从 offset=4 继续拉取&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;位移提交方式影响可靠性：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;自动提交&lt;/strong&gt;：间隔提交，可能丢失（提交后未处理崩溃）或重复（处理中崩溃未提交）。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;手动提交&lt;/strong&gt;：处理成功后再提交，配合幂等可实现至少一次/精确一次。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="consumer-group-与-rebalance"&gt;Consumer Group 与 Rebalance&#10;&lt;/h2&gt;&#10;&lt;p&gt;同一 group 内的消费者共同消费 topic 的全部分区，分区在成员间均衡分配。当成员加入/退出时触发 &lt;strong&gt;rebalance&lt;/strong&gt;，重新分配分区。频繁 rebalance 会暂停消费，应控制 &lt;code&gt;session.timeout&lt;/code&gt; 与 &lt;code&gt;heartbeat.interval&lt;/code&gt; 并避免消费耗时过长。&lt;/p&gt;</description></item><item><title>Kafka 可靠性与精确一次</title><link>https://f91dba72.wangpeng.pages.dev/tech/kafka/reliability/</link><pubDate>Tue, 25 Aug 2026 00:00:00 +0000</pubDate><guid>https://f91dba72.wangpeng.pages.dev/tech/kafka/reliability/</guid><description>&lt;p&gt;Kafka 的可靠性需要在 producer、broker、consumer 三端协同配置。下面从「不丢、不重、有序」三个目标展开。&lt;/p&gt;&#10;&lt;h2 id="producer-端的可靠性"&gt;Producer 端的可靠性&#10;&lt;/h2&gt;&#10;&lt;p&gt;通过 &lt;code&gt;acks&lt;/code&gt; 控制写入持久化级别：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;code&gt;acks=0&lt;/code&gt;：发完即认为成功，可能丢消息，吞吐最高。&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;acks=1&lt;/code&gt;：leader 写入即成功，leader 宕机可能丢。&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;acks=all&lt;/code&gt;：ISR 全部同步才成功，最安全，配合 &lt;code&gt;min.insync.replicas&lt;/code&gt; 防单点。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;开启重试（&lt;code&gt;retries&lt;/code&gt;）可应对瞬时失败，但会带来&lt;strong&gt;重复&lt;/strong&gt;：网络抖动导致 producer 未收到 ack 而重试，同一条消息被写两次。&lt;/p&gt;&#10;&lt;h2 id="幂等-producer-与事务"&gt;幂等 Producer 与事务&#10;&lt;/h2&gt;&#10;&lt;p&gt;Kafka 提供 &lt;strong&gt;幂等 producer&lt;/strong&gt;（&lt;code&gt;enable.idempotence=true&lt;/code&gt;），broker 用 &lt;code&gt;producerId + 序列号&lt;/code&gt; 去重，保证单分区内不重不漏。需要跨分区、跨系统的原子写入时，使用&lt;strong&gt;事务&lt;/strong&gt;（&lt;code&gt;transactional.id&lt;/code&gt;）将多次 produce 与 offset 提交纳入一个事务：&lt;/p&gt;&#10;&lt;div class="td-code td-code--untitled" id="td-code-5a2edc9c-fence-0" data-td-code data-td-code-auto-id&#10; data-td-language="java" data-td-line-count="5"&gt;&#10; &lt;div class="td-code__viewport" id="td-code-5a2edc9c-fence-0-viewport" data-td-code-viewport&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;producer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;initTransactions&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;producer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;beginTransaction&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;producer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;producer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;producer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;commitTransaction&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;要么都成功&lt;/span&gt;&lt;span class="err"&gt;，&lt;/span&gt;&lt;span class="n"&gt;要么都不可见&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;/div&gt;&#10;&lt;h2 id="消费端去重与幂等"&gt;消费端去重与幂等&#10;&lt;/h2&gt;&#10;&lt;p&gt;即使 producer 幂等，consumer 在 rebalance 或位移提交时机不当时仍可能重复消费。消费端应保证&lt;strong&gt;业务幂等&lt;/strong&gt;：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;用唯一键（订单号等）做去重表/唯一索引。&lt;/li&gt;&#10;&lt;li&gt;将「处理 + 提交位移」放在同一事务（如消费写 DB 同时记录 offset）。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="分区顺序性"&gt;分区顺序性&#10;&lt;/h2&gt;&#10;&lt;p&gt;Kafka 只保证&lt;strong&gt;单分区内有序&lt;/strong&gt;。需要全局顺序只能单分区（牺牲并行）；需要业务顺序则按 key（如用户 id）分区，使同一 key 落到同一分区，从而在该 key 维度保持顺序。精确一次（EOS）正是幂等 + 事务 + 消费幂等三者叠加的结果。&lt;/p&gt;</description></item><item><title>消息队列选型：Kafka vs Pulsar vs RabbitMQ</title><link>https://f91dba72.wangpeng.pages.dev/columns/data/message-queue/</link><pubDate>Sat, 28 Feb 2026 00:00:00 +0000</pubDate><guid>https://f91dba72.wangpeng.pages.dev/columns/data/message-queue/</guid><description>&lt;p&gt;消息队列是分布式系统的神经系统，但 Kafka、Pulsar、RabbitMQ 三者的设计哲学差异极大。选错不是「换个客户端」的事，而是架构重做。下面从几个工程最关心的维度拆开看。&lt;/p&gt;&#10;&lt;h2 id="模型差异是根本"&gt;模型差异是根本&#10;&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;RabbitMQ&lt;/strong&gt;：经典消息代理，面向「消息」和「队列」，支持丰富路由（直连、主题、头部、扇出）。适合任务分发、低延迟小消息。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Kafka&lt;/strong&gt;：日志型、分区有序、拉取消费，面向「流」和「事件溯源」。适合高吞吐、可重放。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Pulsar&lt;/strong&gt;：计算存储分离，分层架构，原生多租户和跨地域复制。适合既要 Kafka 的流、又要 RabbitMQ 的队列语义的统一平台。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;div class="td-code td-code--untitled" id="td-code-e7471357-fence-0" data-td-code data-td-code-auto-id&#10; data-td-language="bash" data-td-line-count="4"&gt;&#10; &lt;div class="td-code__viewport" id="td-code-e7471357-fence-0-viewport" data-td-code-viewport&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Kafka 顺序消费依赖分区&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;bin/kafka-console-consumer.sh &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --topic orders --bootstrap-server b1:9092 &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --partition &lt;span class="m"&gt;0&lt;/span&gt; --offset earliest&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;/div&gt;&#10;&lt;h2 id="关键维度对比"&gt;关键维度对比&#10;&lt;/h2&gt;&#10;&lt;div class="td-table-scroll td-table-scroll--static"&gt;&#10;&lt;table&gt;&#10; &lt;thead&gt;&#10; &lt;tr&gt;&#10; &lt;th scope="col"&gt;维度&lt;/th&gt;&#10; &lt;th scope="col"&gt;Kafka&lt;/th&gt;&#10; &lt;th scope="col"&gt;Pulsar&lt;/th&gt;&#10; &lt;th scope="col"&gt;RabbitMQ&lt;/th&gt;&#10; &lt;/tr&gt;&#10; &lt;/thead&gt;&#10; &lt;tbody&gt;&#10; &lt;tr&gt;&#10; &lt;td&gt;吞吐&lt;/td&gt;&#10; &lt;td&gt;极高&lt;/td&gt;&#10; &lt;td&gt;极高&lt;/td&gt;&#10; &lt;td&gt;中等&lt;/td&gt;&#10; &lt;/tr&gt;&#10; &lt;tr&gt;&#10; &lt;td&gt;消息模型&lt;/td&gt;&#10; &lt;td&gt;流/日志&lt;/td&gt;&#10; &lt;td&gt;流+队列&lt;/td&gt;&#10; &lt;td&gt;队列&lt;/td&gt;&#10; &lt;/tr&gt;&#10; &lt;tr&gt;&#10; &lt;td&gt;顺序性&lt;/td&gt;&#10; &lt;td&gt;分区内有序&lt;/td&gt;&#10; &lt;td&gt;分区/Key 有序&lt;/td&gt;&#10; &lt;td&gt;队列有序&lt;/td&gt;&#10; &lt;/tr&gt;&#10; &lt;tr&gt;&#10; &lt;td&gt;运维复杂度&lt;/td&gt;&#10; &lt;td&gt;中&lt;/td&gt;&#10; &lt;td&gt;高（含 BookKeeper）&lt;/td&gt;&#10; &lt;td&gt;低&lt;/td&gt;&#10; &lt;/tr&gt;&#10; &lt;tr&gt;&#10; &lt;td&gt;消费模型&lt;/td&gt;&#10; &lt;td&gt;拉取、可重放&lt;/td&gt;&#10; &lt;td&gt;拉取、可重放&lt;/td&gt;&#10; &lt;td&gt;推送&lt;/td&gt;&#10; &lt;/tr&gt;&#10; &lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&#10;&lt;h2 id="选型思路"&gt;选型思路&#10;&lt;/h2&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;&lt;strong&gt;事件流、日志、可重放&lt;/strong&gt; → Kafka 稳。审计、CDC、行为埋点这类场景它的重放能力几乎是刚需。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;统一消息平台、多租户、跨地域&lt;/strong&gt; → Pulsar 更合适，但团队要扛得住运维。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;任务队列、复杂路由、低延迟&lt;/strong&gt; → RabbitMQ 更直接。&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;div class="td-code td-code--untitled" id="td-code-e7471357-fence-1" data-td-code data-td-code-auto-id&#10; data-td-language="java" data-td-line-count="4"&gt;&#10; &lt;div class="td-code__viewport" id="td-code-e7471357-fence-1-viewport" data-td-code-viewport&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 用消息头做复杂路由（RabbitMQ 风格）&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;basicPublish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;orders.topic&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;order.created.vip&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// routingKey&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;props&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;/div&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;别用 RabbitMQ 扛日均百亿事件流，也别用 Kafka 做需要复杂路由的任务分发——用对的工具，比用最强的工具重要。&lt;/p&gt;</description></item><item><title>Kafka 在交易系统的削峰与解耦</title><link>https://f91dba72.wangpeng.pages.dev/blog/kafka-practice/</link><pubDate>Sat, 28 Mar 2026 00:00:00 +0000</pubDate><guid>https://f91dba72.wangpeng.pages.dev/blog/kafka-practice/</guid><description>&lt;p&gt;交易系统引入 Kafka 通常出于两个动机：扛住流量尖峰，以及把非核心逻辑从主链路上摘下来。这两件事它都能做，但做法和注意点完全不同。&lt;/p&gt;&#10;&lt;h2 id="削峰把洪峰变成队列"&gt;削峰：把洪峰变成队列&#10;&lt;/h2&gt;&#10;&lt;p&gt;代发工资、批量还款、营销活动这些场景的流量特征是极不均匀——平时每秒几百笔，活动开始瞬间冲到几万笔。数据库扛不住的不是总量，是瞬时并发。&lt;/p&gt;&#10;&lt;p&gt;削峰的本质是&lt;strong&gt;用延迟换稳定&lt;/strong&gt;：请求先落 Kafka，下游按自己的处理能力匀速消费。关键是消费端要限速，不能拿到消息就火力全开打数据库。&lt;/p&gt;&#10;&lt;div class="td-code td-code--untitled" id="td-code-9fc028c5-fence-0" data-td-code data-td-code-auto-id&#10; data-td-language="yaml" data-td-line-count="11"&gt;&#10; &lt;div class="td-code__viewport" id="td-code-9fc028c5-fence-0-viewport" data-td-code-viewport&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;spring&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;kafka&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;consumer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;group-id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;txn-processor&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;enable-auto-commit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 必须手动提交&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;max-poll-records&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;max.poll.interval.ms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;300000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 留足单批处理时间，避免误判掉线&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;listener&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ack-mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;manual&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;concurrency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 与分区数匹配，不要超过&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;&lt;code&gt;concurrency&lt;/code&gt; 超过分区数没有任何收益，多出来的线程只会空转。要提升并行度得先加分区，而分区数一旦加了就减不回去，前期要按峰值容量规划好。&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;削峰只对&amp;quot;可以延后处理&amp;quot;的业务成立。用户在 App 里点转账等结果的场景不能这么做——那是同步链路，塞消息队列只是把等待转移到了别处。&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;h2 id="解耦从同步调用到事件"&gt;解耦：从同步调用到事件&#10;&lt;/h2&gt;&#10;&lt;p&gt;主链路上挂着一堆下游是很常见的技术债：转账成功后要发短信、更新积分、推送风控特征、写数仓。每加一个下游，主链路的失败面就大一分。&lt;/p&gt;&#10;&lt;p&gt;改成发一条 &lt;code&gt;transfer.posted&lt;/code&gt; 事件，各下游自己订阅。主链路只负责记账和发事件，下游挂了不影响交易成功。&lt;/p&gt;&#10;&lt;p&gt;需要划清界限的是：&lt;strong&gt;哪些下游能异步&lt;/strong&gt;。判断标准是这个下游失败后，业务上能不能接受&amp;quot;稍后补上&amp;quot;。更新积分可以，扣减额度不行——额度是交易准入条件，必须同步。&lt;/p&gt;&#10;&lt;h2 id="几个必须守住的细节"&gt;几个必须守住的细节&#10;&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;顺序只在分区内保证&lt;/strong&gt;。同一账户的消息必须用账户号做 key，否则先扣款后入账的顺序可能颠倒。跨账户不需要全局顺序，别为了顺序把分区数设成 1。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;消费端必须幂等&lt;/strong&gt;。Kafka 是至少一次投递，重复是常态。用业务唯一键加唯一索引兜住，不要指望不重复。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;先处理成功再提交 offset&lt;/strong&gt;。自动提交会在处理失败时丢消息。手动提交虽然会带来重复，但重复有幂等兜着，丢了就找不回来了。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;积压要能定位到分区&lt;/strong&gt;。整体 lag 正常但单分区堆积，通常是某个 key 数据倾斜或者某条消息反复失败阻塞了分区。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;div class="td-code td-code--untitled" id="td-code-9fc028c5-fence-1" data-td-code data-td-code-auto-id&#10; data-td-language="bash" data-td-line-count="7"&gt;&#10; &lt;div class="td-code__viewport" id="td-code-9fc028c5-fence-1-viewport" data-td-code-viewport&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 按分区查看消费延迟，定位倾斜与阻塞&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kafka-consumer-groups.sh --bootstrap-server &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$BROKERS&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --group txn-processor --describe&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 排查反复失败的毒消息：从指定 offset 读一条看内容&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kafka-console-consumer.sh --bootstrap-server &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$BROKERS&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --topic txn-events --partition &lt;span class="m"&gt;3&lt;/span&gt; --offset &lt;span class="m"&gt;88213&lt;/span&gt; --max-messages &lt;span class="m"&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;毒消息一定要有出路。处理失败超过阈值就转到死信 topic，让分区继续往下走，否则一条脏数据能把整个分区卡死几个小时。&lt;/p&gt;</description></item></channel></rss>