分享一下面试被问了负载均衡的这些问题
负载均衡的类型
负载均衡可以分为两种类型:
- 服务器端负载平衡
- 客户端负载平衡
1.服务器端负载平衡
在服务器端负载平衡中,服务的实例部署在多个服务器上,然后在它们前面放置一个负载均衡器。通常有硬件负载平衡器——F5以及软件负载均衡-Nginx。所有传入的请求流量首先作为中间组件到达此负载均衡器。然后,它根据某种算法来决定将特定请求定向到哪个服务器。
服务器端负载平衡的缺点
- 服务器端负载均衡器就像是一个单点故障,就像它失败一样,由于只有负载均衡器具有服务器列表,因此微服务的所有实例都变得不可访问。
- 由于每个微服务将有一个单独的负载均衡器,因此系统的整体复杂性增加,并且变得难以管理。
- 随着请求的跳数从负载均衡器的一跳增加到两跳,一跳到负载均衡器,然后另一跳从负载均衡器跳转到微服务,网络延迟就会增加。
2.客户端负载均衡
该服务的实例部署在多个服务器上。负载均衡器的逻辑是客户端本身的一部分,它包含服务器列表,并根据某种算法决定特定请求必须定向到哪台服务器。这些客户端负载平衡器也称为软件负载均衡器。
客户端负载均衡的缺点
- 负载均衡器的逻辑与微服务代码混合在一起。
根据官方网站,Netflix的Ribbon是一个进程间通信(远程过程调用)库,内置客户端(软件)负载均衡器,是Netflix开源软件(Netflix OSS)的一部分。负载均衡:它提供客户端负载均衡功能。容错:它可以用来确定服务器是否正常运行,也可以检测那些宕机的服务器,从而忽略它们以发送进一步的请求。可配置的负载均衡规则:默认情况下,ribbon使用RoundRobinRule在服务器之间分配请求。
如下是实际源码中的实现方式:
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
log.warn("no load balancer");
return null;
} else {
Server server = null;
int count = 0;
while(true) {
if (server == null && count++ < 10) {
List<Server> reachableServers = lb.getReachableServers();
List<Server> allServers = lb.getAllServers();
int upCount = reachableServers.size();
int serverCount = allServers.size();
if (upCount != 0 && serverCount != 0) {
int nextServerIndex = this.incrementAndGetModulo(serverCount);
server = (Server)allServers.get(nextServerIndex);
if (server == null) {
Thread.yield();
} else {
if (server.isAlive() && server.isReadyToServe()) {
return server;
}
server = null;
}
continue;
}
log.warn("No up servers available from load balancer: " + lb);
return null;
}
if (count >= 10) {
log.warn("No available alive servers after 10 tries from load balancer: " + lb);
}
return server;
}
}
}
private int incrementAndGetModulo(int modulo) {
int current;
int next;
do {
current = this.nextServerCyclicCounter.get();
next = (current + 1) % modulo;
} while(!this.nextServerCyclicCounter.compareAndSet(current, next));
return next;
}
除此之外,还有权重轮询策略,随机轮询策略等。我们还可以根据需要自定义规则。它支持多种协议,如HTTP, TCP, UDP等。