10.2 负载均衡与限流

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

常见提问方式:如何设计负载均衡策略?限流算法有哪些?如何实现熔断降级?

预计阅读时间:40分钟

🎯 负载均衡策略设计

Nginx负载均衡配置

# nginx.conf 负载均衡配置
upstream backend_servers {
    # 轮询策略(默认)
    server 192.168.1.10:8080 weight=3;
    server 192.168.1.11:8080 weight=2;
    server 192.168.1.12:8080 weight=1;
    
    # 健康检查
    server 192.168.1.13:8080 backup;  # 备用服务器
    
    # 连接参数
    keepalive 32;                      # 保持连接数
    keepalive_requests 100;            # 每个连接最大请求数
    keepalive_timeout 60s;             # 连接超时时间
}

# IP Hash负载均衡
upstream ip_hash_backend {
    ip_hash;  # 基于客户端IP的哈希分配
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
    server 192.168.1.12:8080;
}

# 最少连接数负载均衡
upstream least_conn_backend {
    least_conn;  # 分配给连接数最少的服务器
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
    server 192.168.1.12:8080;
}

server {
    listen 80;
    server_name api.example.com;
    
    location /api/ {
        proxy_pass http://backend_servers;
        
        # 代理设置
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        
        # 超时设置
        proxy_connect_timeout 5s;
        proxy_send_timeout 10s;
        proxy_read_timeout 10s;
        
        # 重试设置
        proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
        proxy_next_upstream_tries 3;
        proxy_next_upstream_timeout 10s;
    }
}

应用层负载均衡实现

/**
 * 应用层负载均衡器
 */
@Component
public class ApplicationLoadBalancer {
    
    /**
     * 负载均衡策略接口
     */
    public interface LoadBalanceStrategy {
        ServerInstance select(List<ServerInstance> servers, String clientInfo);
    }
    
    /**
     * 轮询策略
     */
    @Component
    public static class RoundRobinStrategy implements LoadBalanceStrategy {
        
        private final AtomicInteger counter = new AtomicInteger(0);
        
        @Override
        public ServerInstance select(List<ServerInstance> servers, String clientInfo) {
            if (servers.isEmpty()) {
                return null;
            }
            
            int index = counter.getAndIncrement() % servers.size();
            return servers.get(index);
        }
    }
    
    /**
     * 加权轮询策略
     */
    @Component
    public static class WeightedRoundRobinStrategy implements LoadBalanceStrategy {
        
        private final ConcurrentHashMap<String, AtomicInteger> serverWeights = new ConcurrentHashMap<>();
        
        @Override
        public ServerInstance select(List<ServerInstance> servers, String clientInfo) {
            if (servers.isEmpty()) {
                return null;
            }
            
            // 计算总权重
            int totalWeight = servers.stream().mapToInt(ServerInstance::getWeight).sum();
            
            // 为每个服务器增加当前权重
            servers.forEach(server -> {
                String serverId = server.getId();
                AtomicInteger currentWeight = serverWeights.computeIfAbsent(serverId, 
                    k -> new AtomicInteger(0));
                currentWeight.addAndGet(server.getWeight());
            });
            
            // 选择当前权重最大的服务器
            ServerInstance selected = servers.stream()
                .max(Comparator.comparing(server -> 
                    serverWeights.get(server.getId()).get()))
                .orElse(null);
            
            if (selected != null) {
                // 减去总权重
                serverWeights.get(selected.getId()).addAndGet(-totalWeight);
            }
            
            return selected;
        }
    }
    
    /**
     * 一致性哈希策略
     */
    @Component
    public static class ConsistentHashStrategy implements LoadBalanceStrategy {
        
        private final TreeMap<Long, ServerInstance> hashRing = new TreeMap<>();
        private final int virtualNodes = 150; // 虚拟节点数
        
        public void addServer(ServerInstance server) {
            for (int i = 0; i < virtualNodes; i++) {
                String virtualNodeName = server.getId() + "&&VN" + i;
                long hash = hash(virtualNodeName);
                hashRing.put(hash, server);
            }
        }
        
        @Override
        public ServerInstance select(List<ServerInstance> servers, String clientInfo) {
            if (servers.isEmpty()) {
                return null;
            }
            
            long hash = hash(clientInfo);
            
            // 找到第一个大于等于该hash值的节点
            Map.Entry<Long, ServerInstance> entry = hashRing.ceilingEntry(hash);
            if (entry == null) {
                // 如果没有找到,返回第一个节点(环形)
                entry = hashRing.firstEntry();
            }
            
            return entry != null ? entry.getValue() : null;
        }
        
        private long hash(String key) {
            // 使用FNV1_32_HASH算法
            final int p = 16777619;
            int hash = (int) 2166136261L;
            for (int i = 0; i < key.length(); i++) {
                hash = (hash ^ key.charAt(i)) * p;
            }
            hash += hash << 13;
            hash ^= hash >> 7;
            hash += hash << 3;
            hash ^= hash >> 17;
            hash += hash << 5;
            return hash < 0 ? Math.abs(hash) : hash;
        }
    }
}

🚦 限流算法实现

令牌桶算法

/**
 * 令牌桶限流算法
 */
@Component
public class TokenBucketRateLimiter {
    
    /**
     * 令牌桶实现
     */
    public static class TokenBucket {
        
        private final long capacity;           // 桶容量
        private final long refillRate;         // 令牌生成速率(每秒)
        private final AtomicLong tokens;       // 当前令牌数
        private volatile long lastRefillTime;  // 上次补充时间
        private final ReentrantLock lock = new ReentrantLock();
        
        public TokenBucket(long capacity, long refillRate) {
            this.capacity = capacity;
            this.refillRate = refillRate;
            this.tokens = new AtomicLong(capacity);
            this.lastRefillTime = System.currentTimeMillis();
        }
        
        /**
         * 尝试获取令牌
         */
        public boolean tryAcquire() {
            return tryAcquire(1);
        }
        
        /**
         * 尝试获取指定数量的令牌
         */
        public boolean tryAcquire(long tokensRequested) {
            lock.lock();
            try {
                refill();
                
                if (tokens.get() >= tokensRequested) {
                    tokens.addAndGet(-tokensRequested);
                    return true;
                }
                return false;
            } finally {
                lock.unlock();
            }
        }
        
        /**
         * 补充令牌
         */
        private void refill() {
            long currentTime = System.currentTimeMillis();
            long timePassed = cu

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

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

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

全部评论

相关推荐

评论
点赞
收藏
分享

创作者周榜

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