Java大厂面试:Spring Cloud+Redis+Kafka电商微服务架构实战解析

前言

大家好,今天为大家带来一篇趣味十足的Java大厂面试文章。通过严肃面试官与搞笑程序员谢飞机的对话,带你深入理解电商微服务架构的核心技术点。


面试场景:某互联网大厂Java后端工程师面试

面试官:(严肃脸)请坐,我是今天的技术面试官。看你简历上写有电商项目经验,那我们今天就聊聊电商场景下的微服务架构设计。

谢飞机:(紧张地搓手)好的好的,面试官您好!我...我确实做过电商项目,虽然主要是抄的开源代码...啊不是,是参考学习的!


第一轮:基础架构与服务治理

面试官:好,第一个问题。电商大促时流量激增,你们用什么做服务注册与发现?

谢飞机:(眼睛一亮)这个我会!Eureka!不对...现在都用Nacos了!服务注册发现嘛,就是服务启动时把自己注册到注册中心,其他服务通过注册中心找到它。

面试官:(微微点头)还算可以。那服务间调用用什么?

谢飞机:OpenFeign!声明式的HTTP客户端,加个@FeignClient注解就能调用其他服务了,贼方便!

面试官:不错。那如果下游服务响应慢或者挂了,怎么保护上游服务?

谢飞机:(开始冒汗)这个...这个...熔断!降级!限流!三板斧!具体实现...好像是Resilience4j?还是Hystrix?反正就是防止雪崩效应...

面试官:(皱眉)Hystrix已经停止维护了,现在推荐Resilience4j。继续,第二个问题。电商商品详情页QPS很高,你们怎么优化?

谢飞机:(来精神了)缓存!Redis缓存!热点数据放Redis,数据库扛不住。我们当时做了三级缓存:本地缓存Caffeine + Redis + 数据库。

面试官:(表情缓和)这个思路对。那缓存穿透、击穿、雪崩怎么解决?

谢飞机:穿透用布隆过滤器!击中使用互斥锁!雪崩加随机过期时间!(越说越自信)

面试官:第三个问题。订单创建后需要通知库存、积分、消息推送等多个服务,怎么设计?

谢飞机:(挠头)这个...消息队列?Kafka?RabbitMQ?反正就是异步解耦,订单服务发消息,其他服务订阅消费...

面试官:(记录)好,第一轮先到这。


第二轮:分布式事务与数据一致性

面试官:第二轮。刚才说到订单创建涉及多个服务,那分布式事务怎么保证?

谢飞机:(眼神飘忽)分布式事务啊...2PC?TCC?Saga?Seata?我们当时...好像用的Seata的AT模式?

面试官:AT模式的原理是什么?

谢飞机:(开始含糊)就是...先执行SQL,然后记录undo log,如果回滚就用undo log恢复...大概是这样?

面试官:(挑眉)大概?那如果订单服务扣减库存成功,但积分服务失败了,怎么处理?

谢飞机:(擦汗)这个...重试?补偿?实在不行就人工介入...我们当时有对账系统,每天跑批核对数据...

面试官:继续。电商场景下,超卖问题怎么解决?

谢飞机:(这次比较确定)数据库乐观锁!version字段!或者Redis分布式锁!下单时先扣减库存,扣减成功才能创建订单。

面试官:Redis分布式锁怎么实现?

谢飞机:setnx命令!加过期时间防止死锁!Redisson框架有现成的实现,看门狗机制自动续期...

面试官:(点头)还行。第三个问题,分库分表做过吗?订单表数据量大怎么拆分?

谢飞机:(明显紧张)分库分表...ShardingSphere?按用户ID哈希分片?还是按时间范围?我们当时数据量还没到那个程度...主要是加了索引优化查询...

面试官:(记录)好,进入第三轮。


第三轮:监控运维与性能优化

面试官:第三轮。线上服务怎么监控?

谢飞机:(放松了一些)Prometheus+Grafana!指标采集、可视化展示。还有ELK日志系统,查询日志很方便。

面试官:JVM调优做过吗?

谢飞机:(心虚)调优...看过一些文章。堆内存设置、GC算法选择、年轻代老年代比例...实际生产环境都是运维同事在搞,我主要是看监控面板...

面试官:(微笑)诚实是好事。那链路追踪呢?

谢飞机:SkyWalking!Jaeger!Zipkin!服务调用链可视化,能快速定位问题在哪个环节。

面试官:最后一个问题。如果让你设计一个秒杀系统,核心要点是什么?

谢飞机:(兴奋了)这个我准备过!1.前端限流,按钮点击后禁用 2.网关层限流 3.Redis预减库存 4.消息队列异步下单 5.数据库最终扣减 6.防刷机制...

面试官:(打断)Redis预减库存怎么保证和数据库一致性?

谢飞机:(卡壳)这个...一致性...可能...会有短暂不一致?反正最终会一致的...我们通过定时任务校对...

面试官:(放下笔)好了,今天的面试就到这里。

谢飞机:(紧张)那...结果怎么样?

面试官:(标准微笑)我们会综合评估,3个工作日内HR会通知你。回家等通知吧。

谢飞机:(起身)好的好的,谢谢面试官!期待您的好消息!


详细答案解析

第一轮答案解析

1. 服务注册与发现

业务场景:电商系统有用户服务、商品服务、订单服务、支付服务等多个微服务,服务实例动态扩缩容。

技术方案

  • Nacos:阿里开源,集注册中心和配置中心于一体
  • Consul:HashiCorp出品,支持多数据中心
  • Eureka:Netflix开源,已停止维护
// Nacos配置
spring:
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        service: ${spring.application.name}
2. 服务间调用

业务场景:订单服务需要调用用户服务获取用户信息,调用库存服务检查库存。

技术方案:OpenFeign声明式调用

@FeignClient(name = "user-service")
public interface UserServiceClient {
    @GetMapping("/user/{userId}")
    Result<UserVO> getUserById(@PathVariable("userId") Long userId);
}

// 使用
@Autowired
private UserServiceClient userServiceClient;

UserVO user = userServiceClient.getUserById(userId).getData();
3. 服务容错保护

业务场景:促销期间流量激增,下游服务可能响应慢或宕机。

技术方案:Resilience4j熔断降级

@CircuitBreaker(name = "inventoryService", fallbackMethod = "fallback")
public Result decreaseStock(Long productId, Integer quantity) {
    return inventoryServiceClient.decreaseStock(productId, quantity);
}

public Result fallback(Long productId, Integer quantity, Exception e) {
    // 降级逻辑:返回缓存数据或友好提示
    return Result.error("系统繁忙,请稍后重试");
}
4. 缓存优化方案

业务场景:商品详情页QPS可达10万+,数据库无法直接承载。

技术方案:三级缓存架构

// 一级缓存:Caffeine本地缓存
private Cache<Long, ProductVO> localCache = Caffeine.newBuilder()
    .maximumSize(1000)
    .expireAfterWrite(5, TimeUnit.MINUTES)
    .build();

// 二级缓存:Redis
public ProductVO getProductById(Long productId) {
    // 查本地缓存
    ProductVO product = localCache.getIfPresent(productId);
    if (product != null) {
        return product;
    }
    
    // 查Redis
    String key = "product:" + productId;
    String json = redisTemplate.opsForValue().get(key);
    if (json != null) {
        product = JSON.parseObject(json, ProductVO.class);
        localCache.put(productId, product);
        return product;
    }
    
    // 查数据库
    product = productMapper.selectById(productId);
    if (product != null) {
        redisTemplate.opsForValue().set(key, JSON.toJSONString(product), 30, TimeUnit.MINUTES);
        localCache.put(productId, product);
    }
    return product;
}

缓存问题解决方案

  • 穿透:布隆过滤器 + 缓存空对象
  • 击穿:互斥锁 + 双重检查
  • 雪崩:随机过期时间 + 高可用集群
5. 消息队列解耦

业务场景:订单创建后需要通知库存、积分、消息推送等多个服务。

技术方案:Kafka异步解耦

// 订单服务发送消息
@Autowired
private KafkaTemplate<String, Object> kafkaTemplate;

public void createOrder(Order order) {
    // 1. 创建订单
    orderMapper.insert(order);
    
    // 2. 发送消息
    OrderMessage message = new OrderMessage(order.getId(), order.getUserId());
    kafkaTemplate.send("order-created", message);
}

// 库存服务消费消息
@KafkaListener(topics = "order-created")
public void handleOrderCreated(OrderMessage message) {
    // 扣减库存
    inventoryService.decreaseStock(message.getOrderId());
}

第二轮答案解析

1. 分布式事务方案

业务场景:订单创建涉及订单服务、库存服务、积分服务,需要保证数据一致性。

技术方案对比

| 方案 | 优点 | 缺点 | 适用场景 | |------|------|------|----------| | 2PC | 强一致性 | 性能差,阻塞 | 金融核心交易 | | TCC | 性能较好 | 实现复杂 | 高并发场景 | | Saga | 简单灵活 | 最终一致性 | 长流程业务 | | Seata AT | 易用性好 | 有性能损耗 | 一般业务场景 |

Seata AT模式实现

@GlobalTransactional
public void createOrder(Order order) {
    // 1. 创建订单
    orderService.create(order);
    
    // 2. 扣减库存
    inventoryService.decrease(order.getProductId(), order.getQuantity());
    
    // 3. 增加积分
    pointService.add(order.getUserId(), order.getPoint());
}
2. 超卖问题解决方案

业务场景:秒杀活动中,100件商品可能被10000人同时抢购。

技术方案

// 方案1:数据库乐观锁
@Update("UPDATE product SET stock = stock - #{quantity} WHERE id = #{productId} AND stock >= #{quantity}")
int decreaseStock(@Param("productId") Long productId, @Param("quantity") Integer quantity);

// 方案2:Redis分布式锁
RLock lock = redissonClient.getLock("product:" + productId);
try {
    if (lock.tryLock(5, 10, TimeUnit.SECONDS)) {
        // 扣减库存逻辑
        inventoryService.decreaseStock(productId, quantity);
    }
} finally {
    lock.unlock();
}

// 方案3:Redis预减库存
Long stock = redisTemplate.opsForValue().decrement("stock:" + productId);
if (stock < 0) {
    redisTemplate.opsForValue().increment("stock:" + productId);
    return Result.error("库存不足");
}
3. 分库分表方案

业务场景:订单表数据量超过千万,查询性能下降。

技术方案:ShardingSphere分片

spring:
  shardingsphere:
    datasource:
      names: ds0,ds1
    rules:
      sharding:
        tables:
          t_order:
            actual-data-nodes: ds${0..1}.t_order${0..1}
            table-strategy:
              standard:
                sharding-column: user_id
                sharding-algorithm-name: user-inline
        sharding-algorithms:
          user-inline:
            type: INLINE
            props:
              algorithm-expression: t_order${user_id % 2}

第三轮答案解析

1. 监控体系搭建

业务场景:微服务架构下,需要全方位监控系统健康状态。

技术方案

# Prometheus配置
management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus
  metrics:
    export:
      prometheus:
        enabled: true

# Grafana仪表盘
# 导入JVM、HTTP请求、数据库连接等监控面板
2. JVM调优要点

业务场景:服务运行中出现Full GC频繁,响应时间变长。

调优参数

# 堆内存设置
-Xms4g -Xmx4g

# 年轻代设置
-Xmn2g

# GC算法
-XX:+UseG1GC

# GC日志
-XX:+PrintGCDetails -Xloggc:/var/log/gc.log

# OOM处理
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/var/log/heapdump.hprof
3. 链路追踪实现

业务场景:用户反馈下单慢,需要定位问题环节。

技术方案:SkyWalking

// 添加追踪注解
@Trace(operationName = "createOrder")
public void createOrder(Order order) {
    // 业务逻辑
}

// SkyWalking配置
agent.service_name=order-service
collector.backend_service=127.0.0.1:11800
4. 秒杀系统设计

核心架构

用户请求 → Nginx限流 → 网关鉴权 → Redis预减库存 → 
MQ异步下单 → 数据库扣减 → 返回结果

关键代码

// 秒杀控制器
@PostMapping("/seckill")
public Result seckill(@RequestBody SeckillRequest request) {
    // 1. 用户资格校验
    if (!userService.checkSeckillQualification(request.getUserId())) {
        return Result.error("无秒杀资格");
    }
    
    // 2. Redis预减库存
    Long stock = redisTemplate.opsForValue().decrement("seckill:stock:" + request.getProductId());
    if (stock < 0) {
        redisTemplate.opsForValue().increment("seckill:stock:" + request.getProductId());
        return Result.error("已抢光");
    }
    
    // 3. 发送秒杀消息
    SeckillMessage message = new SeckillMessage(request.getUserId(), request.getProductId());
    kafkaTemplate.send("seckill-order", message);
    
    // 4. 返回排队中
    return Result.success("排队中,请稍后查看结果");
}

// 异步处理秒杀订单
@KafkaListener(topics = "seckill-order")
public void handleSeckillOrder(SeckillMessage message) {
    try {
        // 创建订单
        orderService.createSeckillOrder(message);
        // 扣减真实库存
        inventoryService.decreaseStock(message.getProductId(), 1);
    } catch (Exception e) {
        // 失败回滚Redis库存
        redisTemplate.opsForValue().increment("seckill:stock:" + message.getProductId());
    }
}

学习建议

技术学习路线

  1. 基础阶段:Java核心、Spring Boot、MySQL
  2. 进阶阶段:Spring Cloud、Redis、Kafka
  3. 高级阶段:分布式事务、JVM调优、系统设计

推荐资源

  • 书籍:《深入理解Java虚拟机》《分布式系统原理》
  • 文档:Spring Cloud官方文档、Redis官方文档
  • 项目:GitHub开源电商项目、自己搭建微服务Demo

面试准备

  1. 理解原理,不只是会用
  2. 结合业务场景思考
  3. 准备项目亮点和难点
  4. 保持诚实,不会就说不会

总结

通过谢飞机与面试官的对话,我们梳理了电商微服务架构的核心技术点。记住:

  • ✅ 服务治理:Nacos + OpenFeign + Resilience4j
  • ✅ 缓存优化:多级缓存 + 缓存问题解决方案
  • ✅ 消息队列:Kafka异步解耦
  • ✅ 分布式事务:Seata/TCC/Saga根据场景选择
  • ✅ 监控运维:Prometheus + Grafana + SkyWalking
  • ✅ 系统设计:秒杀场景是经典面试题

希望这篇文章能帮助你更好地准备Java大厂面试!加油!


本文纯属虚构,如有雷同,纯属巧合。谢飞机形象仅供参考,请勿对号入座。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐