从内容社区到AIGC客服:Spring Boot、Redis、Kafka、K8s、RAG的三轮大厂Java面试对话(附标准答案)
角色
- 面试官:语气严肃,追问到底。
- 小Y(水货程序员):基础题能答,复杂题开始"差不多""应该是""看情况"。
面试背景业务
面试官给出一个真实常见的大厂业务:
- 一个内容社区与UGC平台(图文/短视频/评论/私信)
- 有推荐流 、互动计数(点赞/收藏/评论数)
- 有风控与反作弊(刷赞、灌水、恶意注册)
- 采用微服务:内容、用户、互动、推荐、搜索、风控、客服
- 核心技术:Spring Boot/Spring Cloud、Redis、Kafka、MySQL、Elasticsearch、K8s、Prometheus/Grafana、ELK、Jaeger
- 新增:AIGC智能客服,要求基于企业知识库做问答(RAG + Agent)
第一轮(基础能力 + 业务落地):内容发布与互动计数
Q1:面试官
内容发布接口你会怎么设计?需要哪些字段、校验与幂等?
小Y : 接口就 POST /content/publish,传标题、正文、图片啥的。幂等的话加个 token......应该就行。
面试官(点头): 字段还行。幂等 token 不是"应该",要说清楚由谁生成、存哪、过期策略。
Q2:面试官
发布成功后要异步做三件事: 1)审核(机审/人审) 2)入ES用于搜索 3)发到推荐系统做特征抽取 你用什么实现?Kafka 还是 RabbitMQ?为什么?
小Y: 用 Kafka 吧,大厂都用。Kafka 快......然后就行。
面试官 : "快"不是理由。说说吞吐、顺序、回溯、消费组、Exactly-once在这里怎么取舍。
Q3:面试官
点赞计数用 Redis 还是直接 MySQL?如何保证高并发下准确性与成本?
小Y: Redis 肯定快。用 INCR 就行,然后定时写回数据库。
面试官 (认可): 方向对。继续:写回一致性 、丢失风险 、热点 Key 、分片考虑过吗?
Q4:面试官
如果用户重复点击点赞,你如何保证"只点一次"?
小Y: 前端防抖一下?后端再判断一下......
面试官 : 前端不算。后端你要讲:幂等键 、去重集合 、唯一索引 或Lua 原子脚本。
第二轮(进阶:微服务、事务与观测):审核与风控链路
Q1:面试官
发布内容要走"内容服务→审核服务→状态回写"。如果审核超时/失败,怎么设计状态机?
小Y: 就状态加个"审核中""成功""失败"......超时就重试一下。
面试官: 状态机是对的,但要补:
- 可观测的状态变更
- 重试次数与退避
- 死信/人工兜底
Q2:面试官
跨服务写数据库:内容表写入成功,但发 Kafka 事件失败,怎么办?
小Y: 那就 catch 一下再发一次......
面试官 (严肃): 这是经典题:本地事务 + Outbox(事务消息) ,或者 CDC。别用"catch 一下"。
Q3:面试官
如何快速定位一次"发布成功但搜索搜不到"的问题?你会看哪些指标、日志、链路?
小Y: 看日志呗。ELK 搜一下关键词。然后看看 Kafka 有没有消费。
面试官(引导): 不错,但要形成套路:
- Micrometer 指标(publish 成功数、消费延迟、ES 写入失败率)
- Trace(Jaeger/Zipkin)串起链路
- 日志关联 traceId
Q4:面试官
互动计数服务被刷赞攻击,QPS 突增 50 倍,你的限流、熔断、降级怎么做?
小Y: 加机器扩容,然后限流......熔断就是关掉接口?
面试官: 思路有但太粗:
- 网关限流(Redis 令牌桶)
- 服务内 Resilience4j(Bulkhead/CircuitBreaker/RateLimiter)
- 降级返回"计数延迟展示"
第三轮(综合:云原生 + AIGC/RAG/Agent):智能客服与知识库
Q1:面试官
现在要做"社区智能客服":用户问"为什么我发布的内容被限流?"系统要结合风控规则与用户历史给解释。你会怎么做 RAG?数据从哪来?
小Y: RAG 就是把文档放进去,然后 AI 搜一下再回答。数据从数据库导出来就行。
面试官: 太虚了。要讲清:
- 文档加载(规则文档、工单、处罚原因模板、FAQ)
- 切分、向量化、Embedding 模型
- 向量库(Milvus/Chroma/Redis)
- 检索 + 重排 + 引用
Q2:面试官
如何减少 AI 幻觉?当模型不知道时必须说"不知道",并引导人工客服。
小Y: 加个提示词:不要瞎说......
面试官(冷静): 提示词只是底线。还要:
- 置信度阈值(相似度、重排分数)
- 强制引用来源(无引用不回答)
- 工具调用校验(风控处罚原因必须走查询接口)
Q3:面试官
你怎么用 Agent 做复杂工作流:用户申诉→拉取证据→生成申诉单→通知工单系统?
小Y: Agent 就是会自己调用工具......流程就让它自己想。
面试官: "自己想"会出事故。你要设计:
- 工具清单与权限(Tool calling 标准化)
- 会话内存(只存必要字段)
- 工作流编排(状态、超时、补偿)
- 审计日志
Q4:面试官
服务部署在 Kubernetes,上线后发现客服服务偶发 99 线延迟飙升,你怎么排查?
小Y: 看一下 Pod 日志,重启一下......
面试官: 你这属于"玄学运维"。要从:
- HPA 指标(CPU/内存/QPS)
- JVM GC(G1/ZGC 指标)
- 线程池、连接池(HikariCP)
- Trace 看外部依赖(向量库/ES/大模型 API)
面试结束
面试官: 今天先到这。整体基础还可以,部分核心设计题回答比较飘,回去把事务消息、可观测性和 RAG/Agent 的工程化补一下。有结果我们会通知你。
小Y: 好的好的,我回去就"深入理解"。
文末标准答案(把每题讲清楚)
下面按轮次把问题的业务场景 与可落地的技术方案写全,小白也能顺着学。
第一轮答案:内容发布与互动计数
A1:发布接口设计、校验与幂等
场景:用户在弱网/重复点击下可能重复提交内容;同时需要防止"重复发帖"。
设计建议(Spring MVC/Spring Boot)
POST /api/v1/contents- 请求体:
title、body、mediaUrls、topicIds、visibility、clientRequestId - 校验:
- JSR-380 Bean Validation(
@NotBlank @Size) - 敏感词初筛(同步)+ 深度审核(异步)
- JSR-380 Bean Validation(
- 幂等 (关键):
- 客户端生成
clientRequestId(UUID/雪花),服务端要求必传 - Redis
SETNX idempotent:{uid}:{clientRequestId} value EX 10m - 命中则直接返回首次结果(可把 contentId 缓存为 value)
- 客户端生成
- DB 侧兜底:内容表可加
(user_id, client_request_id)唯一索引
A2:Kafka vs RabbitMQ 的取舍(审核/入ES/推荐特征)
场景:发布事件会被多个下游消费,且需要回溯补数。
Kafka 更适合
- 高吞吐:大量内容/互动事件
- Consumer Group:审核、索引、推荐分别独立扩展
- 可回溯:ES 宕机后可从 offset 回放重建索引
- 顺序:按
contentId分区可保证同一内容事件顺序
注意点
- "Exactly-once"不是默认,需要结合事务/幂等:
- 生产端:事务消息或 Outbox
- 消费端:幂等写 ES(以 contentId 作为文档 id),DB 写用唯一键
A3:点赞计数 Redis + 异步落库
场景:点赞是典型高频写,直接打 MySQL 会引发锁竞争与成本飙升。
方案
- Redis 计数:
HINCRBY like:cnt {contentId} 1 - 去重:
SADD like:users:{contentId} {userId},返回 1 才允许 +1 - 定时/流式落库:
- 定时任务扫描增量(或 Kafka 事件)
- 批量 update:
update content set like_cnt = like_cnt + ? where id = ?
风险与处理
- 丢失风险:Redis 宕机 → 开启 AOF + 主从 + 哨兵/集群;或用 Kafka 事件做最终账本
- 热点 Key:
- 分片:
like:cnt:{contentId}%N或 Redis Cluster - 本地缓存 + 聚合上报(Caffeine)
- 分片:
A4:重复点赞幂等
场景:同一用户对同一内容只能点赞一次。
可落地做法
- Redis Set 去重 + Lua 原子化:
- Lua:先
SADD,成功再HINCRBY,保证原子
- Lua:先
- DB 兜底唯一约束:
like(user_id, content_id)唯一索引 - 取消点赞:
SREM+HINCRBY -1(同样 Lua 保证)
第二轮答案:事务消息、状态机、可观测性、弹性
A5:审核状态机与超时处理
场景:发布后进入审核,审核服务可能超时或失败,需要可追踪与可恢复。
状态机示例
DRAFT -> SUBMITTED -> REVIEWING -> (APPROVED | REJECTED) -> (PUBLISHED | BLOCKED)
工程化
- 状态变更写库 + 事件:每次变更记录表(审计)
- 超时:
- 延迟队列/定时任务扫描
REVIEWING超时单 - 重试(指数退避)+ 超过阈值转人工
- 延迟队列/定时任务扫描
- 失败兜底:死信队列(DLQ)+ 管理后台补偿
A6:DB 成功但发消息失败(Outbox/CDC)
场景:内容写入 MySQL 成功,但 Kafka 发送失败,会导致下游(ES/推荐)永远不知道。
推荐方案:Outbox(事务外盒)
- 同一数据库事务内: 1)写
content2)写outbox_event(id, topic, payload, status) - 异步投递器(定时/线程池)扫描 outbox,发送 Kafka 成功再标记 DONE
可选:CDC
- Debezium 监听 binlog,把 outbox 表变更推到 Kafka
消费端幂等
- ES:用
contentId作为文档 id 覆盖写 - DB:用唯一键或版本号(乐观锁)
A7:发布成功但搜索不到------排障路径
目标:用"指标→链路→日志→数据"顺序缩小范围。
- 指标(Prometheus + Grafana + Micrometer)
- publish 成功量、Kafka lag、消费失败率、ES bulk reject
- 链路(Jaeger/Zipkin + OpenTelemetry)
- traceId 贯穿:API → outbox → Kafka produce → indexer consume → ES
- 日志(ELK)
- 统一
traceId、contentId作为字段 - 结构化日志(JSON)方便检索
- 统一
- 数据校验
- Kafka topic 是否存在消息、offset 是否推进
- ES 是否写入、mapping 是否冲突
A8:限流/熔断/降级(刷赞攻击)
场景:恶意请求让互动服务雪崩,需保护核心链路。
- 网关层(Spring Cloud Gateway/Ingress):
- IP/用户维度限流(令牌桶/漏桶,Redis 计数)
- 黑白名单(风控服务下发)
- 服务层(Resilience4j):
- RateLimiter:限制接口并发速率
- Bulkhead:隔离线程池/信号量
- CircuitBreaker:下游(Redis/DB)异常时快速失败
- 降级策略:
- 返回"点赞成功,计数稍后更新"
- 计数展示走缓存,写入走异步队列
第三轮答案:RAG/Agent 工程化、反幻觉、K8s 排障
A9:RAG 方案如何落地(Spring AI/自研)
场景:客服要回答"为什么限流/封禁",必须基于企业规则与用户事实。
数据来源
- 规则文档:风控策略、处罚等级说明(Markdown/HTML/PDF)
- 工单与FAQ:历史问题
- 用户事实:处罚记录、限流命中原因(必须走接口实时查)
流程
- 文档加载(Document Loader)
- 切分(chunk 300~800 tokens,按标题/段落)
- 向量化(Embedding:OpenAI/Ollama 本地模型)
- 向量库(Milvus/Chroma/Redis Vector)存储
- 检索:语义检索 + 关键词(可混合)
- 重排:cross-encoder 或基于 BM25/向量融合
- 生成:提示填充(带引用片段)
关键:回答必须带引用来源(规则条款/工单链接)。
A10:降低幻觉(Hallucination)
- 检索置信度阈值:相似度/重排分数低则不答
- 强制引用:没有检索到足够证据 → 返回模板"未找到依据,转人工"
- 工具校验:涉及用户处罚原因必须调用
PenaltyQueryTool(userId),不允许凭空编造 - 输出约束:JSON schema/结构化输出(便于前端展示与审计)
A11:Agent 工作流(申诉自动化)
目标:让模型"在规则内行动"。
- 工具清单(Tool Calling)
GetUserPenaltyHistory、GetContentInfo、CreateTicket、NotifyIM
- 权限与审计
- 每次工具调用写审计表(谁、何时、参数、结果)
- 会话内存
- 只存必要字段(userId、ticketId、关键摘要),避免泄露
- 工作流编排
- 用状态机/工作流引擎(也可自研)控制:超时、重试、补偿
A12:Kubernetes 上 99 线延迟排查
- 资源:
- Pod CPU/内存是否 throttling(cgroup 限制)
- HPA 是否滞后,是否需要基于 QPS 自定义指标
- JVM:
- GC 次数/停顿(Java 17 可考虑 G1/ZGC),是否频繁 Full GC
- 线程池队列堆积(Tomcat/Netty/自定义线程池)
- 连接池:
- HikariCP 活跃连接耗尽、等待时间过长
- 外部依赖:
- 向量库、ES、模型 API 响应时间(Trace 一眼看出瓶颈)
- 日志与链路:
- 基于 traceId 找到慢点发生在哪个 span
小结(学习路线)
1)先把幂等、去重、缓存计数 写扎实; 2)再补Outbox/CDC 事务消息 ; 3)最后把可观测性 + K8s 排障 与RAG/Agent 工程化串起来。