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圣经

小天才公司福利 1199人发布