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圣经
查看12道真题和解析