Java面试秋招之高并发系统设计

第10章 高并发系统设计

面试重要程度:⭐⭐⭐⭐⭐

常见提问方式:如何设计一个秒杀系统?如何处理热点数据?

预计阅读时间:45分钟

开场白

兄弟,高并发系统设计绝对是面试中的王炸题目!这不仅考察你的技术深度,更能看出你的架构思维和解决复杂问题的能力。我见过太多技术不错的同学,就是在系统设计这关败下阵来。

今天我们就把高并发系统设计的核心思路和实战经验彻底搞清楚,让你在面试中展现出架构师级别的思维。

🏗️ 10.1 系统架构设计原则

高可用、高并发、高性能

面试必问:

面试官:"设计一个高并发系统需要考虑哪些方面?如何保证系统的高可用?"

系统设计三高原则:

1. 高可用(High Availability)

// 高可用设计要点
/*
1. 消除单点故障
   - 服务集群部署
   - 数据库主从/集群
   - 缓存集群

2. 故障隔离
   - 服务拆分
   - 资源隔离
   - 熔断降级

3. 快速恢复
   - 健康检查
   - 自动重启
   - 故障转移
*/

@Component
public class HighAvailabilityService {
    
    // 多数据源配置,实现故障转移
    @Autowired
    @Qualifier("masterDataSource")
    private DataSource masterDataSource;
    
    @Autowired
    @Qualifier("slaveDataSource") 
    private DataSource slaveDataSource;
    
    @Autowired
    private CircuitBreaker circuitBreaker;
    
    public User getUserById(Long userId) {
        // 使用熔断器保护服务调用
        return circuitBreaker.executeSupplier(() -> {
            try {
                // 优先使用主库
                return getUserFromDataSource(masterDataSource, userId);
            } catch (Exception e) {
                log.warn("主库查询失败,切换到从库: {}", e.getMessage());
                // 故障转移到从库
                return getUserFromDataSource(slaveDataSource, userId);
            }
        });
    }
    
    private User getUserFromDataSource(DataSource dataSource, Long userId) {
        // 具体的数据库查询逻辑
        try (Connection conn = dataSource.getConnection()) {
            // 查询用户信息
            return queryUser(conn, userId);
        } catch (SQLException e) {
            throw new RuntimeException("数据库查询失败", e);
        }
    }
}

2. 高并发(High Concurrency)

// 高并发处理策略
@RestController
public class HighConcurrencyController {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Autowired
    private RateLimiter rateLimiter;
    
    @Autowired
    private ThreadPoolExecutor asyncExecutor;
    
    // 1. 限流保护
    @GetMapping("/api/products/{id}")
    public ResponseEntity<Product> getProduct(@PathVariable Long id) {
        // 令牌桶限流
        if (!rateLimiter.tryAcquire(1, TimeUnit.SECONDS)) {
            return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS)
                .body(null);
        }
        
        // 2. 缓存优先
        String cacheKey = "product:" + id;
        Product product = (Product) redisTemplate.opsForValue().get(cacheKey);
        
        if (product != null) {
            return ResponseEntity.ok(product);
        }
        
        // 3. 异步处理
        CompletableFuture<Product> future = CompletableFuture.supplyAsync(() -> {
            Product p = productService.getById(id);
            if (p != null) {
                // 缓存结果
                redisTemplate.opsForValue().set(cacheKey, p, 30, TimeUnit.MINUTES);
            }
            return p;
        }, asyncExecutor);
        
        try {
            product = future.get(2, TimeUnit.SECONDS); // 超时控制
            return ResponseEntity.ok(product);
        } catch (TimeoutException e) {
            // 超时返回默认值
            return ResponseEntity.ok(getDefaultProduct(id));
        }
    }
}

3. 高性能(High Performance)

// 高性能优化策略
@Service
public class HighPerformanceService {
    
    // 1. 连接池优化
    @Bean
    public HikariDataSource dataSource() {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl("jdbc:mysql://localhost:3306/test");
        config.setUsername("root");
        config.setPassword("password");
        
        // 连接池参数调优
        config.setMaximumPoolSize(50);          // 最大连接数
        config.setMinimumIdle(10);              // 最小空闲连接
        config.setConnectionTimeout(30000);     // 连接超时30秒
        config.setIdleTimeout(600000);          // 空闲超时10分钟
        config.setMaxLifetime(1800000);         // 连接最大生命周期30分钟
        config.setLeakDetectionThreshold(60000); // 连接泄漏检测
        
        return new HikariDataSource(config);
    }
    
    // 2. 批量处理
    @Transactional
    public void batchUpdateProducts(List<Product> products) {
        int batchSize = 1000;
        
        for (int i = 0; i < products.size(); i += batchSize) {
            int endIndex = Math.min(i + batchSize, products.size());
            List<Product> batch = products.subList(i, endIndex);
            
            // 批量更新
            productMapper.batchUpdate(batch);
            
            // 防止长事务,分批提交
            if (i > 0 && i % (batchSize * 10) == 0) {
                entityManager.flush();
                entityManager.clear();
            }
        }
    }
    
    // 3. 异步处理
    @Async("taskExecutor")
    public CompletableFuture<Void> processOrderAsync(Order order) {
        try {
            // 耗时的业务处理
            processOrderInternal(order);
            
            // 发送通知
            notificationService.sendOrderNotification(order);
            
            return CompletableFuture.completedFuture(null);
        } catch (Exception e) {
            log.error("异步处理订单失败: orderId={}", order.getId(), e);
            return CompletableFuture.failedFuture(e);
        }
    }
}

可扩展性与可维护性

水平扩展设计:

// 无状态服务设计
@RestController
public class StatelessController {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    // ❌ 有状态设计(不可扩展)
    private Map<String, Object> localCache = new ConcurrentHashMap<>();
    
    // ✅ 无状态设计(可水平扩展)
    @GetMapping("/api/sessions/{sessionId}")
    public ResponseEntity<SessionInfo> getSession(@PathVariable String sessionId) {
        // 使用外部存储,而不是本地状态
        SessionInfo session = (SessionInfo) redisTemplate.opsForValue()
            .get("session:" + sessionId);
        
        return session != null ? 
            ResponseEntity.ok(session) : 
            ResponseEntity.notFound().build();
    }
    
    // 服务实例间负载均衡
    @PostMapping("/api/orders")
    public ResponseEntity<Order> createOrder(@RequestBody OrderRequest request) {
        // 生成全局唯一ID,避免依赖单机序列
        String orderId = generateGlobalUniqueId();
        
        Order order = new Order();
        order.setId(orderId);
        order.setUserId(request.getUserId());
        order.setAmount(request.getAmount());
        
        // 业务处理
        orderService.createOrder(order);
        
        return ResponseEntity.ok(order);
    }
    
    private String generateGlobalUniqueId() {
        // 雪花算法生成分布式ID
        return snowflakeIdGenerator.nextId();
    }
}

⚖️ 10.2 负载均衡与限流

负载均衡策略

面试重点:

面试官:"负载均衡有哪些算法?各自的优缺点是什么?"

负载均衡算法实现:

// 负载均衡器接口
public interface LoadBalancer {
    ServerInstance selectServer(List<ServerInstance> servers, String clientId);
}

// 1. 轮询算法
@Component
public class RoundRobinLoadBalancer implements LoadBalancer {
    
    private final AtomicInteger counter = new AtomicInteger(0);
    
    @Override
    public ServerInstance selectServer(List<ServerInstance> servers, String clientId) {
        if (servers.isEmpty()) {
            return null;
        }
        
        int index = counter.getAndIncrement() % servers.size();
        return servers.get(index);
    }
}

// 2. 加权轮询算法
@Component
public class WeightedRoundRobinLoadBalancer implements LoadBalancer {
    
    private final Map<String, Integer> currentWeights = new ConcurrentHashMap<>();
    
    @Override
    public ServerInstance selectServer(List<ServerInstance> servers, String clientId) {
        if (servers.isEmpty()) {
            return null;
        }
        
        int totalWeight = servers.stream().mapToInt(ServerInstance::getWeight).sum();
        ServerInstance selected = null;
        int maxCurrentWeight = 0;
        
        for (ServerInstance server : servers) {
            String serverId = server.getId();
            int weight = server.getWeight();
            
            // 增加当前权重
            int currentWeight = currentWeights.getOrDefault(serverId, 0) + weight;
            currentWeights.put(serverId, currentWeight);
            
            // 选择当前权重最大的服务器
            if (currentWeight > maxCurrentWeight) {
                maxCurrentWeight = currentWeight;
                selected = server;
            }
        }
        
        // 减少被选中服务器的权重
        if (selected != null) {
            currentWeights.put(selected.getId(), 
                currentWeights.get(selected.getId()) - totalWeight);
        }
        
        return selected;
    }
}

// 3. 一致性Hash算法
@Component
public class ConsistentHashLoadBalancer implements LoadBalancer {
    
    private final int virtualNodes = 150; // 虚拟节点数
    private final TreeMap<Long, ServerInstance> ring = new TreeMap<>();
    
    public void addServer(ServerInstance server) {
        for (int i = 0; i < virtualNodes; i++) {
            String virtualNodeId = server.getId() + "#" + i;
            long hash = hash(virtualNodeId);
            ring.put(hash, server);
        }
    }
    
    public void removeServer(ServerInstance server) {
        for (int i = 0; i < virtualNodes; i++) {
            String virtualNodeId = server.getId() + "#" + i;
            long hash = hash(virtualNodeId);
            ring.remove(hash);
        }
    }
    
    @Override
    public ServerInstance selectServer(List<ServerInstance> servers, String clientId) {
        if (ring.isEmpty()) {
            return null;
        }
        
        long hash = hash(clientId);
        
        // 找到第一个大于等于该hash值的节点
        Map.Entry<Long, ServerInstance> entry = ring.ceilingEntry(hash);
        
        // 如果没找到,则返回第一个节点(环形结构)
        if (entry == null) {
            entry = ring.firstEntry();
        }
        
        return entry.getValue();
    }
    
    private long hash(String key) {
        // 使用FNV1_32_HASH算法
        final int p = 16777619;
        int hash = (int) 2166136261L;
        
        for (byte b : key.getBytes()) {
            hash = (hash ^ b) * p;
        }
        hash += hash << 13;
        hash ^= hash >> 7;
        hash += hash << 3;
        hash ^= hash >> 17;
        hash += hash << 5;
        
        return hash < 0 ? Math.abs(hash) : hash;
    }
}

// 4. 最少连接算法
@Component
public class LeastConnectionsLoadBalancer implements LoadBalancer {
    
    private final Map<String, AtomicInteger> connectionCounts = new ConcurrentHashMap<>();
    
    @Override
    public ServerInstance selectServer(List<ServerInstance> servers, String clientId) {
        if (servers.isEmpty()) {
            return null;
        }
        
        ServerInstance selected = null;
        int minConnections = Integer.MAX_VALUE;
        
        for (ServerInstance server : servers) {
            int connections = connectionCounts.getOrDefault(server.getId(), 
                new AtomicInteger(0)).get();
            
            if (connections < minConnections) {
                minConnections = connections;
                selected = server;
            }
        }
        
        // 增加连接数
        if (selected != null) {
            connectionCounts.computeIfAbsent(selected.getId(), 
                k -> new AtomicInteger(0)).incrementAndGet();
        }
        
        return selected;
    }
    
    public void releaseConnection(String serverId) {
        AtomicInteger count = connectionCounts.get(serverId);
        if (count != null) {
            count.decrementAndGet();
        }
    }
}

限流算法实现

四种限流算法:

// 1. 固定窗口算法
@Component
public class FixedWindowRateLimiter {
    
    private final Map<String, WindowInfo> windows = new ConcurrentHashMap<>();
    
    public boolean tryAcquire(String key, int limit, long windowSizeMs) {
        long now = System.currentTimeMillis();
        long windowStart = now / windowSizeMs * windowSizeMs;
        
        WindowInfo window = windows.computeIfAbsent(key, k -> new WindowInfo());
        
        synchronized (window) {
            if (window.windowStart != windowStart) {
                // 新窗口,重置计数
                window.windowStart = windowStart;
                window.count = 0;
            }
            
            if (window.count < limit) {
                window.count++;
                return true;
            }
            
            return false;
        }
    }
    
    private static class WindowInfo {
        long windowStart;
        int count;
    }
}

// 2. 滑动窗口算法
@Component
public class SlidingWindowRateLimiter {
    
    private final Map<String, Queue<Long>> requestTimes = new ConcurrentHashMap<>();
    
    public boolean tryAcquire(String key, int limit, long windowSizeMs) {
        long now = System.currentTimeMillis();
        Queue<Long> times = requestTimes.computeIfAbsent(key, k -> new ConcurrentLinkedQueue<>());
        
        synchronized (times) {
            // 清理过期的请求时间
            while (!times.isEmpty() && now - times.peek() > windowSizeMs) {
                times.poll();
            }
            
            if (times.size() < limit) {
                times.offer(now);
                return true;
            }
            
            return false;
        }
    }
}

// 3. 令牌桶算法
@Component
public class TokenBucketRateLimiter {
    
    private final Map<String, TokenBucket> buckets = new ConcurrentHashMap<>();
    
    public boolean tryAcquire(String key, int capacity, double refillRate) {
        TokenBucket bucket = buckets.computeIfAbsent(key, 
            k -> new TokenBucket(capacity, refillRate));
        
        return bucket.tryConsume(1);
    }
    
    private static class TokenBucket {
        private final int capacity;
        private final double refillRate;
        private double tokens;
        private long lastRefillTime;
        
        public TokenBucket(int capacity, double refillRate) {
            this.capacity = capacity;
            this.refillRate = refillRate;
            this.tokens = capacity;
            this.lastRefillTime = System.currentTimeMillis();
        }
        
        public synchronized boolean tryConsume(int tokensRequested) {
            refill();
            
            if (tokens >= tokensRequested) {
                tokens -= tokensRequested;
                return true;
            }
            
            return false;
        }
        
        private void refill() {
            long now = System.currentTimeMillis();
            double tokensToAdd = (now - lastRefillTime) / 1000.0 * refillRate;
            tokens = Math.min(capacity, tokens + tokensToAdd);
            lastRefillTime = now;
        }
    }
}

// 4. 漏桶算法
@Component
public class LeakyBucketRateLimiter {
    
    private final Map<String, LeakyBucket> buckets = new ConcurrentHashMap<>();
    
    public boolean tryAcquire(String key, int capacity, double leakRate) {
        LeakyBucket bucket = buckets.computeIfAbsent(key, 
            k -> new LeakyBucket(capacity, leakRate));
        
        return bucket.tryAdd();
    }
    
    private static class LeakyBucket {
        private final int capacity;
        private final double leakRate;
        private double water;
        private long lastLeakTime;
        
        public LeakyBucket(int capacity, double leakRate) {
            this.capacity = capacity;
            this.leakRate = leakRate;
            this.water = 0;
            this.lastLeakTime = System.currentTimeMillis();
        }
        
        public synchronized boolean tryAdd() {
            leak();
            
            if (water < capacity) {
                water++;
                return true;
            }
            
            return false;
        }
        
        private void leak() {
            long now = System.currentTimeMillis();
            double leaked = (now - lastLeakTime) / 1000.0 * leakRate;
            water = Math.max(0, water - leaked);
            lastLeakTime = now;
        }
    }
}

熔断降级机制

熔断器实现:

// 熔断器状态
public enum CircuitBreakerState {
    CLOSED,    // 关闭状态,正常处理请求
    OPEN,      // 开启状态,拒绝所有请求
    HALF_OPEN  // 半开状态,允许少量请求测试服务是否恢复
}

// 熔断器实现
@Component
public class CircuitBreaker {
    
    private volatile CircuitBreakerState state = CircuitBreakerState.CLOSED;
    private final AtomicInteger failureCount = new AtomicInteger(0);
    private final AtomicInteger successCount = new AtomicInteger(0);
    private final AtomicInteger requestCount = new AtomicInteger(0);
    private volatile long lastFailureTime = 0;
    
    // 配置参数
    private final int failureThreshold = 5;      // 失败阈值
    private final int successThreshold = 3;      // 成功阈值
    private final long timeout = 60000;          // 超时时间(毫秒)
    private final double failureRateThreshold = 0.5; // 失败率阈值
    
    public <T> T execute(Supplier<T> supplier) {
        if (state == CircuitBreakerState.OPEN) {
            if (System.currentTimeMillis() - lastFailureTime > timeout) {
                // 超时后进入半开状态
                state = CircuitBreakerState.HALF_OPEN;
                successCount.set(0);
            } else {
                // 熔断器开启,直接抛出异常
                throw new CircuitBreakerOpenException("Circuit breaker is open");
            }
        }
        
        try {
            T result = supplier.get();
            onSuccess();
            return result;
        } catch (Exception e) {
            onFailure();
            throw e;
        }
    }
    
    private void onSuccess() {
        requestCount.incrementAndGet();
        
        if (state == CircuitBreakerState.HALF_OPEN) {
            int currentSuccessCount = successCount.incrementAndGet();
            if (currentSuccessCount >= successThreshold) {
                // 成功次数达到阈值,关闭熔断器
                state = CircuitBreakerState.CLOSED;
                reset();
            }
        } else if (state == CircuitBreakerState.CLOSED) {
            // 重置失败计数
            failureCount.set(0);
        }
    }
    
    private void onFailure() {
        requestCount.incrementAndGet();
        int currentFailureCount = failureCount.incrementAndGet();
        lastFailureTime = System.currentTimeMillis();
        
        if (state == CircuitBreakerState.HALF_OPEN) {
            // 半开状态下失败,立即开启熔断器
            st

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

Java面试圣经 文章被收录于专栏

Java面试圣经,带你练透java圣经

全部评论
考虑南京OD的宝子们看过来,你就是我们要找的人,一对一指导,可私信
点赞 回复 分享
发布于 08-09 16:48 贵州

相关推荐

评论
2
4
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务