负载均衡器实现
什么是负载均衡?
在 RPC 框架中,负载均衡是一个非常重要的概念。它指的是将请求流量合理地分配到多个服务提供者实例上,以提高系统的可用性和性能。通过负载均衡,将请求分发给这个集群下的每个服务节点,从而达到多个服务节点共同分担请求压力的目的。
RPC 的负载均衡完全由 RPC 框架自身实现,RPC 的服务调用者会与“注册中心”下发的所有服务节点建立长连接,在每次发起 RPC 调用时,服务调用者都会通过配置的负载均衡插件,自主选择一个服务节点,发起 RPC 调用请求。
负载均衡类型
负载均衡可分为软件负载均衡和硬件负载均衡,本篇主要分析软件负载均衡,这是我们后端开发比较经常接触的均衡器,常见的如HAProxy、LVS、Nginx、定制化的应用请求负载(RPC框架如Dubbo)等,诸如CDN、DNS也运用到了负载均衡算法。
根据 网络层区分负载均衡:
-
根据网络层协议类型,负载均衡可分为四层负载(TCP/UDP)和七层负载(HTTP/HTTPS)等不同类型。
-
四层负载均衡直接根据传输层信息(IP、端口)进行分发,不处理应用层数据内容,优点是简单高效,缺点是容易受到SYN Flood等攻击。
-
七层负载均衡根据应用层信息(URL、请求头、cookie等)进行更精细的分发,可实现更复杂的负载逻辑,但需要额外的TCP连接层,会略微增加网络性能损耗。
-
尽管七层负载均衡需要更多时间和计算资源,但对现代机器性能而言影响已经很小了。
二层负载均衡会通过一个虚拟 MAC 地址接收请求,然后再分配到真实的 MAC 地址; 三层负载均衡会通过一个虚拟 IP 地址接收请求,然后再分配到真实的 IP 地址; 四层通过虚拟 IP + 端口接收请求,然后再分配到真实的服务器; 七层通过虚拟的 URL 或主机名接收请求,然后再分配到真实的服务器。
无论是哪层负载均衡,都需要有代理服务器,并且对外提供唯一的 IP 地址,然后根据算法将请求转发到目标服务器(实际处理请求的服务器)
负载均衡算法实现
常见的负载均衡算法主要有以下几种:
- 轮询(Round Robin):按照请求顺序依次分配到各个服务实例。这是最基础的负载均衡算法。
- 加权轮询(Weighted Round Robin):根据服务实例的权重进行轮询分配。可以针对不同性能的实例进行加权。
- 随机(Random):随机选择一个服务实例进行分配。
- 最小连接数(Least Connections):选择当前连接数最小的服务实例进行分配。这可以平衡各个实例的负载。
- 一致性哈希(Consistent Hashing):利用一致性哈希算法将请求均匀地分布到不同的服务实例上。可以应对动态扩容缩容场景。
负载均衡接口定义
我们的负载均衡器算法的作用主要就是从注册服务中去选出一个服务来使用,因此定义如下:
/**
* 负载均衡器 (消费端使用)
*/
public interface LoadBalancer {
/**
* 选择服务调用
* @param requestParams 请求参数
* @param serviceMetaInfoList 服务列表
* @return
*/
ServiceMetaInfo select(Map<String, Object> requestParams, List<ServiceMetaInfo> serviceMetaInfoList);
}
随机负载均衡器
实现最简单,每次调用随机函数 ,从列表中随便选一个即可。
/**
* 随机负载均衡器 (消费端使用)
*/
public class RandomLoadBalancer implements LoadBalancer {
private final Random random = new Random();
@Override
public ServiceMetaInfo select(Map<String, Object> requestParams, List<ServiceMetaInfo> serviceMetaInfoList) {
int size = serviceMetaInfoList.size();
if (size == 0) {
return null;
}
if (size == 1) {
return serviceMetaInfoList.get(0);
}
return serviceMetaInfoList.get(random.nextInt(size));
}
}