在线咨询 400-826-1668
回到顶部
ARTICLE DETAIL

资讯详情

深耕国风建站与运营引流的一线实战洞察。

大厂Java面试:Spring Boot自动配置、MySQL索引、Redis扣库存、Kafka消息不丢失、微服务治理与云原生监控——谢飞机智斗铁面面试官

大厂Java面试:Spring Boot自动配置、MySQL索引、Redis扣库存、Kafka消息不丢失、微服务治理与云原生监控——谢飞机智斗铁面面试官 大厂Java面试Spring Boot自动配置、MySQL索引、Redis扣库存、Kafka消息不丢失、微服务治理与云原生监控——谢飞机智斗铁面面试官一家互联网大厂的面试间白炽灯亮得有些刺眼。面试官表情严肃翻开面前写着“精通Java”的简历又抬头看了看对面的求职者——谢飞机。谢飞机穿着格子衬衫头发有点乱正努力挤出职业微笑。面试官“谢飞机是吧既然是Java岗位我们直接开始。假设你在做电商下单业务服务端用Spring Boot。第一个问题Spring Boot的自动配置原理是什么它和传统Spring有什么区别”谢飞机“这个我会Spring Boot用EnableAutoConfiguration通过META-INF/spring.factories加载配置类还有Conditional条件注解实现按需加载。比传统Spring省了XML配置约定大于配置”面试官微微点头“不错基础还算扎实。那第二个问题项目里用Maven还是Gradle说说两者的区别。”谢飞机“我们组用Maven因为公司老项目多。Maven用pom.xmlGradle用Groovy或Kotlin DSL。Gradle构建快支持增量构建但学习成本高。”面试官“好的。第三个问题用户下单后要打印系统日志如果让你从Logback和Log4j2里选一个你怎么选”谢飞机“我选SLF4JLogback因为Spring Boot默认就是Logback简单不用配。Log4j2性能更高但要用Disruptor有点复杂。”面试官“能分清门面和实现第一轮先这样。下面我们深入业务。”面试官“订单数据最终要存MySQL假设订单表有几千万数据。请说说哪些情况下会导致索引失效”谢飞机“唔查询条件里用了函数、隐式类型转换、左模糊还有OR连接的时候如果有一个条件没索引整个可能不走索引。还有在索引列上做运算也会失效。”面试官“说得不错。那为了避免超卖你们用Redis保存库存。假如一个商品库存有10件现在10000人同时抢购你会怎么设计扣库存方案”谢飞机“这个……我会用Redis的decr命令但是要判断不能变负数。可以用Lua脚本原子操作。呃好像还得加分布式锁反正不能先查再减。”面试官“你还知道Lua脚本但临界条件和细节你说清楚了吗那订单创建后要发Kafka消息通知下游怎么保证消息不丢失”谢飞机“啊Kafka不丢失需要设置acksall生产者重试消费者关闭自动提交手动提交offset……嗯……还要注意重平衡导致重复消费。但保证不丢失很难吧唉我有点乱。”面试官“能说出部分但整体不够清晰。我们继续下一个话题。”面试官“服务多了以后需要服务治理。你们用Spring Cloud服务注册中心选什么”谢飞机“选过Eureka和Consul。Eureka是AP用在不需要严格一致的场景Consul是CP数据一致性更好还带KV存储。我们后来用的Nacos呃不过您没问。”面试官“好。那还有安全。请说一下JWT和OAuth2到底什么关系”谢飞机“JWT是一种token格式OAuth2是授权框架。JWT可以当OAuth2的access token用。比如登录后签发JWT请求时带着然后Spring Security过滤器解析。”面试官“那如果服务部署在Kubernetes里节点要升级需要把Pod优雅地排空。你会怎么给Spring Boot应用做优雅停机直接kill -9行不行”谢飞机“直接kill -9肯定不行数据会丢。优雅停机要……Spring Boot 2.3之后有graceful shutdown参数然后还要在K8s里配preStop钩子让Pod从Endpoints摘掉再sleep几秒再退出。但具体怎么配合我有点模糊……”面试官“最后一个问题你打算怎么监控这套微服务”谢飞机“用Actuator暴露端点Micrometer收集指标Prometheus拉取然后Grafana画图。还可以用Zipkin/Jaeger做链路追踪。”面试官“嗯这个你倒是背得挺熟。好了三轮面试结束你先回去等通知吧。”谢飞机“啊这就结束了那……我是不是没戏了”面试官“回去等通知我们会综合评估。”谢飞机垂头丧气地站起来“好吧我回去等‘感谢信’了。”面试问题答案解析以下是本次面试中涉及的技术场景与问题详解结合电商下单、库存扣减、消息通知、微服务治理等业务场景适合Java小白系统学习。1. Spring Boot自动配置原理是什么与传统Spring有什么区别答案Spring Boot 的核心是“自动配置”。SpringBootApplication注解包含了EnableAutoConfiguration这个注解通过Import(AutoConfigurationImportSelector.class)去读取META-INF/spring.factoriesSpring Boot 2.7 之前或META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.importsSpring Boot 2.7 之后文件中的配置类全名。然后Spring Boot 会结合ConditionalOnClass、ConditionalOnMissingBean、ConditionalOnProperty等条件注解判断当前类路径下是否存在对应的依赖以及用户是否已经手动配置过。只有满足条件时自动配置类才会生效。业务场景你在电商订单服务中引入spring-boot-starter-webSpring Boot 会自动配置 DispatcherServlet、Tomcat、Jackson、ViewResolver 等。如果你自己定义了一个RestControllerSpring Boot 就不会再自动装配一个默认的DispatcherServlet这就是“约定优于配置”。传统 Spring 需要手动在 XML 中配置组件扫描、数据源、事务管理器等十分繁琐。Spring Boot 把这些重复劳动通过自动配置消除了。2. Maven 和 Gradle 的区别是什么答案Maven基于 XML 的pom.xml描述项目依赖和构建配置。有固定的生命周期clean、default、site构建过程相对标准依赖管理约定俗成。优点生态庞大大部分公司都在用缺点XML 冗长构建速度较慢增量构建能力弱。Gradle基于 Groovy 或 Kotlin DSL构建脚本是编程式的更加灵活。支持增量构建、构建缓存、并行执行、按需构建所以大型项目构建速度更快。Android 官方默认使用 Gradle。缺点学习成本高构建脚本灵活也意味着容易写乱。业务场景大厂里很多老系统用 Maven因为维护成本低新项目或对构建速度有要求的项目可能选 Gradle。面试时能说出“Gradle 用 DAG有向无环图组织任务Maven 用线性生命周期”会显得更加专业。3. Logback 和 Log4j2 怎么选SLF4J 是什么答案SLF4J 是日志门面Facade它本身不输出日志只提供统一接口。Logback 和 Log4j2 是具体实现。使用slf4j-api 实现框架可以在不修改业务代码的情况下切换底层日志引擎。LogbackSpring Boot 默认使用 Logback。配置简单支持自动重载。性能足够应对大多数业务但异步日志能力弱于 Log4j2。Log4j2在异步日志方面非常强基于 LMAX Disruptor 实现无锁异步记录吞吐量高也支持无垃圾Garbage-free日志。但配置复杂需要额外引入依赖。业务场景电商下单的高并发场景下如果每笔订单打日志可能造成 IO 瓶颈。此时可以使用 Log4j2 的异步日志或 Logback 的 AsyncAppender。但要注意日志框架影响的是系统稳定性不能因为引入复杂依赖导致启动问题。所以默认推荐 Logback遇到性能瓶颈再换 Log4j2。4. MySQL 索引失效的常见场景有哪些答案在基于 MySQL 的订单表查询中索引失效意味着全表扫描性能急剧下降。常见导致索引失效的情况如下对索引列使用函数WHERE DATE(create_time) 2025-01-01会导致create_time索引失效。应该改为WHERE create_time 2025-01-01 00:00:00 AND create_time 2025-01-02。隐式类型转换例如phone是 varchar 类型查询时用WHERE phone 13800001111数字会被转换为字符串可能导致索引失效。LIKE 以通配符开头WHERE name LIKE %张无法使用普通 BTree 索引。可以改为LIKE 张%。OR 连接条件WHERE status 1 OR price 100如果只有status有索引而price没有整个查询可能会走全表扫描。可以把 OR 改成 UNION 或为price加索引。索引列参与运算WHERE price 100 200不会用索引应把运算移到赋值侧。不等值查询WHERE status ! 1优化器可能会放弃索引。联合索引不满足最左前缀原则联合索引(a, b, c)如果直接查b或c条件索引失效。优化器判断全表扫描更快小表或数据显示极小比例时MySQL 可能故意不走索引。业务场景订单查询经常按user_id和create_time建立联合索引要避免在create_time上套 DATE 函数否则千万级订单表会被打垮。5. Redis 如何设计库存扣减方案来避免超卖答案可以用 Redis 的DECR命令但需要配合 Lua 脚本保证原子性。因为多个并发请求“先读后写”会出现资源竞争导致超卖。一个基本的 Lua 脚本思路local stock tonumber(redis.call(GET, KEYS[1])) if stock nil then return -1 -- 表示库存不存在 end if stock 0 then return 0 -- 表示已被买完 end redis.call(DECR, KEYS[1]) return 1 -- 扣减成功为什么用 Lua因为 Redis 的 Lua 脚本是原子执行的整个脚本不会中途插入其他命令天然避免了并发扣减的竞态条件。更完善的方案使用 Hash 结构存储库存和已售数量方便管理。扣减成功后通过消息队列发异步消息由订单服务持久化到数据库。数据库最终使用乐观锁UPDATE stock SET LEFT LEFT - 1 WHERE GOODS_ID ? AND LEFT 1兜底。也可以用 Redisson 分布式锁但锁会降低吞吐不如 Lua 脚本高效。业务场景秒杀活动中10000 个用户同时抢 10 件商品Redis 单线程加 Lua 脚本可以在毫秒级完成原子扣减而不会出现超卖。6. Kafka 如何保证消息不丢失答案Kafka 保证不丢失需要从生产端、Broker 端、消费端三个层面配合。生产端设置acksall或-1Producer 发送消息后需要等待 ISR 中所有副本都写入成功才返回成功。设置retries 0网络抖动时自动重试。开启幂等enable.idempotencetrue避免重试导致消息重复。Broker 端设置min.insync.replicas2假设副本书为 3确保至少有一个 Follower 和 Leader 保持同步。如果同步副本数低于该值Broker 会拒绝写入。unclean.leader.election.enablefalse不允许落后过多的副本成为 Leader避免丢已提交消息。消费端关闭自动提交enable.auto.commitfalse。在业务处理成功后再手动提交 offset。如果处理失败不提交 offset消息会被重复消费此时需要保证消费幂等。业务场景订单服务产生“订单创建成功”事件需要发给积分服务、短信服务。如果消息丢失用户可能不收到积分或短信。使用上述配置可以最大限度保证消息不丢但还可能出现重复消费因此消费者要做幂等。7. Spring Cloud 服务注册中心Eureka 和 Consul 有什么区别答案EurekaNetflix OSSAP 模型优先保证可用性容忍节点间数据不一致。服务实例启动时向 Eureka 注册其他服务通过拉取注册表发现服务。Eureka 通过心跳保活但服务列表可能出现短暂不一致。Eureka 2.0 已停止开发Spring Cloud Netflix 也进入维护模式。ConsulCP 模型基于 Raft 协议保证强一致性。当发生网络分区时为了一致性可能牺牲可用性。Consul 内置健康检查、KV 存储、多数据中心能力功能比 Eureka 更丰富。Nacos国内常用的服务注册中心既能支持 AP 也能支持 CP还提供动态配置管理在很多企业实际项目中替代了 Eureka 和 Consul。业务场景如果你希望服务发现很快且能容忍注册中心之间短暂不一致Eureka 合适如果对数据一致性要求很高比如金融转账服务可能要求跨节点注册信息完全一致那 Consul 更好。但实际大厂更多使用 Nacos 或自研注册中心。8. JWT 和 OAuth2 到底什么关系答案JWTJSON Web Token是一种 token 格式分为 Header、Payload、Signature 三部分。JWT 自包含用户信息服务端无状态验证适合认证场景。OAuth2是一种授权框架解决“第三方应用如何获得用户授权去访问资源”的问题。有四种授权模式授权码、密码、客户端凭证、简化模式已弃用。它不规定 token 必须是 JWT。关系JWT 可以作为 OAuth2 的 access token。比如客户端向认证服务器申请授权认证服务器签发一个 JWT客户端拿着 JWT 去访问资源服务器资源服务器验证签名后放行。业务场景电商系统的 Web 端登录可以是“用户名密码换 JWT”Spring Security 过滤器解析 JWT 并设置 SecurityContext。如果要用微信登录就是 OAuth2 授权码模式前端跳到微信授权页微信回调 code 后后端用 code 换取 access_token 和用户信息再生成自己的 JWT。所以两者可以配合使用。注意JWT 无法主动失效除非引入黑名单或短过期时间。如果发生用户注销需要等 JWT 过期或维护 Redis 黑名单。9. Kubernetes 中如何实现 Spring Boot 应用优雅停机答案Kubernetes 在结束 Pod 时默认向容器中的 1 号进程发送 SIGTERM 信号然后等待terminationGracePeriodSeconds默认 30 秒超时后发送 SIGKILL。如果应用不处理 SIGTERM进程会被强杀正在处理的请求会中断数据可能丢失。Spring Boot 2.3 以后支持优雅停机配置server.shutdowngraceful spring.lifecycle.timeout-per-shutdown-phase30s这样应用接收 SIGTERM 后会等待处理中请求完成再退出并停止接收新请求。但只配置这个还不够。K8s 中 Service 的 Endpoints 列表更新不是瞬间的如果 Pod 已经退出但流量还在转发还是会断。因此需要配置readinessProbe就绪探针让应用在停止接收流量时返回失败状态。Spring Boot 2.6 可以使用liveness和readiness状态组通过/actuator/health/readiness指示是否 ready。配置preStop 钩子在 SIGTERM 之前执行sleep例如lifecycle: preStop: exec: command: [sh, -c, sleep 10]这 10 秒用于让 Pod 从 Service Endpoints 中移除并把正在处理的请求完成。设置terminationGracePeriodSeconds大于应用优雅停机所需的超时时间。业务场景电商大促后的服务发版如果直接 kill -9会导致大量正在提交的订单丢失。正确做法是先摘流量再等存量请求完成最后进程退出。K8s 的 rolling update 会自动执行这个流程前提是应用必须支持优雅停机。10. 如何监控 Spring Boot 微服务答案主流监控方案是Micrometer Spring Boot Actuator Prometheus Grafana。Micrometer是指标门面类似 MyBatis 的 API 封装。它提供 Counter、Timer、Gauge、Histogram 等指标类型。Spring Boot Actuator暴露端点和指标。引入spring-boot-starter-actuator后可以访问/actuator/health检查健康/actuator/metrics查看 JVM、线程、数据库连接池等指标。引入micrometer-registry-prometheus后Actuator 会暴露/actuator/prometheus端点输出 Prometheus 格式的指标。Prometheus通过配置scrape定时拉取该端点存储到时间序列数据库。Grafana连接 Prometheus将指标可视化还可以设置告警。业务场景在订单微服务中可以自定义一个 Counter 记录“订单创建数”一个 Timer 记录“下单接口耗时”。监控平台发现下单耗时突增或订单量归零时马上告警。分布式链路追踪可以用 Jaeger 或 Zipkin方法是在服务间传递 traceId把一次请求经过的所有服务串成链路定位性能瓶颈。常见配套日志监控Filebeat 收集日志 → Logstash 解析 → Elasticsearch 存储 → Kibana 展示ELK。容器监控Kubelet 暴露容器指标Prometheus 拉取可以监控 CPU、内存。告警Prometheus AlertManager 发送钉钉或邮件。掌握这套方案后即使服务很多也能通过“指标 日志 链路”三维度快速定位问题。谢飞机最终没有得到“通过”的通知但这份面试题和他的“半吊子”回答恰好成了 Java 学习路上的反面教材。如果看到这里的你能把这 10 个问题认真吃透下次面对大厂面试官至少会比谢飞机多一分底气。
返回列表