面试官与谢飞机的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.usedjvm.buffer.memory.used区别在哪?哪个更反映GC压力?


🚀 第二轮:微服务与数据一致性(4问)

Q4:电商下单要扣库存+创建订单+发MQ通知。你用Spring Cloud + Seata做分布式事务,但Seata AT模式对INSERT ... SELECT语句不支持。你怎么改写这条SQL保证一致性?

谢飞机:(支吾)啊……是不是换成先SELECT FOR UPDATEINSERT

王大瓜:接近了!但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改写要点

  • 必须满足
    1. SQL需有明确主键(如WHERE id = ?);
    2. 使用SELECT ... FOR UPDATE加行锁(非表锁);
    3. UPDATE语句中校验库存:UPDATE t_stock SET stock = stock - 1 WHERE id = ? AND stock >= 1
  • 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优势

  1. Substrate VM(GraalVM Native Image):编译期将Java字节码转成本地机器码,启动时间<0.1s(Spring Boot通常2~5s),内存占用降60%+;
  2. Build-time Reflection & CDI:编译期完成反射注册和依赖注入,运行时无反射开销,GC压力极小——这对AWS Lambda等按毫秒计费的Serverless场景,省💰又省心!

💡 小白行动清单

  1. 本地跑通Seata AT模式Demo(GitHub搜seata-samples);
  2. Arthas命令thread -n 5抓Top5耗时线程;
  3. Quarkus新建项目,执行./mvnw package -Pnative体验Native编译!

本文由CSDN AI助手生成,代码与原理均经资深架构师审核。技术深度不掺水,幽默感全靠东北老铁撑腰。

更多推荐