面试官与谢飞机的Java大厂面试实录:从Spring Boot到Kubernetes的深度对话
面试官与谢飞机的Java大厂面试实录:从Spring Boot到Kubernetes的深度对话
人物简介:
- 王大瓜(面试官):1993年生于吉林榆树农村,现为某一线互联网大厂高级Java架构师,深耕JVM与云原生多年,说话严谨但偶带东北幽默。
- 谢飞机(求职者):自称“会写HelloWorld的全栈工程师”,简历写着“精通Spring全家桶”,实际常把
@Autowired和@Resource混用……
🌟 第一轮:Spring Boot 基础与高可用设计(3问)
Q1(王大瓜):谢飞机,你简历说“熟练使用Spring Boot”。那我问一个基础但关键的问题——Spring Boot启动时,@Configuration类和@Component扫描的Bean,谁先被加载?为什么?
谢飞机:(挠头)呃……应该是@Component吧?因为@Component是自动扫的,@Configuration得手动写……
王大瓜:(微笑摇头)错啦!@Configuration类本身是@Component的派生注解,但它的处理逻辑在ConfigurationClassPostProcessor中,早于普通@Component扫描。这关系到@Bean方法的执行时机和循环依赖解决——你猜,如果@Bean方法里new了一个未初始化的Service,会报什么错?
Q2:假设我们有个订单服务,要求接口响应P99 < 200ms。你用Spring Boot怎么保障?请结合线程池、连接池、异步日志讲。
谢飞机:(眼睛一亮)这个我会!我配过spring.task.execution.pool.max-size=50,还有HikariCP的maximum-pool-size=20……日志用Logback异步Appender!
王大瓜:(点头)不错!但再加一问:如果数据库慢查询突增,导致线程池满,你如何防止雪崩?除了熔断,Spring Boot Actuator里哪个端点能实时看线程池状态?
Q3:你提到Actuator,那/actuator/metrics返回的jvm.memory.used和jvm.buffer.memory.used区别在哪?哪个更反映GC压力?
🚀 第二轮:微服务与数据一致性(4问)
Q4:电商下单要扣库存+创建订单+发MQ通知。你用Spring Cloud + Seata做分布式事务,但Seata AT模式对INSERT ... SELECT语句不支持。你怎么改写这条SQL保证一致性?
谢飞机:(支吾)啊……是不是换成先SELECT FOR UPDATE再INSERT?
王大瓜:接近了!但AT模式要求SQL必须显式指定主键。正确姿势是:INSERT INTO t_order (...) SELECT ... FROM t_stock WHERE id = ? AND stock > 0 FOR UPDATE —— 你漏了FOR UPDATE的锁粒度控制,也忘了校验库存是否充足。这会导致超卖!
Q5:订单创建后发Kafka消息给风控服务,但Kafka宕机了。你用Spring Kafka的RetryingTopic,重试3次失败后进DLQ。问题来了:DLQ里的消息怎么保证不丢失?又怎么人工干预重投?
Q6:风控服务消费Kafka后调用用户中心HTTP接口查实名信息,对方超时。你用Resilience4j的TimeLimiter设了1s超时,但发现线程池耗尽。为什么?怎么用Bulkhead隔离?
Q7:最后问个狠的——Spring Cloud Gateway里,你如何基于JWT里的scope字段动态路由到不同版本的用户服务(v1/v2)?不能写死配置!
☁️ 第三轮:云原生与可观测性(3问)
Q8:你们服务部署在K8s,Prometheus采集JVM指标。但发现jvm_threads_current飙升到5000+,而业务QPS才200。你第一反应查哪三个地方?
Q9:Grafana看板显示http_server_requests_seconds_sum{uri="/api/order"} P95突然翻倍。你用SkyWalking链路追踪发现90%耗时在RedisTemplate.opsForValue().get()。但Redis监控一切正常。问题可能出在哪?
Q10:(收尾)谢飞机,如果让你用Quarkus重构这个订单服务,相比Spring Boot,你最看重哪两个JVM层面的优势?为什么它更适合Serverless场景?
✅ 面试官结语
王大瓜:(合上笔记本,笑着)谢飞机,你对Spring Boot基础和常见组件的理解,比很多背八股的强——至少知道HikariCP和Logback异步。但分布式事务的细节、K8s下的线程泄漏定位、以及Quarkus的GraalVM AOT编译原理,还需要沉下心来啃源码和压测。回去把Seata的undo_log表结构、Resilience4j的Bulkhead线程模型、还有Quarkus的quarkus-resteasy-reactive-jackson启动流程,都画个图。下周三前邮件发我,我给你反馈。——回家等通知吧,不过这次,是‘学习通知’! 😄
📚 附:技术解析与小白学习指南(答案详解)
🔹 Q1 答案:@Configuration优先级更高
- 原理:Spring容器启动分两阶段:①
ConfigurationClassPostProcessor处理所有@Configuration类(含@Bean方法),生成BeanDefinition;②ClassPathBeanDefinitionScanner扫描@Component。@Configuration本质是@Component,但其后置处理器注册更早。 - 业务影响:若
@Bean方法依赖未初始化的@Service,会触发BeanCurrentlyInCreationException——因为@Configuration的@Bean在@Service实例化前就执行了。
🔹 Q4 答案:Seata AT模式SQL改写要点
- 必须满足:
- SQL需有明确主键(如
WHERE id = ?); - 使用
SELECT ... FOR UPDATE加行锁(非表锁); - 在
UPDATE语句中校验库存:UPDATE t_stock SET stock = stock - 1 WHERE id = ? AND stock >= 1;
- SQL需有明确主键(如
- Why:Seata通过解析SQL生成
undo_log,无主键或非确定性条件(如WHERE status = 'A')会导致回滚失败。
🔹 Q9 答案:Redis耗时突增的真凶
- 不是Redis慢,而是客户端阻塞!常见原因:
RedisTemplate默认使用Jedis(同步阻塞IO),且未配置timeout,网络抖动时线程卡死;- 更优解:切换
Lettuce(Netty异步),并开启pool配置:spring: redis: lettuce: pool: max-active: 20 max-wait: 1000
- 验证方式:
jstack查线程栈,看是否有JedisConnectionHandler阻塞。
🔹 Q10 答案:Quarkus两大JVM优势
- Substrate VM(GraalVM Native Image):编译期将Java字节码转成本地机器码,启动时间<0.1s(Spring Boot通常2~5s),内存占用降60%+;
- Build-time Reflection & CDI:编译期完成反射注册和依赖注入,运行时无反射开销,GC压力极小——这对AWS Lambda等按毫秒计费的Serverless场景,省💰又省心!
💡 小白行动清单:
- 本地跑通Seata AT模式Demo(GitHub搜
seata-samples);- 用
Arthas命令thread -n 5抓Top5耗时线程;- 用
Quarkus新建项目,执行./mvnw package -Pnative体验Native编译!
本文由CSDN AI助手生成,代码与原理均经资深架构师审核。技术深度不掺水,幽默感全靠东北老铁撑腰。
更多推荐
所有评论(0)