大厂Java面试实录:Spring Boot/JPA/Redis/Kafka/K8s 可观测性 + Spring AI RAG/Agent(小Y翻车现场)

大厂Java面试实录:Spring Boot/JPA/Redis/Kafka/K8s 可观测性 + Spring AI RAG/Agent(小Y翻车现场)

故事背景

你正在面试一家互联网大厂「内容社区 + AIGC 助手」业务线:用户发帖(UGC)、评论、点赞、关注,外加一个"智能创作助手"帮用户润色标题、自动生成摘要、以及企业侧知识库问答(RAG)。

面试官(严肃脸):M

候选人(搞笑水货程序员):小Y

规则:三轮面试,每轮 3~5 题,循序渐进。


第一轮:基础能力 + 线上常见坑(UGC发帖服务)

Q1:Spring Boot 请求进来后,到 Controller 再到 Service/DAO,大致链路是什么?你如何在链路上做统一日志与Trace?

M: 你先讲讲一次"发帖"请求在 Spring Boot 里的典型处理链路。

小Y: 就......请求进来,然后 Controller 接一下,再调用 Service,最后 MyBatis/JPA 写库。日志嘛......我一般 log.info("来了")

M: 这只是"写代码的链路",我问的是"框架链路"。比如 Filter、Interceptor、AOP、异常处理、线程模型、TraceId。你能补充吗?

小Y: Filter 我用过,Interceptor 也差不多吧......TraceId 就随缘打印。

M:(点头但不满意)基础还行,细节得补。


Q2:你们发帖用了 JPA/Hibernate 或 MyBatis?如果出现"发帖成功但读不到"的问题,你从事务隔离和缓存角度怎么排查?

M: 发帖成功返回 200,但用户刷新列表却看不到。你会怎么定位?

小Y: 可能是缓存没更新?或者事务没提交?我一般重启试试。

M:(眉毛一挑)重启不是方案。说说事务隔离级别、读写分离延迟、二级缓存、以及 Redis 缓存一致性。

小Y: 事务隔离......有个可重复读?读写分离有延迟我知道。

M: OK,继续。


Q3:连接池你用 HikariCP 还是 C3P0?为什么大厂默认更偏向 HikariCP?关键参数你怎么设?

M: 我们高并发发帖服务,连接池怎么选、怎么配?

小Y: 我都用默认配置,HikariCP 好像快一点。

M: 默认不一定扛得住。说说 maximumPoolSize、connectionTimeout、leakDetectionThreshold、以及数据库端 max_connections 的联动。

小Y: 呃......maximumPoolSize 就越大越好?

M:(叹气)继续下一轮。


第二轮:分布式与性能(Feed流、点赞、异步化)

Q4:点赞接口如何设计才能抗住热点?用 Redis 计数 + Kafka 异步落库怎么做?

M: 点赞是典型热点。你会怎么做?

小Y: 直接 update 表加 1?不行就加索引。

M:(严肃)热点 update 会打爆行锁。请给出:Redis 原子计数、去重、Kafka 异步、幂等、以及最终一致性。

小Y: Redis INCR 我知道,Kafka 就发消息,幂等......靠运气?

M:(冷笑)运气不是设计。


Q5:你们微服务之间用 OpenFeign 还是 gRPC?怎么做超时、重试、熔断与限流?

M: 发帖后要调用「内容审核服务」「用户画像服务」。你怎么选通信方式?

小Y: 我用 Feign,写起来舒服。超时就加长点,重试就再试试。

M: 讲讲 Resilience4j 的 TimeLimiter、Retry、CircuitBreaker、Bulkhead,以及重试导致的放大流量问题。

小Y: Resilience4j 我听过,但没背过。

M:(记笔记)继续。


Q6:线上慢接口怎么定位?你会用哪些监控链路:Micrometer/Prometheus/Grafana + ELK + Jaeger/Zipkin?

M: 你被叫去救火:发帖接口 P99 从 80ms 变 800ms。你怎么排?

小Y: 先看日志吧,日志里搜 error。然后看下 CPU。

M: 说具体:指标、日志、Trace 的组合拳。比如直方图、慢 SQL、GC、线程池队列、外部依赖耗时。

小Y: 嗯嗯,我回去补。


第三轮:云原生 + AIGC/RAG(智能创作助手)

Q7:Kubernetes 上灰度发布怎么做?你如何保证"发帖服务"与"AI助手服务"升级过程中不影响用户?

M: 你们用 Kubernetes。灰度/回滚怎么设计?

小Y: K8s 我会 kubectl apply,不行就回滚一下。

M: 讲:Deployment 滚动更新参数、readiness/liveness、PodDisruptionBudget、HPA、以及金丝雀发布的流量切分。

小Y: 我一般靠运维同学。

M:(面无表情)运维同学不是答案。


Q8:智能助手要做企业知识库问答(RAG)。你如何设计:文档加载→向量化→向量库→召回→重排→提示填充→模型调用→结果引用?

M: 给你 2 周落地一个企业文档问答。你会怎么做?

小Y: RAG 就是先把文档丢进去,然后问就出来答案。

M:(盯着小Y)"丢进去"具体怎么丢?Chunk 策略?Embedding 选型?向量库用 Milvus/Chroma/Redis?怎么做引用与防幻觉?

小Y: Chunk 就切一切,Embedding 用 OpenAI?引用我就加一句"仅供参考"。

M:(记笔记)继续。


Q9:如果要做 Agent(智能代理)执行"查库存+下单+发券+通知"这种复杂工作流,你如何设计工具调用标准化?如何避免幻觉导致误操作?

M: 我们要做一个 Agent 帮运营自动处理工单:查订单、改地址、补发券。你怎么做工具框架与权限控制?

小Y: Agent 就让模型自己决定调用哪个接口......它应该不会乱来吧。

M:(冷冷地)线上系统不接受"应该"。讲:工具白名单、Schema 校验、幂等、审批、审计日志、会话内存、失败补偿。

小Y: 嗯......我觉得可以加个"确认"按钮。

M:(叹气)


面试收尾

M: 今天先到这里。你基础还可以,但分布式、可观测性、云原生和 RAG/Agent 的工程化细节比较薄。回去等通知吧。

小Y: 好的好的,我回去把 Resilience4j 和 RAG 背熟。


题目答案详解(业务场景 + 技术点,小白可直接学习)

下面把每一题的"面试官想听到的答案"拆开讲清楚,尽量贴近大厂真实工程。

A1:Spring Boot 一次请求的框架链路 & 统一日志/Trace

典型链路(Servlet 栈 Spring MVC):

  1. Nginx/LB :反向代理、限流、Header 透传(如 X-Request-Id)。
  2. Spring Boot 内置容器(Tomcat/Jetty/Undertow)接入请求。
  3. Filter (Servlet Filter):
    • 最适合做:TraceId 注入、请求体大小限制、鉴权预处理、CORS
    • 例如自定义 OncePerRequestFilter 生成/透传 TraceId(MDC)。
  4. Spring MVC DispatcherServlet:核心分发器。
  5. HandlerInterceptor
    • 更适合做:用户身份解析、业务埋点、接口耗时统计
  6. Controller → Service → Repository/Mapper
  7. AOP
    • 适合做:统一日志、参数校验、权限注解、事务边界(@Transactional 本质是 AOP)。
  8. 异常处理@ControllerAdvice + @ExceptionHandler 统一返回结构。

统一日志/Trace(关键点):

  • 通过 SLF4J + Logback/Log4j2
  • 使用 MDC 放入 traceIduserId
    • 入口 Filter 创建 traceId,写入 MDC。
    • 日志格式加 %X{traceId}
  • 分布式链路追踪:
    • Micrometer Tracing + OTEL (或 Brave)上报到 Jaeger/Zipkin
    • 下游调用(Feign/gRPC)把 trace context 透传。

(加分点)WebFlux:线程模型是事件循环,MDC 需要 Reactor Context 适配,不能靠 ThreadLocal 直接传。


A2:发帖成功但读不到:事务、读写分离、缓存一致性排查

常见原因清单:

  1. 事务未提交/回滚
    • @Transactional 是否生效(不要在同类内部调用导致 AOP 失效)。
    • 异常被吞掉导致回滚/不回滚不符合预期。
  2. 读写分离延迟
    • 写入主库成功,但读走了从库(复制延迟)。
    • 解决:写后读走主库、或引入"读主"策略(短时间 sticky)。
  3. 隔离级别导致的可见性
    • 常见是 READ COMMITTED / REPEATABLE READ 配合长事务。
    • 如果列表查询在一个长事务里,可能读到旧快照(RR)。
  4. 缓存不一致
    • 列表页缓存(Redis)没失效,仍返回旧数据。

工程化做法:缓存一致性三板斧

  • Cache Aside(旁路缓存):写库后删除缓存;读时缓存未命中再查库回填。
  • 删除缓存要注意:
    • 先写库再删缓存(常用);
    • 或引入消息队列重试删除(避免删除失败)。
  • 对热点列表:用 逻辑过期 + 异步刷新,降低缓存击穿。

(加分点)如果用了 Hibernate 二级缓存,要确认二级缓存与查询缓存是否开启,可能导致"写后读旧"。


A3:HikariCP vs C3P0 & 关键参数

为什么 HikariCP 常用:

  • 性能和稳定性更好(更轻量,延迟低)。
  • Spring Boot 默认也倾向 HikariCP。

关键参数(示例思路,不是固定值):

  • maximumPoolSize
    • 不是越大越好。
    • 需要结合:单实例 QPS、SQL 耗时、DB max_connections、以及实例数。
  • minimumIdle:空闲连接预热,防止突发延迟。
  • connectionTimeout:拿不到连接的等待时间,防止线程堆积。
  • leakDetectionThreshold:连接泄漏检测(仅排查期启用,避免性能影响)。

与数据库联动:

  • 假设 DB 允许 2000 连接,总共有 50 个应用实例,则单实例 pool 上限要有预算(比如 20~30),还要留给运维工具、其他服务。

A4:点赞热点:Redis 计数 + Kafka 异步落库 + 幂等

目标:

  • 接口快(内存级)。
  • DB 不被热点行锁打爆。
  • 允许最终一致,但要可控。

设计:

  1. Redis 去重
    • SADD like:post:{postId} userId 判断是否首次点赞。
  2. Redis 计数
    • 首次点赞成功后 INCR like:count:{postId}
    • 取消点赞:SREM + DECR
  3. Kafka 异步
    • 发送 like/unlike 事件到 Kafka topic。
    • 消费端批量落库(减少写放大)。
  4. 幂等(必须)
    • 事件包含 eventId(postId,userId,action,ts)
    • 消费端用唯一键/去重表保证同一事件只处理一次。
  5. 最终一致性兜底
    • 定时任务把 Redis 计数与 DB 对账(或以 Kafka 日志为准重放)。

A5:Feign vs gRPC & Resilience4j 容错治理

选择:

  • OpenFeign(HTTP/JSON):生态好、易调试、适合对外 REST。
  • gRPC(HTTP/2 + Protobuf):性能高、IDL 强约束、适合内部高频调用。

治理:Resilience4j 常见组合:

  • TimeLimiter:控制最大调用时长。
  • Retry:只对幂等请求重试;注意重试会放大流量。
  • CircuitBreaker:下游异常率高时快速失败,保护自身。
  • Bulkhead(舱壁隔离):线程池/信号量隔离,防止被慢依赖拖垮。
  • RateLimiter:限流。

要点:

  • 超时要比上游更短,形成"超时金字塔"。
  • 重试要加退避(backoff),并设置最大重试次数。
  • 写操作尽量不重试或使用业务幂等 token。

A6:慢接口定位:指标 + 日志 + Trace

三件套:

  1. 指标(Metrics) :Micrometer 暴露到 Prometheus,Grafana 看:
    • QPS、P95/P99 延迟直方图
    • JVM:GC 次数/耗时、堆内存、线程数
    • Tomcat/线程池:busy threads、queue size
    • 连接池:active/idle、等待时间
  2. 日志(Logs) :ELK(Elasticsearch + Logstash/Fluentd + Kibana):
    • 慢 SQL、异常堆栈、关键业务日志(带 traceId)
  3. 链路(Tracing) :Jaeger/Zipkin:
    • 看一次请求在 DB、Redis、Kafka、外部 HTTP 上分别花多久

典型排查路径:

  • P99 飙升 → 看是否某个下游 span 变慢 → 对应慢 SQL/连接池耗尽/GC 抖动。

A7:K8s 灰度发布与稳定性

滚动更新关键参数:

  • maxSurge / maxUnavailable 控制替换节奏。
  • readinessProbe:未就绪不接流量。
  • livenessProbe:卡死自动重启。

高可用:

  • PDB(PodDisruptionBudget):避免同时被驱逐太多。
  • HPA:根据 CPU/自定义指标扩缩。

灰度/金丝雀:

  • 基础:分批次滚动。
  • 进阶:基于 Ingress/Service Mesh 做按比例/按用户标签切流。
  • 必须配套:
    • 指标看板(成功率、延迟)
    • 一键回滚

A8:RAG 落地全链路(Spring AI/向量库/防幻觉)

**业务目标:**企业文档问答,回答要"可追溯、有引用"。

标准流水线:

  1. 文档加载(Document Loader):PDF/HTML/Word/Confluence 等。
  2. 清洗与切分(Chunking)
    • 按语义段落/标题切;
    • chunk 大小常见 300~800 tokens,配 overlap(例如 50~100 tokens)。
  3. 向量化(Embedding)
    • 选 OpenAI/本地 Ollama embedding 模型;
    • 保证同一语料同一模型版本,方便重建。
  4. 向量库(Milvus/Chroma/Redis Vector)
    • vector + docId + chunkId + metadata(来源,页码,权限)
  5. 召回(Retrieval)
    • 相似度检索 topK。
    • 可混合 BM25(Elasticsearch)做混合检索。
  6. 重排(Rerank,可选):提升相关性。
  7. 提示填充(Prompt Template)
    • 把检索片段以"引用块"形式塞进 prompt。
  8. 模型调用(LLM)
    • 输出答案 + 引用列表。
  9. 防幻觉策略:
    • 明确指令:只根据提供的 context 回答;不知道就说不知道。
    • 输出强制包含引用(无引用则拒答)。
    • 关键场景加人工审核/置信度阈值。

Spring AI 对应点:

  • VectorStore(Milvus/Redis 等实现)
  • EmbeddingModel
  • Retriever + PromptTemplate
  • 会话记忆(Chat Memory)用于多轮问答,但要注意权限隔离。

A9:Agent 工具调用框架:标准化、权限、审计、反幻觉误操作

**目标:**让模型"会用工具",但不允许乱用工具。

核心设计:

  1. 工具白名单 + Schema
    • 每个工具定义:name、description、JSON Schema(入参/出参)。
    • MCP/函数调用标准化就是为这个服务的。
  2. 权限控制
    • 工具执行前做鉴权(RBAC/ABAC)。
    • 细到"能查不能改""能改必须审批"。
  3. 幂等与审计
    • 工具调用要有 requestId。
    • 所有工具调用写审计日志(谁、何时、改了什么)。
  4. 高风险动作二次确认
    • 改地址、退款、发券:必须 human-in-the-loop。
  5. 失败补偿
    • 工作流要支持重试、回滚、死信队列。
  6. 会话内存
    • 只保存必要信息;注意不同用户/租户隔离。

避免"幻觉操作"的硬规则:

  • 模型输出不能直接执行写操作;必须通过工具层校验。
  • 工具层不信任模型:校验参数、范围、业务约束。

你可以如何用这套答案去准备面试

  • 把每题当成一条"项目叙事线":UGC发帖 → 点赞热点 → 微服务治理 → 可观测性 → K8s 灰度 → RAG/Agent 落地
  • 真实面试里,面试官更喜欢你讲:为什么这么做、踩过什么坑、怎么验证有效
相关推荐
likerhood1 小时前
认识 JVM:Java 程序背后的那台“隐形计算机”
java·开发语言·jvm
wangchunting1 小时前
Java9功能更新说明
java·开发语言
hikktn1 小时前
30万数据导出从2分钟到15秒:一场与内存溢出的生死较量【宗申集团】
java
武帝为此1 小时前
【软件开发日志介绍】
java·前端·数据库
likerhood1 小时前
Java 反射与注解的详细讲解
java·开发语言·数据库
asdfg12589631 小时前
从Java的设计模式看接口和实现---List与ArrayList
java·开发语言·设计模式·面向对象·面向接口
djk88881 小时前
.net swagger api 开启跨域 开启注释
java·前端·.net
庞轩px1 小时前
第六篇:Redis Cluster——分布式缓存的进阶方案
redis·分布式·缓存
秋91 小时前
java中对操作mysql8.0.46与MySQL9.7.0有什么区别,并举例说明
android·java·adb