Java后端面试36问:电商大促场景下的JVM、Redis、Kafka与微服务连环拷问(含答案)
场景设定:某互联网大厂“XX商城”后台开发岗位面试现场。 面试官:王工,架构组资深大佬,眼镜反光,气场两米八。 候选人:谢飞机,简历写得天花乱坠,Java满级,实则一碰就碎的“水货”。
前情提要
谢飞机把头发梳成大人模样,穿着不太合身的西装,背着一个装满“八股文”的U盘走进了面试间。他坚信,只要把网上那些“面试速成宝典”背熟,就能拿捏今天的面试官。
“请坐。先做个自我介绍吧。”王工推了推眼镜。
“好的面试官,我叫谢飞机,有5年Java开发经验……主攻电商高并发,做过XX商城、XX团购、XX闪购……”
“打住。你简历上写了电商大促项目?那我们今天就从一场大促说起。”王工在笔记本上敲了几个字,一场让谢飞机“螺旋升天”的连环追问,开始了。
第一轮:热身问(基础关)——谢飞机的舒适区
面试官意图:先探底,确认候选人是不是“纯背题选手”。这一轮他答得不错,王工甚至露出了难得的微笑。
问题1-1:先介绍一下你负责的电商项目,技术栈是什么?
谢飞机眼睛一亮,这可是他简历上背了一百遍的开场白。
“我们做的是XX商城,典型的高并发电商。技术栈是:Java 11 + Spring Boot 2.x + Spring Cloud + MyBatis-Plus + Redis + Kafka + MySQL + H2……哦不,MySQL + ES。整体是微服务架构,服务间用 OpenFeign 调用,注册中心用的 Nacos,配置中心也是 Nacos,网关用的 Spring Cloud Gateway。”
“可以,简历基本属实。那你为什么选 Java 11 而不是 8?”
问题1-2:Java 8 / 11 / 17 你怎么选?有什么区别?
“嗯……这个我熟!”谢飞机挺直了腰板。
“Java 8 是经典老将,Stream 和 Lambda 用着顺手,社区资料最多;Java 11 是长期支持版本(LTS),加入了 var 局部变量推断、String 新增了 repeat/isBlank 等 API,还有 HTTP Client 可以直接发请求;Java 17 是最新的 LTS,有密封类、新的 switch 表达式、text block 文本块,性能也更好。我们新项目用 11,兼顾稳定和特性。”
王工点点头:“嗯,LTS 版本确实是大厂的主流选择,说明你对版本策略有概念。你们项目是怎么构建和打包的?”
问题1-3:Maven 和 Gradle 你都用过,为什么最终选了 Maven?
“这题我会!”谢飞机擦擦汗,“Maven用 XML 写 pom.xml,约定优于配置,生态成熟,CI/CD 里插件多,团队上手成本低;Gradle用 Groovy/Kotlin DSL,构建速度更快,增量编译强,适合大工程和 Android。我们团队当时大多数人只会 Maven,为了降低学习成本就统一用了 Maven。”
“合理,技术选型要考虑团队,不是越新越好。”王工难得地夸了一句,“那说说一个 HTTP 请求从客户端到你后端,Spring MVC 是怎么处理的?”
问题1-4:请描述 Spring MVC 处理请求的完整流程。
谢飞机清了清嗓子,开始背他那套“流程八股”:
“DispatcherServlet(前端控制器)是核心。流程大概是:
- 客户端发请求,先经过DispatcherServlet;
- DispatcherServlet 调用HandlerMapping找到对应的Handler(Controller 方法);
- 再通过HandlerAdapter去执行这个 Handler;
- 执行前会经过一系列拦截器 HandlerInterceptor;
- Controller 处理完返回数据,经过HandlerAdapter组装成ModelAndView;
- 如果是接口,经过HttpMessageConverter把对象序列化成 JSON 返回给前端;
- 最后视图解析器渲染,或者直接 @ResponseBody 返回。”
“虽然有点倒背如流的味道,但核心链路没错。”王工在纸上画了个勾,“大促场景,下单接口第一个瓶颈就是数据库。你们怎么扛的?跟我说说你的缓存方案。”
问题1-5:商品详情页高并发读取,你们怎么做缓存?
“我们用Redis + Caffeine 本地缓存做两级缓存。”谢飞机越说越顺,“先查 Caffeine 本地缓存,没命中再查 Redis,Redis 没有再查数据库,查完回填。热点商品用 Caffeine 扛,Redis 做兜底,MySQL 只在缓存全失效时才被打到,QPS 能扛住。”
“不错,你还知道本地缓存和分布式缓存配合。那……如果 Redis 挂了,或者缓存大面积失效呢?”王工的眼神突然犀利起来。
“呃……挂了就……挂了……我们……有主从……”谢飞机的声音开始发抖。
“好,这个问题我们放到第二轮。今天先到这里,第二轮见。”王工微微一笑。
第二轮:进阶问(并发关)——谢飞机的“薛定谔式”回答
面试官意图:从缓存穿透、缓存雪崩,聊到消息削峰、分布式锁,都是电商大促的“送命题”。谢飞机开始原形毕露。
问题2-1:大促期间,很多请求打到不存在的商品上(比如活动被下架),Redis 全是空,数据库被打爆。这种现象叫什么?怎么解决?
“这个……叫缓存穿透!”谢飞机一激灵,背过的词冒出来了,但接下来就卡壳了,“解决就是……嗯……就是……把空值也缓存起来?还有……布隆过滤器?对,布隆过滤器!就是那个……那个会误判的位图!”
“布隆过滤器误判的概率怎么控制?误判了会怎样?”王工追问。
“误判……误判就是……有些商品明明不存在,它却说可能存在……那就会去打数据库……但至少拦截了大部分……”谢飞机的答案像一锅乱炖。
“概念知道,细节模糊。那缓存雪崩呢?”
问题2-2:如果 Redis 里大量 key 在同一时刻过期,或者 Redis 整个宕机,大量请求直接打到 MySQL,怎么处理?
谢飞机脑子里的“八股文碎片”飞速拼装:
“缓存雪崩……解决有几点:设置过期时间加随机值,避免同一时刻集体过期;用多级缓存,本地缓存兜底;Redis 搞高可用,主从+哨兵或者 Cluster;还可以限流降级,把打崩数据库的流量挡在网关;对了,缓存不设置过期时间,用逻辑过期后台异步更新……”
王工眼睛一亮:“哦?你居然知道逻辑过期和随机过期时间。那再考你一个:超卖问题,你怎么解决?”
问题2-3:大促秒杀,库存只有100件,10万人抢,怎么保证不超卖?
“这个我会!”谢飞机兴奋地拍了下桌子,随即意识到失态,赶紧缩回去,“我们用Redis 的 Lua 脚本 + 分布式锁来控制库存扣减。先把库存预扣到 Redis,秒杀请求先在 Redis 里减库存,用 Lua 保证原子性,减成功才放行去下单,最后异步把结果同步回数据库。”
“那你知道 Lua 脚本为什么能保证原子性吗?Redis 是单线程吗?”王工抛出了最关键的灵魂拷问。
“呃……Lua 原子……因为……Redis 处理命令是单线程的?不对,Redis 6.0 网络模块是多线程了……执行命令是单线程……Lua 在执行过程中不会被其他命令打断……嗯……大概是这样吧……”谢飞机的答案开始左右摇摆,“分布式锁……我们用的 Redisson 的 lock,底层是……是……SETNX + 过期时间 + ……看门狗?对,看门狗续期……”
“能说出单线程执行命令和 Redisson 看门狗,说明你刷过不少题,但深挖就露怯了。”王工在本子上写下一行评语,“好,库存扣了,接下来下单要通知积分、发优惠券、扣减物流仓。如果都同步做,下单接口会非常慢。你们怎么做异步解耦?”
问题2-4:下单成功后,要同步做积分、券、物流等多件事,如何异步解耦?怎么保证不丢消息?
“我们用Kafka!”这次谢飞机答得干脆,“订单服务把下单结果发到 Kafka 的 order_topic,积分服务、券服务、物流服务各自订阅,各干各的,互不阻塞。这样下单接口只做核心事,响应快。”
“那如果积分服务消费失败,消息会丢失吗?Kafka 是怎么保证消息可靠性的?”王工继续加压。
“消息丢失……这个……生产端可以设置acks=all,并且开启重试,保证 broker 收到了;broker 端设副本数大于1 + min.insync.replicas;消费端要手动提交 offset,处理成功再提交,别用自动提交……这样就不会丢……”谢飞机停顿了一下,声音越来越小,“但是……如果服务处理到一半挂了,还没来得及提交 offset,重启后会重复消费……那就要……要……幂等!用 Redis 存消费过的消息 ID,实现消费幂等!对,幂等!”
“不错,能一口气说出 acks、副本、手动提交 offset、消费幂等,这轮算你及格。最后一个问题——”王工扶了扶眼镜,抛出了今天的“核弹”。
问题2-5:大促下单包含扣库存、扣余额、生成订单。如果扣了库存,余额扣款失败,你怎么保证数据一致?你了解分布式事务吗?
空气突然安静。谢飞机的额头开始冒汗,他沉默了足足十秒,终于开口:
“分布式事务……嗯……我们可以用Seata……它有 AT 模式、TCC 模式……AT 模式就是……自动生成 undo_log……TCC 就是 Try-Confirm-Cancel……还有……MQ 的最终一致性……本地消息表?对,本地消息表 + 消息重试……两阶段提交 2PC 也行,但是……阻塞……性能差……XA 就是数据库层面的 2PC……呃……还有 Saga……补偿……”
他把所有关键词都堆了出来,却没有一个能讲清楚。
王工看着谢飞机语无伦次的样子,笑了笑:“你知道 AT 模式本质是‘业务无侵入’的全局事务,靠全局锁和 undo_log 实现回滚,但性能有损吗?你知道 TCC 要自己写三个方法还要处理悬挂、空回滚吗?”
“……”谢飞机低下了头,他只知道名词,并不知道内涵。
“先休息一下,喝口水,我们聊聊第三轮。”
第三轮:终局问(架构关)——谢飞机的“滑铁卢”
面试官意图:从微服务治理、服务发现、熔断降级,问到安全认证、链路追踪、K8s 部署,检验候选人是否真的从“会用”到了“懂原理”。
问题3-1:你们订单、库存、积分拆成了微服务,服务之间是怎么互相发现和调用的?Feign 调用失败怎么办?
“服务注册与发现用的是Nacos/Consul/Eureka……服务启动时把自己注册到注册中心,客户端通过注册中心拿到服务地址列表,再用OpenFeign做声明式 HTTP 调用,配合LoadBalancer/Ribbon做负载均衡。”谢飞机开始像复读机一样输出。
“那如果订单服务调用库存服务,库存服务超时、报错,你不加处理,会发生什么?”
“会……会一直重试?重试多了会拖垮库存服务,甚至引发雪崩?所以要加熔断降级……用Resilience4j / Sentinel……超时时间、失败率阈值,达到阈值就熔断,走 fallback 降级逻辑……”
“那你知道 Eureka 和 Nacos 在服务发现上的最大区别吗?CAP 你怎么取舍?”王工继续挖。
“区别……Eureka 是 AP,Nacos 支持 AP 和 CP 切换……我们 Nacos 配置中心用 CP……注册中心用 AP……保证可用性……”谢飞机用一连串缩写堆砌出了答案,但显然没有真正消化。
“能背出 CAP 和 AP/CP 已经比很多人强了。那大促接口是需要鉴权的,用户登录状态怎么校验?说说你了解的认证方案。”
问题3-2:大促接口要对用户做鉴权,你是用 Session 还是 JWT?为什么?
“我们用JWT + Spring Security / OAuth2!”谢飞机感觉这题终于能说全了,“JWT 是无状态的,用户登录后服务端签发一个 token,里面带用户 ID 和过期时间,客户端每次请求带上Authorization: Bearer xxx,网关统一校验。因为无状态,方便水平扩展,适合微服务和跨域场景。”
“Session 呢?和 JWT 比有什么痛点?”
“Session 是服务端存储的,分布式场景要搞 Session 共享,存 Redis……有状态……扩容麻烦……JWT 的问题就是……不能主动踢人、不能服务端撤回,泄露了就只能等过期,所以要把过期时间设短,配合 refresh token 刷新……”
“可以,知道 JWT 的短板,说明不是纯背书。那你怎么实现 OAuth2 的授权码流程?或者你们大促前要做安全风控,防止脚本刷接口,怎么做?”
“风控……限流!网关层用令牌桶限流……再配合验证码……滑块验证……设备指纹……IP 黑名单……用 Redis 做计数器统计访问频次……反正就是……把羊毛党挡在外面……”谢飞机越说越心虚,因为他只见过别人写的限流配置。
问题3-3:线上出了问题,用户说“下单很慢”,你怎么定位是哪个服务、哪个环节慢了?
“这个……我们有Prometheus + Grafana监控指标,用Micrometer埋点……还有Zipkin/Jaeger做链路追踪,在 Feign 和 MQ 消费里埋了 traceId……把一次请求经过的所有服务串起来看耗时……”谢飞机回答得还算流畅,因为他确实用过。
“日志呢?你们日志怎么打的?大促排查用什么?”
“日志用SLF4J + Logback……线上通过ELK:Filebeat 采集、Logstash 传输、Elasticsearch 存储、Kibana 可视化,再配合 traceId 把同一请求的日志串起来检索……”
“哦?这轮你居然答得不错。”王工点点头,“那最后一问——你们服务是怎么部署上线的?聊聊 Docker 和 K8s。”
问题3-4:说说你们项目的 Docker 化与 Kubernetes 部署?
谢飞机心里一紧,这是他简历上最唬人的一行。
“嗯……我们用Docker把每个微服务打成镜像……Dockerfile 用多阶段构建……基础镜像用 openjdk:11……打出来的镜像推送到 Harbor……然后 CI/CD 用GitLab CI / Jenkins + GitHub Actions,代码 push 触发构建、测试、打包、推送镜像……再部署到Kubernetes……用 Deployment 管理副本……Service 暴露……HPA 根据 CPU 自动扩缩容……”
“K8s 里 Pod 挂了,谁负责拉起?Deployment 和 StatefulSet 有什么区别?你们 MySQL、Redis 这种有状态服务在 K8s 里怎么跑的?”
“……”谢飞机愣住了。他其实只见过别人用 K8s 界面点“更新”,从没亲手写过 yaml。
“Pod 挂了由……ReplicaSet?控制器自动……拉起……Deployment 管无状态……StatefulSet 给每个 Pod 固定标识、稳定网络和存储……MySQL 这种一般不建议随便扔 K8s,有状态服务我们用裸机或云 RDS……Redis 也尽量不用 StatefulSet 那套,直接上云或者哨兵集群……”
谢飞机的回答像踩在棉花上,每一句都是“感觉是这样”,没有一句有底气。
“谢先生,今天的交流非常充分。”王工合上笔记本,站起来,礼貌地伸出手,“您的学习热情我们感受到了,技术广度也不错。这样,您先回去,等我们技术委员会综合评估后,再通知您面试结果,大概一周内。路上注意安全。”
“好的好的!那我……那我等通知!谢谢面试官!”谢飞机差点同手同脚走出门,冷汗已经浸透了衬衫。
他回头看了一眼会议室,王工正把“建议挂:原理理解不足,需加强深挖”写进评语里。
——完——
📚 附:面试问题与知识点详解(写给小白的完整答案)
以下按三轮回合顺序,把每道题背后的业务场景和技术点讲透。建议对照上面的故事场景阅读,效果更佳。
第一轮详解:基础关
1-1 电商项目技术栈选型
- 业务场景:电商核心链路 = 商品 → 购物车 → 下单 → 支付 → 履约(物流/库存)。
- 技术选型:
- Java 11(LTS)做语言底座;
- Spring Boot 简化配置、内嵌 Tomcat 快速启动;
- Spring Cloud(注册中心 Nacos、配置中心 Nacos、网关 Gateway、RPC OpenFeign)支撑微服务;
- MyBatis-Plus 做 ORM(简化 CRUD、自带分页与逻辑删除);
- Redis 扛热点缓存与分布式锁;Kafka 做异步削峰;Elasticsearch 做商品检索。
1-2 Java 8 / 11 / 17 版本差异
| 版本 | 定位 | 核心特性 | 支持 | |------|------|----------|------| | Java 8 | 里程碑版本 | Lambda、Stream、Optional、新的日期时间 API(LocalDate 等)、接口默认方法 | 已停止免费商用更新 | | Java 11 | LTS | var 局部变量推断、String.repeat()/isBlank()、标准 HTTP Client、ZGC 引入 | 长期支持 | | Java 17 | LTS | 密封类 sealed、switch 表达式、文本块 text block、增强的伪随机数 | 长期支持,性能更好 |
面试要点:大厂通常紧跟 LTS,即 8 → 11 → 17;8 与 17 语法兼容性高,迁移成本可控。
1-3 Maven vs Gradle
- Maven:XML 描述依赖(pom.xml)、约定优于配置、生命周期(compile→test→package)、生态成熟、CI 集成简单。缺点是 XML 冗长、构建速度偏慢。
- Gradle:Groovy/Kotlin DSL、基于任务图的增量构建、构建缓存,速度明显更快,适合大型多模块与 Android。
- Ant:纯脚本、自由度最高但无约定无依赖管理,已基本淘汰,遇到老项目才见。
- 选型依据:团队技能、构建时长、生态插件。面试答“团队统一 + 生态成熟”即可。
1-4 Spring MVC 请求流程(必背)
- 请求到达DispatcherServlet(Spring MVC 的核心前端控制器);
- DispatcherServlet 通过HandlerMapping找到处理该请求的 Controller 方法(Handler);
- 通过HandlerAdapter执行 Handler,期间会经过HandlerInterceptor(拦截器)链;
- Controller 处理业务,返回数据;
- 结果交给HttpMessageConverter序列化为 JSON(@ResponseBody / @RestController 场景),或ViewResolver渲染页面;
- 响应返回客户端。
补充:Spring Boot 自动装配了 DispatcherServlet、内嵌容器(Tomcat)、Jackson 消息转换器等,开发者无需手动配置 XML。
1-5 两级缓存(Caffeine + Redis)方案
- 为什么用两级:本地缓存(Caffeine)延迟最低、无网络开销,适合扛热点;Redis 是分布式共享缓存,适合集群一致与兜底。
- 读取顺序:Caffeine(本地)→ Redis → MySQL,命中即返回并回填上级。
- 常见考点:本地缓存的“一致性问题”(每台机器缓存可能不同步,需要发布/定时刷新);缓存更新采用Cache Aside(旁路缓存):先更新 DB,再删缓存。
第二轮详解:并发关
2-1 缓存穿透
- 定义:查询一个根本不存在的数据,缓存永远不命中,请求每次都打到数据库,DB 被打爆。
- 解决方案:
- 缓存空值:即使查不到也把空值/占位符缓存(TTL 设短,如 60s);
- 布隆过滤器(Bloom Filter):用位数组 + 多个哈希函数预判 key 是否存在。注意它有误判率(说不存在的一定不存在,说存在的不一定存在),可通 过位数组大小与哈希函数个数调节误判率;误判的请求仍会穿透到 DB,所以常与空值缓存组合;
- 接口层参数校验(如商品 ID 格式合法性)。
2-2 缓存雪崩 & 缓存击穿
- 雪崩:大量 key同一时刻集中过期,或 Redis 整体宕机 → 海量请求瞬间打到 DB。
- 解决:过期时间加随机值打散;多级缓存(本地缓存兜底);Redis高可用(主从 + 哨兵 / Cluster);网关限流 + 熔断降级;逻辑过期(不设物理 TTL,后台异步重建)。
- 击穿(与雪崩区分):某个热点 key恰好过期,一瞬间大量并发打 DB。
- 解决:互斥锁(只让一个线程回源 DB,其余等待/快速失败);逻辑过期。
2-3 秒杀不超卖 & Redis 原子性
- 方案:把库存扣减前置到 Redis,用Lua 脚本保证“判断库存 + 扣减”两步原子执行。
- 示例:
if redis.call('get', key) > 0 then redis.call('decr', key) return 1 else return 0 end
- 示例:
- 为什么原子:Redis执行命令是单线程的,而Lua 脚本执行期间不会被其他命令插入,所以天然原子(Redis 6.0 的多线程仅针对网络 I/O 与持久化,命令执行仍是单线程)。
- 最终落库:扣减成功的请求放行下单,订单异步通过 MQ 同步库存扣减到 MySQL,保证最终一致。
- 分布式锁:跨进程互斥可用Redisson,底层
SET key value NX PX原子写 +watchdog(看门狗)自动续期,防止业务未执行完锁就过期;释放用 Lua 校验持有者,防止误删他人锁。
2-4 Kafka 异步解耦与消息可靠性
- 业务场景:下单后积分、券、物流是多系统的旁路动作,不应阻塞主链路 → 用 Kafka 解耦削峰。
- 消息不丢三端保障:
- 生产端:
acks=all(等待所有副本确认)+ 失败重试,保证消息落到 broker; - Broker 端:副本数 replication.factor>1、
min.insync.replicas保证至少一个同步副本; - 消费端:手动提交 offset(处理成功后再 commit),避免自动提交导致“处理失败但 offset 已提交”而丢消息。
- 生产端:
- 重复消费问题:手动提交 offset 后,若服务在提交前崩溃,重启会重复消费→ 必须做消费幂等:用唯一业务 ID(订单号)配合 Redis SETNX / 数据库唯一索引去重。
- 面试加分:能讲清“至少一次(At-least-once)投递语义下必须配合幂等消费”才算真的懂。
2-5 分布式事务
- 场景:跨服务/跨库操作(扣库存、扣余额、生成订单)无法用单库事务保证原子性。
- 主流方案:
- 2PC / XA:数据库层面两阶段提交(prepare → commit/rollback)。优点:强一致;缺点:同步阻塞、协调者单点、性能差,电商高并发很少用。
- Seata AT 模式:业务无侵入,框架记录前后镜像 + undo_log,出现异常自动回滚,靠全局锁避免脏写。优点:侵入小;缺点:有额外开销与全局锁,并发性能受损。
- TCC(Try-Confirm-Cancel):业务侵入强,要手写三个方法(预留资源→确认→取消);需处理空回滚(Try 未执行却收到 Cancel)与悬挂问题;性能好,适合强一致核心资金场景。
- 本地消息表 + MQ 最终一致性:本地事务里写业务 + 消息表,异步投递 MQ,下游消费 + 幂等 + 定时对账补偿。适合弱一致性、可容忍短时延迟的场景,也是电商下单的常见落地方案。
- Saga 编排/协同:长事务拆多个子事务 + 补偿,适合业务流程长、无强一致要求的场景。
面试送分记忆点:能接受最终一致 → MQ + 本地消息表;核心强一致 + 能接受改代码 → TCC;不想改业务代码 → Seata AT。
第三轮详解:架构关
3-1 微服务注册发现、负载均衡与熔断降级
- 注册中心:服务启动注册自身实例,客户端拉取/订阅服务列表。
- Eureka:AP 风格(优先可用性,允许暂时读到过期实例),客户端自带缓存、去中心化互相注册;
- Nacos:注册中心可切 AP/CP(临时实例走 AP,持久实例走 CP),同时兼具配置中心;
- Consul:CP 风格,基于 Raft 强一致。
- CAP 取舍:注册中心一般更看重可用性(AP),因为读到过期实例可重试;配置中心需要一致性(CP),避免读到不同配置。
- 调用与负载均衡:OpenFeign(声明式 HTTP)+ LoadBalancer/Ribbon 完成服务间调用与轮询/加权负载。
- 故障防护(核心):不加防护时,下游超时会占用线程、不断重试,最终线程池耗尽 → 雪崩。
- Resilience4j / Sentinel提供:超时限制 → 重试策略 → 熔断(失败率阈值触发,快速失败)→ 降级(fallback 返回兜底数据)→ 限流。
- 老牌 Netflix OSS(Eureka、Zuul、Hystrix、Ribbon)多为历史项目;新项目常见 Nacos + Gateway + OpenFeign + Sentinel/Resilience4j。
3-2 认证鉴权:JWT / OAuth2 / Session
- Session 方案:服务端存储会话,天然可主动踢人;但分布式下要Session 共享(存 Redis),有状态、扩容与跨域不便。
- JWT 方案:
- 结构:
Header.Payload.Signature(头部算法、载荷、签名); - 无状态,服务端不存会话 → 便于水平扩展、适合微服务网关统一校验;
- 痛点:服务端无法主动撤回,泄露只能等过期;解决:缩短 access token 有效期 +refresh token 刷新;
- 算法注意:用 RS256(非对称)别用 HS256 裸密钥;
secret不要硬编码,防止 JWT 伪造。
- 结构:
- OAuth2:第三方授权协议,核心流程Authorization Code(授权码):用户授权 → 回调带 code → 客户端用 code + client_secret 换 token → 之后带 token 访问资源。
- Keycloak是可开箱即用的开源身份认证服务器,提供 OIDC/OAuth2、统一登录、用户管理,很多中大型团队直接部署它做统一认证中心。
- 风控/防刷:网关令牌桶/滑动窗口限流(Redis 计数)、验证码/滑块、设备指纹、IP 频次统计、黑名单,把脚本流量挡在业务之外。
3-3 监控、日志与链路追踪
- 指标监控:Micrometer是指标门面(类似日志里的 SLF4J),暴露 JVM、HTTP、DB 等指标 → 由Prometheus抓取存储 →Grafana可视化告警。
- 链路追踪:Zipkin / Jaeger(基于 OpenTelemetry/Sleuth)为每个请求生成全局traceId+ span,把一次请求跨多个服务的调用树串联起来,准确定位“慢在哪个服务哪一步”。
- 日志链路:SLF4J(门面)+Logback/Log4j2(实现);大促排查用ELK:Beanstalkd……不,Filebeat 采集 → Logstash 传输过滤 → Elasticsearch 存储检索 → Kibana 可视化,配合 traceId 全文检索串联一次请求的所有日志。
- 面试金句:指标(Prometheus)、链路(Jaeger/Zipkin)、日志(ELK)三把斧配合,才能在大促快速定位问题。
3-4 Docker 与 Kubernetes 部署
- Docker:Dockerfile 多阶段构建 → 精简基础镜像(如
eclipse-temurin:11-jre)→ 打成不可变镜像推送到 Harbor/阿里云 ACR。 - CI/CD:GitLab CI / GitHub Actions / Jenkins,触发链路:代码 push → 单元测试 → 构建 → 推送镜像 → 更新 K8s(ArgoCD/Rollout 更现代)。
- K8s 核心概念:
- Pod:最小调度单元;Deployment:无状态应用,管理 ReplicaSet 并维护期望副本数,Pod 挂了由控制器自动拉起;
- StatefulSet:给每个 Pod 稳定标识(有序命名)、稳定网络(Headless Service)、稳定存储(PVC),适合有状态服务(如 ZK、ES);
- Service:稳定访问入口与负载均衡;Ingress:七层网关入口;
- HPA:按 CPU/内存/自定义指标自动扩缩容;**滚动更新 + 探针(readiness/liveness)**保证发布不中断。
- 有状态中间件部署策略(重点):MySQL、Redis、Kafka 这类有状态、对 IO/存储高敏感的服务,生产环境多数团队优先使用云 RDS / 云 Redis / 自建集群,而非硬塞进 K8s 用 StatefulSet,因为本地盘/网络盘性能、故障恢复、备份运维复杂度高。答出这一点,是区分“背概念”与“有实战”的关键。
面试复盘:给准备去大厂的小白
- 广度有了,深度才是分水岭:谢飞机能说出所有名词,却讲不清 AT 模式的回滚机制、TCC 的空回滚与悬挂、Redis Lua 的原子原理,这正是面试被刷的核心原因。
- 要能把“面试题”还原到“业务场景”:缓存穿透对应“查不存在的商品”、雪崩对应“大促缓存集体过期”、消息可靠性对应“下单后积分消费不丢”——脱离场景背八股,一问就穿帮。
- 技术选型要会说“为什么”:为什么 Java 11?为什么 Maven?为什么 Nacos AP?给出“业务 + 团队 + 成本”的理由,才像有经验的工程师。
- 遇到不会的,别硬堆名词:可以坦诚说“这块我了解 xxx 概念,但细节需要回去补”,再主动把话题引向自己擅长的部分。
祝每一位“谢飞机”都能在下次面试前,把故事里所有的“……”变成真正的“我会”。
(全文完)