架构系列十(负载均衡组件设计实现思考)
1.简介
提到负载均衡,我们最早想到的可能是nginx,f5这样一些产品解决方案。毕竟在微服务盛行以前,业界更多的是将单体应用通过集群方式部署,在集群应用前通过f5硬负载,或者通过nginx软负载,架构如下图
像上图通过nginx负载,我们叫做服务端负载均衡,且常用实现负载均衡的算法有轮询、随机等。
那么在微服务盛行的当下,我们更加需要实现负载均衡,一个典型的微服务架构如下图
上图架构文字描述
在微服务体系架构下,需要服务注册与发现组件,比如说Eureka,Nacos
所有服务,启动时向注册中心注册,比如服务提供者a、服务提供者b、服务消费者
服务消费者,当需要使用其它服务提供的能力时
从注册中心注册表中,获取服务提供者信息
服务提供者,可能是一个多实例集群部署
那么服务消费者,在拿到服务提供者信息时,该如何处理呢?
需要一个负载均衡组件,比如说ribbon,它可以解决
从某服务的一组实例信息中(一个服务列表)
根据某种负载均衡策略,选定某个实例
将该实例信息,提供给服务消费者,实现服务时间的远程调用
像这样集成在服务消费者内部的负载均衡组件,比如说ribbon,我们称为客户端负载均衡。
通过上述了解负载均衡应用案例,那么假如让我们来设计实现这样一个负载均衡的组件,需要考虑哪些因素呢?
这是本篇文章的重点,下面我们一起来看一看!
2.案例
2.1.需求分析、设计实现
回归本质,负载均衡这个事情,如果从需求层面去看,会是一个什么样子呢?大概是这样,从一个数据源(服务列表资源)中,通过某种规则,选择出一个服务以供使用,简化成编程模型,即多个输入,一个输出
于是,我们可以像下面这样去考虑设计实现一个负载均衡组件,它需要包含这么一些组件
ServerList:服务资源列表,即数据源
Rule:实现多个输入,到一个输出转换的规则,即负载均衡规则,或者说是算法(轮询、随机、加权)
Ping:确保输出的服务实例是可用的、健康的
ServerListFilter:服务资源列表过滤,也许我们拿到的10个服务实例中,有2个是不满足要求的,需要过滤处理掉,最终保留8个。最后再从8个中选择1个
ServerListUpdater:服务资源列表更新,当注册中心注册表中的服务发生更新后,需要更新负载均衡组件,缓存的服务资源列表
LoadBalancer:负载均衡,作为通用的解决方案,通用组件,当与其它组件整合使用时,需要有一个使用入口,该入口整合了负载均衡自身的其它组件
你看,这样一来,我们就抽象出负载均衡产品的核心组件了。事实上,它们正是ribbon的六大核心组件
2.2.ribbon各组件源码
2.2.1.ServerList
public interface ServerList<T extends Server> {
// 获取服务列表
List<T> getInitialListOfServers();
// 获取更新后的服务列表
List<T> getUpdatedListOfServers();
}
具体实现参考,可以参考
com.netflix.loadbalancer.ConfigurationBasedServerList
org.springframework.cloud.netflix.ribbon.StaticServerList
com.alibaba.cloud.nacos.ribbon.NacosServerList
2.2.2.ServerListFilter
public interface ServerListFilter<T extends Server> {
// 过滤处理传入的ServerList
List<T> getFilteredListOfServers(List<T> var1);
}
具体实现,可以参考
com.netflix.loadbalancer.ZoneAffinityServerListFilter
2.2.3.ServerListUpdater
public interface ServerListUpdater {
void start(ServerListUpdater.UpdateAction var1);
void stop();
String getLastUpdate();
long getDurationSinceLastUpdateMs();
int getNumberMissedCycles();
int getCoreThreads();
// 更新客户端缓存的serverList
public interface UpdateAction {
void doUpdate();
}
}
具体实现,可以参考
com.netflix.loadbalancer.PollingServerListUpdater
2.2.4.IPing
public interface IPing {
// 检查服务是否健康
boolean isAlive(Server var1);
}
具体实现,可以参考
com.netflix.loadbalancer.DummyPing
2.2.5.IRule
public interface IRule {
// 根据负载均衡算法,从ServerList列表中,选择出一个服务实例
Server choose(Object var1);
void setLoadBalancer(ILoadBalancer var1);
ILoadBalancer getLoadBalancer();
}
具体实现,可以参考
com.netflix.loadbalancer.RandomRule
com.netflix.loadbalancer.RoundRobinRule
com.alibaba.cloud.nacos.ribbon.NacosRule
2.2.6.ILoadBalancer
public interface ILoadBalancer {
// 添加服务列表
void addServers(List<Server> var1);
// 选择某个服务实例
Server chooseServer(Object var1);
// 标记服务下线
void markServerDown(Server var1);
/** @deprecated */
List<Server> getServerList(boolean var1);
// 获取健康可达的服务列表
List<Server> getReachableServers();
// 获取所有服务列表
List<Server> getAllServers();
}
具体实现,可以参考
com.netflix.loadbalancer.BaseLoadBalancer
com.netflix.loadbalancer.DynamicServerListLoadBalancer