从 MQ 到 Celery:把异步任务、状态表、重试补偿和 Outbox 一次讲清楚

技术 适合场景 特点
Redis Queue 简单任务队列 轻量、接入快,但可靠性一般
RabbitMQ 传统业务消息队列 路由能力强,适合订单、通知、任务分发
Kafka 高吞吐事件流 适合日志、埋点、数据同步、消息回放
Celery Python 后台任务框架 适合 Django / FastAPI 异步任务

做后端一段时间后会发现,很多业务其实根本不适合同步做完。

比如资讯抓取、内容清洗、工单流转、通知分发、离线索引构建,这些任务要么耗时长,要么依赖第三方服务,要么链路太长。如果硬塞进一个接口里同步执行,结果往往就是:

  • 请求超时
  • 用户体验差
  • 系统耦合严重
  • 某个下游挂了,整条链路一起受影响
  • 出问题后还很难恢复

所以,异步化几乎是后端系统走向工程化的必经之路。

这篇文章我想把这条链路完整梳理一遍:

从为什么要异步,到 MQ、Kafka、Celery,再到任务状态表、幂等、重试、死信、Outbox 和最终一致性,最后再说说这些东西怎么落到真实项目里。

1. 为什么很多后端任务必须异步化?

先不讲技术,先看业务。

1.1 有些任务本来就不该卡在请求里

比如一个海外资讯聚合系统,用户点一下"开始抓取",后端实际要做的事情可能有:

  • 抓网页
  • 清洗正文
  • 去重
  • 翻译
  • 入库
  • 建索引
  • 更新缓存

这些动作很明显不是几十毫秒能完成的。如果把这整条链路都塞进一个 HTTP 请求里,请求大概率会很慢,甚至超时。

再比如智能工单系统,监控发现机器异常后,后端可能要:

  • 创建工单
  • 写审计日志
  • 通知负责人
  • 调用 Agent 分析原因
  • 触发工具执行诊断命令
  • 回填执行结果

这也不适合在一个接口里同步串行执行。

1.2 真正需要的不是"更快",而是"解耦"

异步化的核心价值不只是提升接口响应速度,更重要的是把"提交任务"和"执行任务"拆开。

同步链路是这样:

复制代码
用户请求
  ↓
接口服务直接执行所有逻辑
  ↓
执行很久 / 中途失败
  ↓
返回结果

异步链路通常会变成这样:

复制代码
用户请求
  ↓
接口服务创建任务
  ↓
消息进入队列
  ↓
Worker 后台消费执行
  ↓
任务状态更新

前者是"请求绑死执行",后者是"请求只负责提交,执行交给后台"。

这两种思路的系统稳定性,差别非常大。

2. MQ 到底解决了什么问题?

很多人刚学消息队列时,容易把重点放在"怎么发消息"。但 MQ 真正解决的是几个更底层的问题。

2.1 解耦

没有 MQ 的时候,上游服务经常要直接调用下游:

复制代码
A 服务 -> B 服务 -> C 服务 -> D 服务

任何一个环节出问题,前面都会被拖住。

有了 MQ 之后,会变成:

复制代码
A 服务 -> MQ
          ↓
       B 服务消费
       C 服务消费
       D 服务消费

A 只管发消息,不关心谁来处理、什么时候处理。这就是解耦。

2.2 异步

用户请求不需要等待所有耗时动作做完,只要先把任务投递出去,立即返回即可。

这对接口 RT 改善非常明显。

2.3 削峰

流量高峰来的时候,数据库和下游服务扛不住,最怕请求直接打穿。

MQ 相当于加了一个缓冲层:

复制代码
高峰请求 -> 先进队列 -> Worker 按能力慢慢消费

这就是削峰填谷。

2.4 重试和补偿

真实系统里,失败很常见:

  • 网络超时
  • 第三方服务抖动
  • 数据库短暂不可用
  • 某个 Worker 崩了

MQ 不会让失败消失,但能让你有机会把失败的任务重新拉起来做重试和补偿。

2.5 最终一致性

很多链路不一定要求"立刻全部成功",但要求"最终要成功"。

比如工单已经创建好了,但通知消息暂时没发出去,这种情况一般不需要回滚工单,而是通过重试补发通知,最后达到一致。

这就是最终一致性思路。

3. Producer、Broker、Consumer 是怎么协作的?

消息队列这套东西,最核心的角色就三个:

复制代码
Producer -> Broker -> Consumer

3.1 Producer:生产消息的人

Producer 不是凭空发消息,它通常来自某个业务动作。

比如:

  • 用户点击"开始抓取资讯"
  • 定时任务触发"刷新数据字典"
  • 监控系统发现服务器异常
  • BI 系统创建了一次新的 query run

这时候,Producer 会把业务动作封装成一条结构化消息。

比如一条资讯抓取任务,消息体可以长这样:

复制代码
{
  "event_id": "evt_001",
  "event_type": "news.crawl.requested",
  "task_id": "task_10001",
  "source": "bbc",
  "url": "https://example.com/news/xxx",
  "trace_id": "trace_abc123",
  "created_at": "2026-05-06T10:00:00+08:00"
}

这里几个字段很重要:

  • event_id:事件唯一 ID
  • event_type:事件类型
  • task_id:任务 ID
  • trace_id:链路追踪 ID
  • created_at:时间戳

消息一定要结构化,否则后面排查和幂等会非常难做。

3.2 Broker:接收、存储、分发消息的人

Broker 就是消息中间件服务端。

常见的 Broker 有:

  • Redis
  • RabbitMQ
  • Kafka

它负责三件事:

  • 接收 Producer 发来的消息
  • 存储消息
  • 分发给 Consumer

3.3 Consumer:真正执行任务的人

Consumer 才是异步任务真正的执行者。

比如:

  • Celery Worker 消费资讯抓取任务
  • Worker 消费工单工具执行任务
  • BI 系统异步消费索引构建任务

真正的难点,不在 Producer 能不能发出去,而在 Consumer 能不能正确处理:

  • 什么时候 ack
  • 失败后怎么重试
  • 重复消费怎么办
  • 卡死的任务怎么恢复

这也是后面最值得展开的部分。

4. Kafka 和普通 MQ 有什么区别?

这一块非常容易混。

很多人会说 Kafka 也是消息队列,RabbitMQ 也是消息队列,Redis 也能做队列,所以它们都差不多。

其实不太一样。

4.1 Queue 和 Topic 的区别

先把两个基础模型分清。

Queue:点对点模型

一条消息通常只会被一个消费者处理。

适合场景:

  • 资讯抓取任务
  • 清洗任务
  • 发邮件任务
  • 工具执行任务

特点是"这件事只要一个人做就行"。

Topic:发布订阅模型

一条消息发出去后,可以被多个消费者组分别消费。

适合场景:

  • 工单创建事件
  • 用户注册事件
  • 数据同步事件
  • 审计日志事件

特点是"这件事发生了,谁关心谁订阅"。

4.2 Kafka 更像事件流平台

Kafka 最核心的几个概念是:

  • Topic
  • Partition
  • Offset
  • Consumer Group
Topic

消息主题,类似分类。

Partition

一个 Topic 可以拆成多个 Partition,用来提升吞吐和并发消费能力。

Offset

消息在 Partition 里的位置,也是 Consumer 的消费进度。

Kafka 和很多传统 MQ 最大的不同是:

Kafka 消费消息后,不会立刻把消息删掉,而是保留日志,Consumer 通过 offset 记录自己读到哪里。

这意味着 Kafka 天然支持:

  • 断点续传
  • 消息回放
  • 历史重放

比如资讯清洗逻辑升级后,可以从旧 offset 重新消费历史原始数据,重新跑一遍。

4.3 Kafka 为什么吞吐高?

Kafka 高吞吐一般来自几个点:

  • 顺序写磁盘
  • 批量发送
  • Page Cache
  • Partition 并行消费

所以 Kafka 更适合:

  • 高吞吐事件流
  • 日志采集
  • 埋点数据
  • 数据同步
  • 需要回放的业务链路

4.4 Celery 和 Kafka 不是一类东西

这个很重要。

Kafka 更偏消息流平台。

Celery 更偏 Python 任务执行框架。

一句话理解:

  • Kafka:更适合"事件流"
  • Celery:更适合"后台任务执行"

所以在 Django / FastAPI 业务里,中小规模异步任务很多时候用 Celery + Redis 已经够了,不一定非要一上来就 Kafka。

5. Celery 在 Django / FastAPI 里怎么落地?

Celery 是 Python 生态里最常见的异步任务框架之一。

它最适合做的事其实很简单:

把一个 Python 函数丢到后台 Worker 去执行。

5.1 Celery 解决的是什么问题?

在 Python Web 项目里,很多事情你不想同步做:

  • 发通知
  • 抓数据
  • 导出报表
  • 生成 embedding
  • 清理缓存
  • 调用第三方接口

Celery 的价值就是把这些任务从请求线程里拿出去。

典型结构大概是这样:

复制代码
Django / FastAPI
  ↓
Celery Task
  ↓
Redis / RabbitMQ Broker
  ↓
Celery Worker
  ↓
执行任务

5.2 Web 服务应该怎么提交任务?

很多人刚用 Celery 时会直接在接口里这样写:

复制代码
crawl_news_task.delay(url)

这当然能跑,但工程上还不够。

更稳的方式通常是:

  1. 先创建业务任务记录
  2. 生成 task_id
  3. 把任务状态写进数据库
  4. 再投递 Celery 消息
  5. Celery 消息里只传 task_id

也就是说,消息只做"触发器",真正的任务参数和状态都在数据库里。

这样 Consumer 收到消息后,不是直接相信消息内容,而是先根据 task_id 去查数据库状态。

这会让幂等、重试、恢复都简单很多。

5.3 Redis Broker 和业务状态不是一回事

很多人刚学 Celery 时还容易混一点:

  • Redis Broker:负责存放待执行任务消息
  • 业务状态表:负责记录任务现在执行到哪里了

Redis 只是中转层,不应该成为业务任务状态的最终事实来源。

真正能告诉你:

  • 任务是否成功
  • 失败几次
  • 是否已经重试
  • 是否需要人工介入

这些信息的,还是你自己的任务状态表。

5.4 Worker 怎么拆更合理?

在项目里,最好不要把所有任务都塞进一个队列。

比如资讯中台里,可以拆成:

复制代码
crawl_queue
clean_queue
index_queue
notify_queue

这样做的好处很明显:

  • 抓取慢,不会拖垮清洗
  • 入库慢,不会影响通知
  • 每类任务可以单独扩容
  • 出问题更容易定位

5.5 定时任务怎么做?

Celery Beat 可以负责定时投递任务。

比如:

  • 每小时生成一批资讯抓取任务
  • 每分钟扫描超时任务
  • 每分钟扫描 retrying 到期任务
  • 每天夜里做归档和清理

这就把"业务定时任务"和"系统补偿任务"都串起来了。

6. 为什么任务状态表才是异步系统的核心?

这一节我觉得是整套异步链路里最重要的部分。

因为很多人以为有了 MQ、Celery,异步系统就完整了。其实不是。

MQ 只能告诉你:

有任务要做。

但它回答不了这些更关键的问题:

  • 任务当前执行到哪一步了?
  • 有没有执行过?
  • 失败几次了?
  • 下次什么时候重试?
  • 是不是已经卡死了?
  • 能不能人工重新执行?

这些都需要任务状态表。

6.1 一个典型的任务状态表长什么样?

核心字段一般包括:

  • task_id
  • task_type
  • biz_id
  • idempotency_key
  • status
  • retry_count
  • max_retry_count
  • next_retry_at
  • worker_id
  • lease_until
  • payload
  • result
  • error_msg

6.2 状态怎么设计?

我比较推荐这几个基础状态:

复制代码
pending
running
success
failed
retrying
dead_letter

含义比较直观:

  • pending:等待执行
  • running:正在执行
  • success:执行成功
  • failed:这次执行失败
  • retrying:等待下次重试
  • dead_letter:超过次数或不可恢复,转人工处理

6.3 状态机怎么走?

可以抽象成这样:

复制代码
pending -> running -> success
running -> failed -> retrying -> pending
failed -> dead_letter

关键点是:

状态不能乱跳,必须按规则流转。

比如已经 success 的任务,就不应该被再次执行。

已经 dead_letter 的任务,也不应该继续自动重试。

6.4 为什么说任务状态表才是事实来源?

因为 MQ 消息可能:

  • 重复
  • 延迟
  • 乱序
  • 被重投

所以 Consumer 不应该看到消息就直接执行,而应该:

复制代码
收到 task_id
  ↓
查任务状态表
  ↓
确认状态允许执行
  ↓
再执行

这句话很值得记:

MQ 负责触发执行,任务状态表负责描述任务生命周期。

7. Ack、幂等、重试、死信:异步系统最容易翻车的地方

这一部分基本就是异步系统稳定性的核心。

7.1 Ack 应该什么时候做?

最安全的原则通常是:

业务处理成功后,再 ack。

如果一拿到消息就先 ack,再执行业务,一旦 Worker 中途挂了,这条消息就彻底丢了。

当然,业务成功后再 ack 也会有另一个问题:

  • 业务已经做完了
  • 但 ack 之前进程挂了
  • Broker 以为消息没处理成功
  • 又重新投了一次

这就是为什么重复消费几乎不可避免。

7.2 为什么重复消费一定会发生?

常见原因很多:

  • ack 没成功
  • offset 提交前宕机
  • Producer 重试导致重复发送
  • Broker 重投
  • Consumer rebalance

所以异步系统里一定要接受一件事:

重复消费不是异常,而是常态。

真正要做的是幂等。

7.3 幂等怎么做?

幂等就是同一个操作做一次和做多次,结果一样。

常见做法有几种。

业务唯一键

资讯系统里可以用:

  • source_url
  • news_id

工单系统里可以用:

  • ticket_id + step_id + tool_name + action
任务状态表

执行前先看这个任务是不是已经 success。如果已经成功,直接跳过。

执行记录表

对于工具执行这种有副作用的任务,最好单独做执行记录表,防止危险动作被重复执行。

数据库唯一索引

这是最后一层兜底。

单靠代码判断幂等不够,并发下还是可能翻车。最好配上唯一索引。

7.4 哪些失败该重试?

可以重试的一般是临时错误:

  • 网络超时
  • 连接失败
  • 第三方 5xx
  • 429 限流
  • 数据库短暂抖动

不应该重试的一般是确定性错误:

  • 参数错误
  • 权限不足
  • URL 非法
  • 业务状态不允许
  • 工具不存在

7.5 为什么要有死信队列?

坏消息不能无限重试。

如果一条消息本身就有问题,还一直重试,只会:

  • 占用 Worker
  • 拖慢主队列
  • 对下游形成重试风暴

所以超过最大重试次数后,应该进入 dead_letter 或死信队列,转人工处理。

7.6 消息积压怎么排查?

消息积压本质上就是:

复制代码
生产速度 > 消费速度

排查思路通常是:

  • Consumer 是否还活着
  • 失败率是不是变高了
  • 单任务耗时是不是变长了
  • 数据库 / 第三方接口是不是变慢了
  • Worker 数是不是不够
  • Kafka 的 Partition 是否成了并发瓶颈

不要一上来就说"加机器",先定位瓶颈才更像工程师。

8. Worker 抢占、超时恢复和重试补偿怎么设计?

这部分是把状态表真正用起来的关键。

8.1 为什么要抢占任务?

假设有多个 Worker 同时看到一条 pending 任务,如果没有控制,很可能会被执行多次。

所以需要"抢占"。

正确做法不是:

复制代码
先 select 看状态是不是 pending
再 update 改成 running

因为并发下多个 Worker 可能同时查到 pending

更稳的方式是:

直接用带条件的 update 抢占。

比如只有 status in ('pending', 'retrying') 时,才能改成 running

影响行数为 1,说明抢占成功;为 0,说明已经被别人抢了。

8.2 running 卡死怎么办?

最常见的事故之一就是任务一直卡在 running

原因可能是:

  • Worker 宕机
  • 代码死循环
  • 第三方接口一直不返回
  • 进程被杀
  • 机器重启

所以 running 状态不能无限挂着。

8.3 lease_until 很关键

Worker 抢占任务时,可以给它一个租约:

  • worker_id
  • lease_until

意思是:

这个任务在 lease_until 之前归当前 Worker 处理。

如果 Worker 还活着,可以续租。

如果 Worker 挂了,就没人续租。

后台扫描器发现 lease_until 过期的 running 任务,就可以判定它可能已经卡死了,然后把任务恢复成 retrying,或者直接进 dead_letter

8.4 重试补偿不是 while 死循环

错误做法是:

复制代码
失败了 -> while 一直重试

这样会把 Worker 占死。

正确做法应该是:

  • 失败后更新任务状态
  • 设置 next_retry_at
  • 释放 Worker
  • 等后台扫描器到时间再重新投递

这才是工程上的重试补偿。

9. 数据库和 MQ 怎么保证一致?为什么需要 Outbox?

这一块是异步系统再往前走一步,必须面对的问题。

9.1 最经典的问题:库写成功了,但消息没发出去

比如创建工单时:

复制代码
ticket 表写成功
  ↓
准备发 ticket.created
  ↓
进程突然挂了

结果就是:

  • 工单在数据库里已经存在
  • 但通知服务没收到
  • 审计服务没收到
  • Agent 也没收到

整个异步链路断了。

9.2 反过来也有问题:消息发出去了,但数据库回滚了

如果你先发消息再写数据库,也可能出现:

  • 消息已经被下游消费
  • 但数据库事务失败回滚
  • 下游拿到的是假事件

9.3 为什么不能简单"先写库再发 MQ"?

因为数据库事务提交和消息发送,本来就是两个独立动作,不是天然原子操作。

中间这段空隙就是最危险的:

复制代码
数据库提交成功
  ↓
消息还没发
  ↓
进程挂了

9.4 Outbox 的思路是什么?

核心思路其实很朴素:

不要业务一成功就立刻发 MQ,而是先把"待发送消息"也落到数据库里。

也就是说,在同一个本地事务里同时做两件事:

  • 写业务表
  • 写 outbox_event 表

事务提交后,再由后台 dispatcher 去扫描 outbox_event 并投递 MQ。

流程大概是这样:

复制代码
业务操作
  ↓
同一事务里:
写业务表 + 写 outbox_event
  ↓
事务提交
  ↓
后台 dispatcher 扫描 outbox_event
  ↓
发送 MQ
  ↓
成功后标记 sent

9.5 Outbox 解决了什么?

它解决的是:

消息不会无记录地丢失。

即使服务在事务提交后、消息发送前挂了,也没关系,因为 outbox_event 还在数据库里,后面扫描器还能补发。

9.6 dispatcher 也要有状态机

Outbox 表我一般会设计这些状态:

  • pending
  • sending
  • sent
  • retrying
  • dead_letter

dispatcher 工作逻辑一般是:

  1. 扫描 pending / retrying
  2. 抢占改成 sending
  3. 真正发送 MQ
  4. 成功改 sent
  5. 临时失败改 retrying
  6. 永久失败或超过次数改 dead_letter

9.7 Outbox 不解决重复,要靠幂等

这一点一定要说清楚:

Outbox 解决的是"不丢",不是"绝不重复"。

比如 MQ 明明发成功了,但 dispatcher 还没来得及把 outbox 状态改成 sent 就挂了,后面还是可能再发一遍。

所以消费端依然要做幂等。

9.8 什么叫最终一致性?

最终一致性不是"永远一致",而是:

允许短时间不一致,但系统会通过补偿机制,最终达到一致。

比如工单创建好了,通知稍后几秒补发成功,这在很多业务里都是完全可以接受的。

10. 这些方案在我的项目里怎么落地?

技术写到这里,如果不落项目,很容易变成"知识点罗列"。

下面说说我觉得比较自然的几个落地场景。

10.1 海外资讯聚合中台

这个项目里,抓取、清洗、入库本质上都是异步任务。

业务问题
  • 抓取链路耗时长
  • 资讯源质量不稳定
  • 网络抖动比较多
  • 不能同步卡在请求里
技术方案
  • Django + Celery + Redis
  • 抓取、清洗、入库拆成不同队列
  • 任务状态表记录 pending / running / success / retrying / dead_letter
  • source_url / news_id 做幂等
  • 入库层加唯一索引防止重复写入
  • Beat 定时扫描超时任务和待重试任务
更进一步

如果后期量更大、链路更复杂,其实可以继续升级成:

  • 抓取事件
  • 清洗事件
  • 索引事件

用 Kafka 作为事件流承载,做可回放的数据链路。

10.2 智能工单与自动化运维 Agent 系统

这个场景特别适合讲状态表、幂等和 Outbox。

业务问题
  • 告警触发后会有多步异步动作
  • 工具调用有副作用,不能重复执行
  • 通知、审计、Agent 分析不应该强耦合同步调用
技术方案
  • 告警事件进入 MQ
  • 用 run / step / attempt 管理任务链路
  • ticket_id + step_id + tool_name + action 生成幂等键
  • Worker 条件 update 抢占任务
  • lease_until 做超时恢复
  • 临时失败进 retrying
  • 超过次数进 dead_letter
  • ticket.created 等事件通过 Outbox 保证不丢

这个项目里最关键的一点其实是:

工具执行类任务不能只想着"跑通",必须先解决"不能乱跑、不能重复跑、跑挂了能恢复"。

10.3 智能 BI 项目

BI 场景里,主查询链路很多时候是同步 + SSE 返回进度,但离线步骤非常适合异步化。

比如:

  • schema 刷新
  • 字段 embedding 构建
  • 历史 query 向量化
  • 索引重建

这些任务可以:

  • 用 Celery 异步跑
  • 用 run / step / attempt 记录状态
  • datasource_id + schema_version + task_type 做幂等键

这样后面做观测、错误恢复、重建索引都会轻松很多。

11. 这套方案最容易踩的坑

最后再集中说一下几个最常见的坑。

11.1 以为用了 MQ 就天然可靠

不是。

MQ 只是传消息,可靠性还要靠:

  • 状态表
  • 幂等
  • 重试
  • 死信
  • 超时恢复

11.2 只用 Celery,不做状态表

这样系统表面上能跑,但出了问题很难排查:

  • 任务到底成功没
  • 重试几次了
  • 卡在哪一步
  • 是否需要人工介入

全都不清楚。

11.3 幂等只靠代码判断,不加唯一索引

并发场景下很容易翻车。

最好数据库层也有唯一索引做最终兜底。

11.4 失败后无限重试

这会导致重试风暴。

正确做法是:

  • 分类错误
  • 有限次数
  • 指数退避
  • 超过次数转死信

11.5 没有超时恢复

没有 lease_until 和扫描器,任务就会永远卡在 running

11.6 没有 Outbox

数据库和消息不一致是非常典型的事故来源。

业务只要开始依赖异步事件,Outbox 基本就绕不开了。

12. 还能怎么继续优化?

异步任务系统真正成熟之后,优化点其实很多。

比如:

  • 队列按任务类型拆分
  • Worker 水平扩容
  • Kafka 增加 Partition 提升并发
  • 批量发送、批量消费
  • 指数退避避免重试风暴
  • 增加监控指标:
    • 队列长度
    • lag
    • retry_count
    • dead_letter 数量
    • 成功率
    • P95 执行耗时
  • 状态表归档
  • Outbox 用 CDC 替代纯轮询
  • 链路追踪接 OpenTelemetry

这些优化的方向其实可以浓缩成一句话:

异步系统不只是"能跑",还要做到低延迟、可观测、可恢复、可扩展。

13. 总结

回头看这整条链路,其实会发现:

  • MQ 解决的是异步解耦和削峰
  • Kafka 更适合高吞吐事件流和可回放链路
  • Celery 更适合 Python 业务里的后台任务执行
  • 任务状态表决定了系统能不能恢复、能不能排障
  • 幂等、重试、死信决定了系统能不能稳定运行
  • Outbox 解决的是生产端消息不丢
  • 最终一致性是大多数业务系统更现实的选择

真正难的,从来不是把消息发出去。

真正难的是:

在重复、失败、超时、宕机这些现实问题下,任务依然能被正确地执行完。

这也是为什么我现在越来越觉得,异步任务系统的重点从来不在"会不会用队列",而在"能不能把这条链路做成一个可恢复、可追踪、可补偿的系统"。

相关推荐
苍煜1 小时前
Kafka消息零丢失核心全解:生产者acks机制+消费者offset机制
分布式·kafka
敖正炀7 小时前
Kafka 安全机制:SASL 认证、SSL 加密与 ACL 授权
kafka
敖正炀9 小时前
Kafka 特性全景与选型指南
kafka
何中应12 小时前
RabbitMQ集群搭建
分布式·rabbitmq
乐之者v14 小时前
Kafka 跨服数据同步
分布式·kafka
富士康质检员张全蛋14 小时前
Kafka 消息查找流程和消息读取流程
分布式·kafka
未来龙皇小蓝16 小时前
SpringBoot API日志系统设计-02:线程池异步化与RabbitMQ解耦
数据库·spring boot·后端·性能优化·rabbitmq·java-rabbitmq
深蓝轨迹16 小时前
Kafka入门教程--帮你理清所有概念和细节
分布式·zookeeper·kafka
小尘要自信16 小时前
Kafka 从原理到实践:分区副本机制、生产消费可靠性、以及如何避开那些年踩过的坑
分布式·kafka