kafka的docker配置
Kafak

kafka的docker配置

Kafka 的 Docker 配置:一次点赞为什么会把 Kafka 内存打爆?

这篇笔记记录一次本地项目排障:知光后端在执行点赞 / 取消点赞时,Kafka 大量报错,最后定位到 Kafka Docker 配置不合理。

1. 现象

在后端日志里,先看到类似这样的错误:

Topic counter-events not present in metadata after 60000 ms

后来修复端口和 topic 后,又出现大量消费者组相关错误:

Unexpected error from SyncGroup:
The server experienced an unexpected error when processing the request.

Kafka 服务端日志里最终能看到真正的根因:

java.lang.OutOfMemoryError: Java heap space

也就是说,后端不是单纯代码异常,而是 Kafka broker 自己内存爆了。

2. 点赞为什么会用到 Kafka?

这个项目里,点赞不是简单地把数据库字段 like_count + 1。

它的流程大概是:

用户点赞 / 取消点赞
-> 后端修改 Redis 位图,记录这个用户是否点过
-> 后端发送一条 counter-events 消息到 Kafka
-> Kafka 消费者异步聚合点赞数
-> 聚合结果再写回 Redis 计数结构

这样设计的好处是:

  • 点赞接口响应更快;
  • 高频点赞不会每次都同步重算总数;
  • 计数可以异步聚合,降低主链路压力。

所以,用户点一下赞,确实会触发 Kafka。

但是这次的问题不是“点赞消息太大”。

点赞消息本身可能只有几百字节,比如:

{
  "entityType": "knowpost",
  "entityId": "123",
  "metric": "like",
  "idx": 0,
  "userId": 10001,
  "delta": 1
}

它远远不到 1MB,更不可能自己撑爆 Kafka。

3. 第一层问题:端口和 advertised listener 不一致

一开始 Kafka Docker 配置里有一个关键错误:

KAFKA_CFG_ADVERTISED_LISTENERS: PLAINTEXT://10.13.1.5:9080

但是 Docker 实际暴露出来的端口是:

ports:
  - ${HOST_IP}:${PANEL_APP_PORT_HTTP}:9092

.env 里:

PANEL_APP_PORT_HTTP=9092

也就是说:

真实可用地址:10.13.1.5:9092
Kafka 对外宣称:10.13.1.5:9080

这会导致客户端先连上 Kafka,但 Kafka 返回的 broker 地址是错的。于是后端会出现 metadata 超时、topic 找不到、消费者反复重连等问题。

修复方式是统一成 9092:

KAFKA_CFG_ADVERTISED_LISTENERS: PLAINTEXT://10.13.1.5:9092

后端也要对应:

spring:
  kafka:
    bootstrap-servers: 10.13.1.5:9092

4. 第二层问题:Kafka 内存配置不合理

端口修好以后,Kafka topic 能正常列出来:

canal-outbox
counter-events

但是消费者组仍然大量报错。继续看 Kafka 服务端日志,发现 broker 自己爆了:

java.lang.OutOfMemoryError: Java heap space

当时 Kafka 的配置大概是:

KAFKA_CFG_MESSAGE_MAX_BYTES: 524288000
KAFKA_CFG_FETCH_MESSAGE_MAX_BYTES: 524288000
KAFKA_CFG_PARTITION_FETCH_BYTES: 524288000
KAFKA_CFG_REPLICA_FETCH_MAX_BYTES: 524288000
KAFKA_CFG_MAX_REQUEST_SIZE: 524288000
KAFKA_HEAP_OPTS: -Xmx512m -Xms256m

这些数字里,524288000 大约等于 500MB。

翻译成人话:

Kafka 最多只有 512MB JVM 堆内存,
但是允许单条消息 / 单次请求 / 单次拉取达到 500MB。

这就像:

仓库总容量只有 512MB,
但允许单个包裹最大 500MB。

哪怕实际点赞消息很小,Kafka 在网络缓冲、消费者组协调、请求处理、元数据写入时,也可能围绕这些“最大上限”准备资源。再加上之前端口错误导致消费者频繁加入、离开、重平衡,最终 broker 的 group coordinator 处理消费者组状态时爆了堆内存。

5. 每个配置是什么意思?

KAFKA_CFG_MESSAGE_MAX_BYTES

KAFKA_CFG_MESSAGE_MAX_BYTES: 524288000

Kafka broker 允许单条消息最大约 500MB。

KAFKA_CFG_FETCH_MESSAGE_MAX_BYTES

KAFKA_CFG_FETCH_MESSAGE_MAX_BYTES: 524288000

消费者一次拉取消息时,允许从 broker 拿到最大约 500MB 的数据。

KAFKA_CFG_PARTITION_FETCH_BYTES

KAFKA_CFG_PARTITION_FETCH_BYTES: 524288000

消费者从单个分区一次最多拉取约 500MB。

KAFKA_CFG_REPLICA_FETCH_MAX_BYTES

KAFKA_CFG_REPLICA_FETCH_MAX_BYTES: 524288000

Kafka 副本之间同步数据时,一次最多拉取约 500MB。

单节点 Kafka 也可能保留这个配置,但本地开发一般不需要这么大。

KAFKA_CFG_MAX_REQUEST_SIZE

KAFKA_CFG_MAX_REQUEST_SIZE: 524288000

Kafka 允许客户端发来的单个请求最大约 500MB。

KAFKA_HEAP_OPTS

KAFKA_HEAP_OPTS: -Xmx512m -Xms256m

Kafka JVM 堆内存:

-Xms256m = 启动时先给 256MB 堆内存
-Xmx512m = 最多只能用 512MB 堆内存

这和上面的 500MB 请求上限明显不匹配。

6. 推荐修复配置

对于这个项目,点赞、收藏、outbox 事件都不应该是超大消息。开发环境可以把最大消息限制到 10MB,并把 Kafka heap 提高到 1GB。

推荐配置:

networks:
  1panel-network:
    external: true

services:
  kafka:
    container_name: ${CONTAINER_NAME}
    deploy:
      resources:
        limits:
          cpus: ${CPUS}
          memory: ${MEMORY_LIMIT}
    environment:
      ALLOW_PLAINTEXT_LISTENER: "yes"
      KAFKA_CFG_ADVERTISED_LISTENERS: PLAINTEXT://10.13.1.5:9092
      KAFKA_CFG_CONTROLLER_LISTENER_NAMES: CONTROLLER
      KAFKA_CFG_CONTROLLER_QUORUM_VOTERS: 0@127.0.0.1:9093
      KAFKA_CFG_FETCH_MESSAGE_MAX_BYTES: 10485760
      KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
      KAFKA_CFG_LISTENERS: PLAINTEXT://:9092,CONTROLLER://:9093
      KAFKA_CFG_LOG_RETENTION_MS: 60000
      KAFKA_CFG_MAX_REQUEST_SIZE: 10485760
      KAFKA_CFG_MESSAGE_MAX_BYTES: 10485760
      KAFKA_CFG_NODE_ID: 0
      KAFKA_CFG_PARTITION_FETCH_BYTES: 10485760
      KAFKA_CFG_PROCESS_ROLES: controller,broker
      KAFKA_CFG_REPLICA_FETCH_MAX_BYTES: 10485760
      KAFKA_HEAP_OPTS: -Xmx1g -Xms512m
    image: bitnamilegacy/kafka:4.0.0
    labels:
      createdBy: Apps
    networks:
      - 1panel-network
    ports:
      - ${HOST_IP}:${PANEL_APP_PORT_HTTP}:9092
    restart: always

其中:

10485760 = 10MB

这个大小对普通业务事件已经很宽松。

7. 修复后要验证什么?

1. Kafka broker 地址是否正确

应该看到 broker 是:

10.13.1.5:9092

而不是:

10.13.1.5:9080

2. topic 是否存在

至少需要:

counter-events
canal-outbox

其中:

  • counter-events:点赞、收藏等计数事件;
  • canal-outbox:Canal 监听 outbox 表后转发的领域事件。

3. 后端是否读取了新配置

后端配置应该是:

spring:
  kafka:
    bootstrap-servers: 10.13.1.5:9092

如果改了 application.yml,要重新编译或重新启动后端,避免运行进程还读着旧配置。

4. Kafka 日志是否还出现 OOM

重点看有没有:

java.lang.OutOfMemoryError: Java heap space

如果还有,说明 Kafka 堆内存或容器内存仍然不够,或者还有异常大消息。

8. 这次学到的东西

这次问题很容易误判成:

点赞接口写错了
Kafka topic 没建
前端按钮有问题

但真正链路是:

点赞触发 Kafka 事件
-> Kafka advertised listener 配错端口
-> 客户端 metadata / consumer group 不稳定
-> Kafka 配置允许 500MB 超大请求,但 JVM heap 只有 512MB
-> broker 在消费者组协调时 OOM
-> 后端看到大量 SyncGroup / LeaveGroup 报错

所以排查 Kafka 问题时,要按顺序看:

  1. 客户端连接的是哪个 bootstrap-servers;
  2. Kafka 对外宣称的 advertised.listeners 是什么;
  3. Docker 实际暴露的端口是什么;
  4. topic 是否存在;
  5. broker 自己有没有 OOM;
  6. 消费者组是否在反复 rebalance。

9. 一句话总结

这次不是“一个点赞消息太大”,而是:

Kafka 端口配置错 + 超大消息上限 + 很小的 JVM 堆内存

共同导致 Kafka broker 在消费者组协调时内存爆掉。

kafka的docker配置

Kafka的docker配置

你可能也会喜欢...

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注