<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>消息队列 on Tequila's 学习笔记</title><link>https://latnx.github.io/docs/notes/d-205f3859abe469be/d-ea456e7df09fd4f7/</link><description>Recent content in 消息队列 on Tequila's 学习笔记</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><copyright>© 2026 Tequila</copyright><atom:link href="https://latnx.github.io/docs/notes/d-205f3859abe469be/d-ea456e7df09fd4f7/index.xml" rel="self" type="application/rss+xml"/><item><title>架构</title><link>https://latnx.github.io/docs/notes/d-205f3859abe469be/d-ea456e7df09fd4f7/n-f1d1d318ae7cef96/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://latnx.github.io/docs/notes/d-205f3859abe469be/d-ea456e7df09fd4f7/n-f1d1d318ae7cef96/</guid><description> Broker：可以理解为机器或者节点吧，也可以理解为就是运行Kafka程序的服务器。​
Topic：主题是Kafka中的一个核心概念，它是对消息进行分类的一种方式。生产者将消息发送到特定的主题中，而消费者则通过订阅主题来接收相关的消息。但是要注意的是，主题是一个逻辑概念，实际上，一个主题可以被分为多个分区（Partition），以实现消息的并行处理和负载均衡，数据是存储在Partition这个级别的。​
Partition：分区是Kafka中的一个重要概念，它是主题的物理存储单位。每个分区都是一个有序的、不可变的消息序列，可以被独立地读写。分区在物理上对应一个文件夹及文件夹下面的文件，分区的命名规则为主题名称后接“—”连接符，之后再接分区编号，比如TopicA-1就表示主题A得1号分区，每个分区又可以有一至多个副本（Replica），以提高可用性。​
​同一个Partition里的数据，消息是有序的。即使同一个主题里的消息，如果分布在多个Partition，不同Partition 的消息之间也是无序的
Consumer Group再平衡，同一个 partition 同时只被一个 consumer 消费 范围分配，轮询分配，粘性分配，合作粘性分配，其中合作粘性分配和粘性分配一样都是尽可能减少变动，不同点是合作粘性分配下，未受变动的消费者可以继续消费主题。
Range Assignor：基于范围的分配策略，将分区按照范围分配给消费者。​ RoundRobin Assignor：基于轮询的分配策略，分区均匀地分配给消费者。​ Sticky Assignor：优先保持当前的分配状态，并尽量减少在再平衡过程中的分区移动。​ CooperativeStickyAssignor：和Sticky Assignor的策略是基本一样的，区别在于该协议将原来的一次大的全部分区重平衡，改成多次小规模分区重平衡。简单理解就是渐进式重平衡。 手动提交和自动提交
for { msg, err := reader.FetchMessage(ctx) if err != nil { panic(err) } // 业务处理 fmt.Println(string(msg.Value)) // 成功后提交 err = reader.CommitMessages(ctx, msg) if err != nil { panic(err) } } for { m, _ := r.ReadMessage(context.Background()) fmt.Println(string(m.Value)) } Replica：Replica是指Kafka集群中的一个副本，它可以是Leader副本或者Follower副本的一种。每个分区都有多个副本，其中一个是Leader副本，其余的是Follower副本。每个副本都保存了分区的完整数据，以保证数据的可靠性和高可用性。​ Leader：Leader是指Kafka集群中的一个分区副本，它负责处理该分区的所有读写请求。Leader副本是唯一可以自主向分区写入数据的副本，它将写入的数据都会同步到所有的Follower副本中，以保证数据的可靠性和一致性。​ Follower：Follower是指Kafka集群中的一个分区副本，Follower副本不能直接向分区写入数据，它只能从Leader副本中复制数据，并将数据同步到本地的副本中，以保证数据的可靠性和一致性。在Leader副本挂掉的时候，Follower副本有机会被选举为新的leader副本从而保证分区的可用性。 数据是直接往Leader写入，写入之后Leader和Follower之间会进行同步，是否要等待同步完成取决于选择哪种写入策略。</description></item><item><title>实践</title><link>https://latnx.github.io/docs/notes/d-205f3859abe469be/d-ea456e7df09fd4f7/n-b49f2410fd9c713d/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://latnx.github.io/docs/notes/d-205f3859abe469be/d-ea456e7df09fd4f7/n-b49f2410fd9c713d/</guid><description> 不丢失 # 生产 存储：持久化存储；Kafka实际上还通过副本机制，让持久化数据更可靠，即将每个主题划分为多个分区，并为每个分区创建多个副本。这确保了即使部分Broker发生故障，消息仍然可以从其他副本中恢复，并被消费者消费。 消费：另一种方式是手动提交，也就是由消费者业务代码自己控制提交时机，主动调用函数来提交。​简单来说，为了保证消费最终被处理过，只有在消费端处理成功之后，才提交偏移到Broker，否则不进行偏移提交，这样下次拉取还能拉取到这条消息。 不重复 # 生产：幂等性生产，对于每个 Topic Partition，Kafka生产者为每条消息分配一个递增的序列号。Kafka 该序列号是递增的，表示消息的顺序，Broker 会跟踪每个 Topic Partition 的最后一个已提交的序列号。
消费：
Redis 唯一标识符：为每个消息分配一个全局唯一的标识符（如UUID）。​ Redis Set：将已消费的消息ID存储在Redis的Set数据结构中。每次消费消息前，检查该消息ID是否已存在于Set中。​ 原子操作：使用Redis的原子操作（如SISMEMBER和SADD）来检查和添加消息ID，确保操作的原子性。 如果存储用MySQL，如何进行幂等处理​ MySQL常见实现幂等性的思路有如下三种：​
唯一约束：在MySQL表中为消息ID创建一个唯一约束。​ INSERT IGNORE：使用 INSERT IGNORE 或 ON DUPLICATE KEY UPDATE 语句来尝试插入消息记录。如果消息ID已存在，则忽略或更新该记录。​ 事务：在需要的情况下，使用事务来确保多个操作的原子性。 有序 # 一种最简单的做法，就是根据业务确定分区，即每类业务自己一个分区，这样就可以实现业务消息有序。 业务内分区​：我们前面说了，可以根据业务分区，而如果单个业务压力过大，我们就要考虑业务内再次切分。 子业务 客户</description></item><item><title>应用场景</title><link>https://latnx.github.io/docs/notes/d-205f3859abe469be/d-ea456e7df09fd4f7/n-990f91d0e5da416c/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://latnx.github.io/docs/notes/d-205f3859abe469be/d-ea456e7df09fd4f7/n-990f91d0e5da416c/</guid><description> 1. 解耦： # 一个模块A接受前端请求，收到请求之后需要调用另一个模块B，以完成后续事项。一开始需要B做完之后，才回包给前端，这是第一种流程，这种流程很明显会很依赖模块B的运作情况。​ 但是如果业务允许，我们还可以有第二种流程：​ 模块A不用等模块B把事情做完，只用将信息传递到一个中转站，B从中转站感知到这件事，再自己去做就可以了，这个中转站，就是消息队列，可以起到解耦的作用。
2. 异步： # 用户很难通过同步接口长时间等待结果，那就应该做成异步，先扔进消息队列，后续再进行消费，和解耦一样可以收获更高的性能，以及获得更好的可靠性。
3. 消息分发： # 信息更新场景，比如某个用户信息更新了，而B、C、D三个模块都需要缓存这个信息，那么用户信息更新之后就可以发一条信息到消息队列，B、C、D只要订阅了相关主题，就都可以收到这条信息。
4. 削峰： # 架构和异步相同，将请求放入队列中，慢慢消费 模块A不用一次性把消息打给B，而是只用将信息传递到一个中转站，B按自身的消费能力从中转站拉取消息，再自己去做就可以了，这个中转站，就是消息队列，可以起到削峰的作用。
选型思路
我们团队主要对性能和可靠性有要求​ 性能要有5000 QPS，这里Kafka和RocketMQ都远远够用​ 可靠性要有多副本机制，这点Kafka和RocketMQ也都OK​ 其他功能则不是我们选择的核心要点​ 所以最后考虑到Kafka是我们团队已经成熟使用的技术栈，最终还是选择了Kafka。</description></item></channel></rss>