面试现场

面试官:严肃的架构师老李,10年大厂经验

面试者:王德发,号称3年Java开发经验的"资深"工程师


第一轮:基础微服务架构

面试官:王德发是吧,简历上写着熟悉Spring Cloud微服务架构。我们公司是做电商的,先问几个基础问题。你能说说Spring Cloud中的核心组件有哪些吗?

王德发:啊,这个我知道!Spring Cloud主要有Eureka做服务注册发现,Gateway做网关,Config做配置管理,还有Feign做服务调用。

面试官:不错,基础概念还是清楚的。那你在项目中是如何使用Eureka进行服务注册的?

王德发:就是在启动类上加个@EnableEurekaClient注解,然后在配置文件里配置eureka.client.service-url.defaultZone指向Eureka服务器地址。

面试官:很好!那假设我们电商系统有用户服务、订单服务、商品服务,你觉得应该如何划分这些微服务?

王德发:嗯...按照业务领域划分吧,用户相关的放用户服务,订单的放订单服务,商品的放商品服务...

面试官:思路正确,DDD领域驱动设计的思想。那服务间调用你一般用什么方式?

王德发:用Feign啊,声明式的HTTP客户端,很方便的。就是定义个接口,加上@FeignClient注解就行了。

面试官:基础掌握得不错。那如果订单服务调用用户服务时,用户服务挂了怎么办?

王德发:这个...用Hystrix做熔断?不过好像Hystrix已经不维护了...现在用什么来着...Resilience4j?

第二轮:深入技术实现

面试官:对,现在确实推荐用Resilience4j。看得出来你对新技术还是有关注的。那我问个具体场景,电商系统中,用户下单时需要:1.检查商品库存,2.创建订单,3.扣减库存,4.发送消息通知。这是个典型的分布式事务场景,你会怎么处理?

王德发:分布式事务...这个...可以用Seata?或者...TCC模式?具体怎么实现我...我觉得需要根据具体业务场景来设计。

面试官:概念知道,但实现细节不太清楚是吧。那我换个问题,如果商品服务需要支持高并发查询,你会怎么优化?

王德发:加缓存!用Redis!还可以用CDN,数据库读写分离,分库分表...

面试官:思路对,但你能具体说说Redis在这个场景下的使用策略吗?比如缓存更新策略?

王德发:缓存更新...旁路缓存模式?先查缓存,没有再查数据库,然后把数据放到缓存里。更新的话...先更新数据库再删缓存?还是先删缓存再更新数据库?我记得有个什么双删策略...

面试官:看得出来有了解,但细节还需要深入。那我问最后一个技术问题,微服务架构中如何实现链路追踪?

王德发:链路追踪...用Sleuth?配合Zipkin?在每个请求上加个TraceId,然后在各个服务间传递...具体实现我...我觉得Spring Boot应该有自动配置。

第三轮:架构设计与实践

面试官:我看你对技术栈都有一定了解,但实践经验还需要提升。现在给你个综合场景:我们要设计一个秒杀系统,预计QPS达到10万,你会如何设计整体架构?

王德发:秒杀系统...这个难度很大啊。需要...需要限流,用网关限流。然后用消息队列削峰填谷,把请求先放到队列里慢慢处理。缓存肯定要用,预热缓存。数据库...分库分表?还有就是...防止超卖,要用分布式锁?

面试官:方向是对的,但你能说说具体用什么技术实现限流吗?

王德发:限流...令牌桶算法?滑动窗口?具体用什么组件...Sentinel?还是网关自带的限流?我...我觉得需要结合具体情况选择。

面试官:那消息队列你会选择哪种?为什么?

王德发:Kafka?因为性能好,吞吐量大。或者RabbitMQ也行,功能比较全面。选择的话...看业务需求吧,如果要求性能就Kafka,如果要求功能丰富就RabbitMQ。

面试官:最后一个问题,如何保证整个秒杀流程的数据一致性?

王德发:数据一致性...这个真的很复杂。可能需要用分布式事务,或者设计补偿机制,最终一致性?具体怎么实现...我觉得需要根据业务容错性来设计。

面试官:好的,今天的面试就到这里。你的基础概念掌握得还不错,但在实践经验和深入理解方面还需要加强。我们会在一周内给你答复,你先回去等通知吧。

王德发:好的,谢谢面试官!


技术知识点详解

第一轮知识点解析

1. Spring Cloud核心组件
  • Eureka: 服务注册与发现中心,提供服务实例的注册、续约、下线等功能
  • Gateway: 微服务网关,提供路由、过滤、限流等功能,是Spring Cloud官方推荐的网关解决方案
  • Config: 配置管理中心,支持Git、SVN等多种配置源
  • OpenFeign: 声明式HTTP客户端,简化服务间调用
// Eureka客户端配置示例
@SpringBootApplication
@EnableEurekaClient
public class UserServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserServiceApplication.class, args);
    }
}

// application.yml配置
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
  instance:
    prefer-ip-address: true
2. 微服务划分原则

基于DDD(领域驱动设计)进行服务划分:

  • 用户服务: 用户注册、登录、个人信息管理
  • 商品服务: 商品信息管理、库存管理、价格管理
  • 订单服务: 订单创建、支付、状态管理
  • 优惠服务: 优惠券、促销活动管理
3. Resilience4j容错处理
@Component
public class UserService {
    
    @CircuitBreaker(name = "userService", fallbackMethod = "fallbackUser")
    @Retry(name = "userService")
    @TimeLimiter(name = "userService")
    public CompletableFuture<User> getUserById(Long id) {
        return CompletableFuture.supplyAsync(() -> userClient.getUser(id));
    }
    
    public CompletableFuture<User> fallbackUser(Long id, Exception ex) {
        return CompletableFuture.completedFuture(new User(id, "默认用户"));
    }
}

第二轮知识点解析

1. 分布式事务解决方案

Saga模式在电商订单场景的应用:

@SagaOrchestrationStart
public class OrderSaga {
    
    @SagaOrchestrationTask
    public void checkInventory(OrderCreatedEvent event) {
        inventoryService.reserveInventory(event.getProductId(), event.getQuantity());
    }
    
    @SagaOrchestrationTask
    public void createOrder(InventoryReservedEvent event) {
        orderService.createOrder(event.getOrderInfo());
    }
    
    @SagaOrchestrationTask
    public void processPayment(OrderCreatedEvent event) {
        paymentService.processPayment(event.getPaymentInfo());
    }
}
2. Redis缓存策略

Cache-Aside(旁路缓存)模式:

@Service
public class ProductService {
    
    @Autowired
    private RedisTemplate<String, Product> redisTemplate;
    
    public Product getProduct(Long productId) {
        String key = "product:" + productId;
        // 先查缓存
        Product product = redisTemplate.opsForValue().get(key);
        if (product == null) {
            // 缓存未命中,查数据库
            product = productRepository.findById(productId);
            if (product != null) {
                // 写入缓存,设置过期时间
                redisTemplate.opsForValue().set(key, product, Duration.ofMinutes(30));
            }
        }
        return product;
    }
    
    public void updateProduct(Product product) {
        // 先更新数据库
        productRepository.save(product);
        // 删除缓存
        redisTemplate.delete("product:" + product.getId());
    }
}
3. 链路追踪实现
// pom.xml依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>

// application.yml配置
spring:
  sleuth:
    sampler:
      probability: 1.0  # 采样率
    zipkin:
      base-url: http://localhost:9411

第三轮知识点解析

1. 秒杀系统架构设计

整体架构图:

用户请求 → CDN → 负载均衡 → API网关(限流) → 秒杀服务 → 消息队列 → 订单服务
                                            ↓
                                        Redis缓存
                                            ↓
                                        数据库集群

核心技术实现:

// 1. 网关限流配置
@Component
public class RateLimitGatewayFilterFactory extends AbstractGatewayFilterFactory<RateLimitGatewayFilterFactory.Config> {
    
    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            String key = getClientId(exchange);
            return redisRateLimiter.isAllowed(config.getRouteId(), key)
                .flatMap(response -> {
                    if (response.isAllowed()) {
                        return chain.filter(exchange);
                    } else {
                        exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
                        return exchange.getResponse().setComplete();
                    }
                });
        };
    }
}

// 2. 秒杀业务逻辑
@Service
public class SeckillService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Autowired
    private RabbitTemplate rabbitTemplate;
    
    public SeckillResult seckill(Long userId, Long productId) {
        String stockKey = "seckill:stock:" + productId;
        String userKey = "seckill:user:" + productId + ":" + userId;
        
        // 检查用户是否已经参与过秒杀
        if (redisTemplate.hasKey(userKey)) {
            return SeckillResult.fail("用户已参与过秒杀");
        }
        
        // 原子性扣减库存
        Long stock = redisTemplate.opsForValue().decrement(stockKey);
        if (stock < 0) {
            return SeckillResult.fail("商品已售完");
        }
        
        // 标记用户已参与
        redisTemplate.opsForValue().set(userKey, "1", Duration.ofDays(1));
        
        // 异步处理订单
        SeckillMessage message = new SeckillMessage(userId, productId);
        rabbitTemplate.convertAndSend("seckill.order", message);
        
        return SeckillResult.success("秒杀成功,请稍后查看订单");
    }
}

// 3. 消息队列处理
@RabbitListener(queues = "seckill.order")
public class SeckillOrderHandler {
    
    public void handleSeckillOrder(SeckillMessage message) {
        try {
            // 创建订单
            Order order = orderService.createSeckillOrder(message.getUserId(), message.getProductId());
            
            // 发送订单创建成功消息
            notificationService.sendOrderNotification(order);
        } catch (Exception e) {
            // 订单创建失败,恢复库存
            String stockKey = "seckill:stock:" + message.getProductId();
            redisTemplate.opsForValue().increment(stockKey);
            
            // 清除用户参与标记
            String userKey = "seckill:user:" + message.getProductId() + ":" + message.getUserId();
            redisTemplate.delete(userKey);
        }
    }
}
2. 分布式锁防止超卖
@Component
public class RedisDistributedLock {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    public boolean tryLock(String key, String value, long expireTime) {
        Boolean result = redisTemplate.opsForValue().setIfAbsent(key, value, Duration.ofMillis(expireTime));
        return result != null && result;
    }
    
    public void unlock(String key, String value) {
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Collections.singletonList(key), value);
    }
}
3. 消息队列选型对比
特性 Kafka RabbitMQ ActiveMQ
吞吐量 极高(100万+/秒) 高(10万/秒) 中等(5万/秒)
延迟 极低 中等
持久化 优秀 优秀 良好
可用性 中等
消息顺序 分区内有序 队列有序 队列有序
适用场景 大数据处理、日志收集 业务消息、任务队列 传统企业应用

秒杀场景推荐使用Kafka,因为需要处理大量并发请求,Kafka的高吞吐量特性非常适合。

4. 数据一致性保证

在秒杀场景中,采用最终一致性策略:

  1. 订单创建阶段: 使用Redis分布式锁保证库存扣减的原子性
  2. 支付阶段: 使用本地消息表+定时任务保证支付状态同步
  3. 库存同步: 使用消息队列异步同步缓存和数据库库存
  4. 补偿机制: 定时任务检查并修复不一致数据
// 本地消息表实现
@Transactional
public void createOrderWithMessage(Order order) {
    // 1. 创建订单
    orderRepository.save(order);
    
    // 2. 创建本地消息记录
    LocalMessage message = new LocalMessage();
    message.setContent(JsonUtils.toJson(order));
    message.setTopic("order.created");
    message.setStatus(MessageStatus.CREATED);
    localMessageRepository.save(message);
}

// 定时任务发送消息
@Scheduled(fixedDelay = 5000)
public void sendPendingMessages() {
    List<LocalMessage> pendingMessages = localMessageRepository.findByStatus(MessageStatus.CREATED);
    for (LocalMessage message : pendingMessages) {
        try {
            rabbitTemplate.convertAndSend(message.getTopic(), message.getContent());
            message.setStatus(MessageStatus.SENT);
            localMessageRepository.save(message);
        } catch (Exception e) {
            message.setRetryCount(message.getRetryCount() + 1);
            if (message.getRetryCount() > 3) {
                message.setStatus(MessageStatus.FAILED);
            }
            localMessageRepository.save(message);
        }
    }
}

总结

这次面试涵盖了Spring Cloud微服务架构在电商场景下的核心技术点:

  1. 微服务基础: Eureka服务注册发现、Gateway网关、OpenFeign服务调用
  2. 容错处理: Resilience4j熔断限流、分布式事务处理
  3. 性能优化: Redis缓存策略、消息队列削峰填谷
  4. 架构设计: 秒杀系统设计、分布式锁、数据一致性保证

掌握这些知识点,对于Java后端开发特别是微服务架构开发非常重要。建议深入学习每个技术点的原理和最佳实践,并通过实际项目加深理解。

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐