【中台·技术篇】消息队列中台:Kafka/RocketMQ 统一管理与多租户隔离

前言

消息队列是微服务间异步通信的核心。中台化后,多个业务线共用一套消息队列,如何隔离、如何管理、如何保证可靠性是关键。本篇讲解 Kafka 和 RocketMQ 的中台化建设。


一、消息队列在中台中的角色

复制代码
中台架构中的消息队列:

  用户中心 → 发布事件 → ┌──────────┐ → 订阅 → 消息中心
  订单中心 → 发布事件 → │  MQ 中台 │ → 订阅 → 库存中心
  支付中心 → 发布事件 → │          │ → 订阅 → 推荐引擎
  商品中心 → 发布事件 → └──────────┘ → 订阅 → 数据中台

  职责:
  1. 事件驱动 → 中心间解耦
  2. 削峰填谷 → 应对流量洪峰
  3. 最终一致 → 跨中心数据同步
  4. 日志采集 → 统一日志管道

二、Kafka vs RocketMQ 选型

维度 Kafka RocketMQ
吞吐量 极高(百万级) 高(十万级)
延迟 毫秒级 毫秒级
顺序消息 分区内有序 分区内有序
事务消息
延迟消息 不支持 支持(18 级)
死信队列 不支持 支持
消息回溯 支持 支持
多租户 Topic 隔离 Group 隔离
运维 较复杂 较简单
生态 流处理 消息业务

选型建议

yaml 复制代码
# 选 Kafka 如果你:
  - 高吞吐日志/数据管道
  - 需要流处理(Kafka Streams/Flink)
  - 已有 Kafka 基础设施

# 选 RocketMQ 如果你:
  - 业务消息(订单/支付)
  - 需要事务消息
  - 需要延迟消息
  - 需要死信队列

三、Kafka 中台化

集群架构

复制代码
┌─────────────────────────────────────────────────┐
│              Kafka Cluster (3 Broker)            │
│                                                  │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐      │
│  │ Broker 0 │  │ Broker 1 │  │ Broker 2 │      │
│  │          │  │          │  │          │      │
│  │ Topic A  │  │ Topic A  │  │ Topic B  │      │
│  │ (Leader) │  │(Follower)│  │ (Leader) │      │
│  │ Topic C  │  │ Topic B  │  │ Topic C  │      │
│  │          │  │          │  │          │      │
│  └──────────┘  └──────────┘  └──────────┘      │
│                                                  │
│  Zookeeper (3 节点)                              │
│  Schema Registry                                 │
│  Kafka Connect                                   │
│  Kafka Streams                                   │
└─────────────────────────────────────────────────┘

多租户隔离

yaml 复制代码
# 方案 1:Topic 前缀隔离(推荐)
Topic 命名规范:
  格式: <team>.<domain>.<event>

  示例:
    payments.team.payment.completed
    payments.team.payment.failed
    orders.team.order.created
    inventory.team.stock.updated

# 方案 2:独立集群隔离(高安全级别)
  团队 A → Kafka Cluster A
  团队 B → Kafka Cluster B

Topic 管理

bash 复制代码
# 创建 Topic
kafka-topics.sh --create \
  --bootstrap-server kafka:9092 \
  --topic payments.payment.completed \
  --partitions 6 \
  --replication-factor 3 \
  --config retention.ms=604800000 \
  --config max.message.bytes=1048576

# 查看所有 Topic
kafka-topics.sh --list \
  --bootstrap-server kafka:9092

# 查看 Topic 详情
kafka-topics.sh --describe \
  --bootstrap-server kafka:9092 \
  --topic payments.payment.completed

# 生产消息
kafka-console-producer.sh \
  --bootstrap-server kafka:9092 \
  --topic payments.payment.completed

# 消费消息
kafka-console-consumer.sh \
  --bootstrap-server kafka:9092 \
  --topic payments.payment.completed \
  --from-beginning \
  --group payments-consumer

配额限流

bash 复制代码
# 按 Client ID 限流
kafka-configs.sh --bootstrap-server kafka:9092 \
  --alter --add-config \
  'producer_byte_rate=10485760,consumer_byte_rate=10485760' \
  --entity-type clients \
  --entity-name payments-client

# 按 User 限流(需要 SASL)
kafka-configs.sh --bootstrap-server kafka:9092 \
  --alter --add-config \
  'producer_byte_rate=5242880,consumer_byte_rate=5242880' \
  --entity-type users \
  --entity-name orders-user

ACL 权限控制

bash 复制代码
# 授权 payments-team 只能读写 payments.* Topic
kafka-acls.sh --bootstrap-server kafka:9092 \
  --add --allow-principal User:payments-team \
  --producer --topic "payments." \
  --resource-prefix

kafka-acls.sh --bootstrap-server kafka:9092 \
  --add --allow-principal User:payments-team \
  --consumer --topic "payments." \
  --resource-prefix \
  --group "payments-"

Schema Registry

bash 复制代码
# 注册 Avro Schema
curl -X POST http://schema-registry:8081/subjects/payments.completed-value/versions \
  -H "Content-Type: application/vnd.schemaregistry.v1+json" \
  -d '{
    "schema": "{\"type\":\"record\",\"name\":\"PaymentCompleted\",\"fields\":[{\"name\":\"paymentId\",\"type\":\"string\"},{\"name\":\"amount\",\"type\":\"double\"}]}"
  }'

# 生产端使用 Schema
# 消费端自动校验 Schema

Kafka Connect 数据管道

json 复制代码
// 从 MySQL 同步到 Kafka
{
  "name": "mysql-orders-source",
  "config": {
    "connector.class": "io.debezium.connector.mysql.MySqlConnector",
    "database.hostname": "mysql",
    "database.port": "3306",
    "database.user": "debezium",
    "database.password": "dbz",
    "database.server.id": "184054",
    "database.allowPublicKeyRetrieval": "true",
    "database.server.name": "orders",
    "table.include.list": "orders.orders,orders.order_items",
    "database.history.kafka.bootstrap.servers": "kafka:9092",
    "database.history.kafka.topic": "schema-changes.orders"
  }
}

四、RocketMQ 中台化

部署

bash 复制代码
# NameServer
docker run -d --name rmqnamesrv \
  -p 9876:9876 \
  apache/rocketmq:5.2.0 \
  sh mqnamesrv

# Broker
docker run -d --name rmqbroker \
  -p 10911:10911 -p 10909:10909 \
  --link rmqnamesrv:namesrv \
  -e "NAMESRV_ADDR=namesrv:9876" \
  apache/rocketmq:5.2.0 \
  sh mqbroker -c /home/rocketmq/rocketmq-5.2.0/conf/broker.conf

Topic 管理

bash 复制代码
# 创建 Topic
mqadmin updateTopic -n namesrv:9876 \
  -b broker-a \
  -t payments.payment.completed \
  -r 3 -w 3    # 3 读 3 写队列

# 创建消费组
mqadmin updateSubGroup -n namesrv:9876 \
  -b broker-a \
  -g payments-consumer-group \
  -r 2    # 重试 2 次

事务消息

java 复制代码
// 事务消息生产者
TransactionMQProducer producer = new TransactionMQProducer("tx-group");
producer.setNamesrvAddr("namesrv:9876");

// 事务监听器
producer.setTransactionListener(new TransactionListener() {
    @Override
    public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
        // 执行本地事务
        try {
            orderService.createOrder(msg);
            return LocalTransactionState.COMMIT_MESSAGE;
        } catch (Exception e) {
            return LocalTransactionState.ROLLBACK_MESSAGE;
        }
    }

    @Override
    public LocalTransactionState checkLocalTransaction(MessageExt msg) {
        // 事务回查
        Order order = orderService.findById(msg.getKeys());
        if (order != null && order.isCreated()) {
            return LocalTransactionState.COMMIT_MESSAGE;
        }
        return LocalTransactionState.UNKNOW;
    }
});

// 发送事务消息
Message msg = new Message("orders.created", 
    "order-data".getBytes());
producer.sendMessageInTransaction(msg, null);

延迟消息

java 复制代码
// 延迟消息(RocketMQ 支持 18 个延迟级别)
Message msg = new Message("orders.timeout", 
    "order-data".getBytes());
// 延迟级别: 1=1s 2=5s 3=10s ... 18=2h
msg.setDelayTimeLevel(4);  // 30s 后投递
producer.send(msg);

消费者组管理

yaml 复制代码
# 消费者配置
consumer:
  group_name: payments-consumer-group
  namesrv_addr: namesrv:9876
  consume_mode: CONSUME_PASSIVELY  # 被动消费
  consume_from_where: CONSUME_FROM_LAST_OFFSET  # 从最后位置消费
  max_reconsume_times: 3   # 重试 3 次
  suspend_current_queue_time_millis: 1000  # 暂停时间

死信队列处理

bash 复制代码
# 死信队列 Topic 格式:%DLQ%<consumer_group>
# 查看
mqadmin consumerProgress -n namesrv:9876 -g payments-consumer-group

# 消费死信队列
mqadmin queryMsgByOffset -n namesrv:9876 \
  -b broker-a -t %DLQ%payments-consumer-group \
  -o <offset>

五、消息可靠性保证

生产端可靠性

python 复制代码
# Kafka:acks=all
from kafka import KafkaProducer
producer = KafkaProducer(
    bootstrap_servers=['kafka:9092'],
    acks='all',              # 所有副本确认
    retries=3,               # 失败重试
    max_in_flight_requests_per_connection=1,  # 顺序保证
    enable_idempotence=True, # 幂等生产
    compression_type='lz4',  # 压缩
)

# 发送并等待确认
future = producer.send('payments.completed', value=message)
result = future.get(timeout=10)  # 同步等待
java 复制代码
// RocketMQ:同步发送
DefaultMQProducer producer = new DefaultMQProducer("group");
producer.setNamesrvAddr("namesrv:9876");
producer.setRetryTimesWhenSendFailed(3);    // 重试 3 次
producer.setSendMsgTimeout(5000);           // 超时 5 秒
producer.start();

SendResult result = producer.send(msg);
if (result.getSendStatus() != SendStatus.SEND_OK) {
    // 处理发送失败
}

消费端可靠性

python 复制代码
# Kafka 消费者
from kafka import KafkaConsumer
consumer = KafkaConsumer(
    'payments.completed',
    bootstrap_servers=['kafka:9092'],
    group_id='payments-consumer',
    enable_auto_commit=False,    # 手动提交
    auto_offset_reset='earliest',
)

for message in consumer:
    try:
        process_message(message)
        consumer.commit()    # 处理成功后提交
    except Exception as e:
        # 处理失败,不提交,下次重新消费
        log.error(f"Failed: {e}")

消息幂等

python 复制代码
# 消费幂等性设计
def process_payment_message(message):
    payment_id = message['payment_id']

    # 幂等检查
    if is_processed(payment_id):
        return  # 已处理,跳过

    # 处理业务
    process_payment(message)

    # 标记已处理
    mark_as_processed(payment_id)

六、监控告警

Kafka 监控

yaml 复制代码
# Kafka Exporter
scrape_configs:
  - job_name: 'kafka'
    static_configs:
      - targets: ['kafka-exporter:9308']

# 关键指标
# kafka_consumergroup_lag → 消费延迟
# kafka_topic_partitions → 分区数
# kafka_topic_partition_under_replicated_partition → 欠同步分区

告警规则

yaml 复制代码
groups:
  - name: mq-alerts
    rules:
      - alert: KafkaConsumerLag
        expr: kafka_consumergroup_lag > 10000
        for: 10m
        labels:
          severity: warning

      - alert: KafkaUnderReplicated
        expr: kafka_topic_partition_under_replicated_partition > 0
        for: 5m
        labels:
          severity: critical

      - alert: RocketMQBrokerDown
        expr: rocketmq_broker_runtime_status == 0
        for: 1m
        labels:
          severity: critical

要点回顾

维度 Kafka RocketMQ
适用 高吞吐/流处理 业务消息
多租户 Topic 前缀 Group 隔离
事务消息
延迟消息 不支持 支持
死信队列 不支持 支持
配额限流 Client ID Consumer
  • Kafka 适合数据管道,RocketMQ 适合业务消息
  • 多租户用 Topic 前缀命名规范
  • 生产端 acks=all + 重试 + 幂等
  • 消费端手动提交 + 幂等处理
  • 监控重点:Consumer Lag 和分区同步状态

下一篇预告

下一篇 【中台·技术篇】缓存中台建设:Redis 集群统一管理与多级缓存架构 将讲解缓存中台的建设。

相关推荐
heimeiyingwang10 小时前
【中台·技术篇】API 网关建设:Kong 与 APISIX 选型与落地实践
kong·apisix·中台
程序员天天困19 小时前
Kafka 接入 AI 的三条路线:MCP 提案、会话记忆与实时上下文
大数据·后端·kafka
heimeiyingwang20 小时前
【中台·技术篇】技术中台概览:API 网关、消息队列、缓存、配置中心的统一规划
中台
hey you~2 天前
云客服多渠道统一接入,消息队列技术实现方案
kafka·消息队列·rocketmq·系统集成·云客服·多渠道接入·接口对接
雾隐隐o2 天前
Kafka 消费者消息丢失:Offset 原理与解决方案
kafka
clz13145213 天前
Kafka 日消 10 亿场景:用 ConcurrentLinkedQueue 实现高性能批量消费缓冲
分布式·kafka·linq
(Charon)4 天前
【Kafka】消息队列学习(一):为什么需要Kafka?从消息队列到整体架构
学习·架构·kafka
clz13145214 天前
风险特征系统 EPC 子系统:基于 Kafka 数据源与数据集配置的 Flink 指标清洗加工
分布式·flink·kafka