互联网大厂Java面试实录:从JVM调优到微服务架构的实战演练
互联网大厂Java面试实录:从JVM调优到微服务架构的实战演练
场景介绍
某互联网大厂电商业务线面试现场,面试官老张(资深架构师)与候选人谢飞机(自称"全栈开发"的水货程序员)的对话实录。
第一轮:基础技术考察
老张:你好,请简单介绍一下你自己。
谢飞机:您好!我叫谢飞机,毕业三年,主要做Java开发,对Spring Boot、MySQL这些都很熟悉,平时喜欢研究新技术,像什么Kafka、Redis、Docker都有接触过...
老张:好的,那我们开始吧。第一个问题,在电商大促场景下,如果JVM堆内存突然飙升,你会如何排查?
谢飞机:嗯...这个嘛,我记得可以用jstat命令看看GC情况,还有jmap可以dump内存,然后MAT分析工具看大对象...应该是这样吧。
老张:思路是对的。那具体说说G1垃圾收集器和CMS有什么区别呢?
谢飞机:呃...G1好像是新一代的收集器,CMS是旧版的?它们都用来处理老年代...具体参数不太记得了。
老张:(微笑)至少方向对了。那你说说,电商订单系统里,为什么常用ThreadLocal存储用户信息?
谢飞机:这个我知道!ThreadLocal每个线程独立存储空间,避免线程安全问题,就像存用户ID一样很方便...
老张:不错!继续。如果一个订单创建接口响应时间从200ms变成2s,你的排查思路是什么?
谢飞机:先看日志有没有异常,然后查数据库慢查询,再看网络延迟...可能还要检查Redis连接池?
老张:很好,逻辑清晰。那DB和Redis数据不一致怎么处理?
谢飞机:这个...可以用延时双删策略?或者消息队列保证最终一致性?记不太清具体怎么实现了...
老张:(点头)有概念就行。好,我们进入下一轮。
第二轮:微服务与架构设计
老张:现在电商系统通常采用微服务架构,你理解Spring Cloud的核心组件有哪些?
谢飞机:Eureka做注册中心,Feign做远程调用,Hystrix熔断,Gateway做网关...还有配置中心Config。
老张:很好!那如果服务A调用服务B出现雪崩效应,你怎么解决?
谢飞机:用熔断降级,比如Resilience4j或者Sentinel,设置超时时间和失败次数阈值...
老张:不错。那分布式事务怎么处理?比如下单扣减库存和积分的场景。
谢飞机:TCC模式?或者Seata的AT模式?反正就是保证多个服务的数据一致性...
老张:(挑眉)具体说说Seata AT模式的原理。
谢飞机:呃...好像是有全局锁和回滚日志?提交前做undo_log记录,失败了就回滚...
老张:大体正确。那消息队列Kafka在电商中有什么应用场景?
谢飞机:订单创建后发消息通知库存系统,还有用户行为数据采集,解耦各个服务...
老张:对的。那如何保证消息不丢失?
谢飞机:开启ACK机制,持久化消息,消费者手动确认...
老张:很好。最后一个,微服务链路追踪怎么做?
谢飞机:SkyWalking或者Zipkin,加Trace ID,把请求头传递下去...
老张:回答得不错。进入最后一轮。
第三轮:深度场景题
老张:假设我们要做一个秒杀活动,高并发场景,你如何设计整个流程?
谢飞机:先在Redis预扣库存,然后用MQ异步下单,限流防刷,数据库最后落盘...
老张:具体说说Redis预扣库存的实现细节。
谢飞机:用Lua脚本保证原子性,key的设计要唯一,过期时间设置合理...
老张:那如果Redis挂了怎么办?
谢飞机:集群部署?主从备份?或者本地缓存兜底?
老张:(笑)这问题有点深啊。再问你一个,支付系统的幂等性怎么保证?
谢飞机:唯一流水号,数据库唯一索引,或者Redis去重键...
老张:很好。最后问个复杂的,如何设计一个支持千万级用户的推荐系统?
谢飞机:这个...可能要结合大数据平台,Flink做实时计算,Elasticsearch做检索,机器学习模型推荐...
老张:(露出赞赏的笑容)思路是对的,但细节还需要深入研究。好了,今天的面试就到这里。
谢飞机:谢谢面试官!
老张:回去等通知吧,我们会在一周内给你反馈。
面试答案解析
第一轮答案详解
1. JVM堆内存飙升排查
排查步骤:
# 查看进程PID
jps -l
# 监控GC情况
jstat -gcutil <pid> 1000
# 导出堆 dump
jmap -dump:format=b,file=heap.hprof <pid>
# 分析工具:VisualVM、Eclipse MAT、JProfiler
G1 vs CMS对比: | 特性 | G1 | CMS | |------|-----|-----| | 新生代+老年代划分 | Region分区 | 连续空间 | | GC停顿时间 | 可预测 | 不可控 | | 适用场景 | 大堆内存 | 低延迟需求 | | 标记算法 | 可达性分析 | 增量标记 |
2. ThreadLocal的使用场景
// 用户上下文封装
public class UserContext {
private static final ThreadLocal<UserInfo> USER_INFO = new ThreadLocal<>();
public static void setUserInfo(UserInfo user) {
USER_INFO.set(user);
}
public static UserInfo getUserInfo() {
return USER_INFO.get();
}
public static void clear() {
USER_INFO.remove();
}
}
**注意:**必须在finally块中clear(),防止内存泄漏。
3. DB和Redis数据不一致解决方案
方案一:Cache Aside Pattern
@Transactional
public void updateOrder(Order order) {
// 1. 更新数据库
orderMapper.update(order);
// 2. 删除缓存
redisTemplate.delete("order:" + order.getId());
}
方案二:延时双删
- 先删缓存
- 更新数据库
- 休眠N毫秒
- 再次删除缓存
方案三: Canal监听Binlog异步更新
第二轮答案详解
1. Spring Cloud核心组件
| 组件 | 作用 | 替代方案 | |------|------|----------| | Eureka/Nacos | 服务注册发现 | Consul、Zookeeper | | Feign/OpenFeign | 声明式HTTP客户端 | RestTemplate、Dubbo | | Hystrix/Sentinel | 熔断降级 | Resilience4j | | Gateway/Zuul | API网关 | Kong、Apigee | | Config/Bus | 配置管理 | Apollo、Nacos Config |
2. Seata AT模式原理
执行流程:
- 获取全局锁(Global Lock)
- 执行本地事务,记录Before Image和After Image
- 提交本地事务
- 异步提交全局事务(Undo Log)
- 若失败则根据Undo Log回滚
关键表结构:
CREATE TABLE undo_log (
id BIGINT NOT NULL AUTO_INCREMENT,
branch_id BIGINT NOT NULL,
xid VARCHAR(128) NOT NULL,
context VARCHAR(128) NOT NULL,
rollback_info LONGBLOB NOT NULL,
log_status INT NOT NULL,
log_created DATETIME NOT NULL,
log_modified DATETIME NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY ux_undo_log (xid, branch_id)
);
3. Kafka消息可靠性保证
# Producer配置
acks: all # 所有副本确认
retries: 3 # 重试次数
batch.size: 65536
linger.ms: 10
# Consumer配置
enable.auto.commit: false # 手动提交offset
max.poll.interval.ms: 300000
第三轮答案详解
1. 秒杀系统设计
完整流程图:
用户请求 → 限流网关(Nginx/Lua) → Redis预扣库存(Lua)
→ MQ异步下单 → 消费者处理 → 数据库落库 → 返回结果
Redis库存扣减代码:
String script = "if redis.call('get', KEYS[1]) > ARGV[1] then " +
"return redis.call('decr', KEYS[1]) else return 0 end";
Long result = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class),
Collections.singletonList(key), count);
防刷措施:
- Nginx层限流:limit_req_zone
- 验证码校验
- 用户黑名单
- IP频率限制
2. 支付系统幂等性设计
方案一:唯一业务流水号
CREATE TABLE payment_order (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
pay_no VARCHAR(64) UNIQUE NOT NULL COMMENT '支付流水号',
amount DECIMAL(10,2) NOT NULL,
status TINYINT DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
方案二:状态机控制
public enum PaymentStatus {
CREATED(0), PAID(1), REFUNDED(2), CLOSED(3);
// 状态转换规则
Map<Pair<PaymentStatus, PaymentStatus>, Boolean> TRANSITION_RULES = new HashMap<>();
}
方案三:Redis去重
String key = "pay:lock:" + payNo;
Boolean success = redisTemplate.opsForValue().setIfAbsent(key, "1", 30, TimeUnit.SECONDS);
if (success) {
try {
// 执行业务逻辑
} finally {
redisTemplate.delete(key);
}
}
3. 千万级推荐系统架构
技术选型: | 模块 | 技术栈 | 说明 | |------|--------|------| | 数据采集 | Flume/Kafka | 用户行为日志收集 | | 实时计算 | Flink | 实时特征工程 | | 离线计算 | Spark/Hive | T+1批量训练 | | 模型服务 | TensorFlow Serving | 模型推理 | | 召回引擎 | Elasticsearch | 向量相似度搜索 | | 排序引擎 | XGBoost/LightGBM | 精排模型 | | 缓存 | Redis Cluster | 热点数据缓存 | | 存储 | Cassandra/Doris | 海量数据存储 |
推荐流程:
用户请求 → 召回层(多路召回) → 粗排 → 精排 → 重排 → 返回结果
↓ ↓ ↓ ↓
协同过滤 特征拼接 深度学习 多样性调整
内容推荐 交叉特征 模型推理 业务规则
总结
本次面试覆盖了以下技术要点:
Java基础
- JVM内存模型与GC算法
- ThreadLocal生命周期管理
- 并发编程最佳实践
中间件
- Redis数据结构与持久化
- Kafka消息可靠投递
- MySQL索引优化与事务隔离
微服务
- Spring Cloud组件选型
- 分布式事务解决方案
- 服务治理与监控
架构设计
- 高并发秒杀方案
- 幂等性保障策略
- 大规模推荐系统架构
运维监控
- ELK日志收集
- Prometheus+Grafana监控
- SkyWalking链路追踪
建议求职者:
- 基础知识要扎实,不能只背八股文
- 要有实际项目经验,能说出遇到的问题和解决方案
- 关注技术原理,理解底层实现
- 培养系统性思维,能从业务角度思考技术方案
祝各位求职顺利!🚀
更多推荐
所有评论(0)