1.Ribbon是如何进行负载的
配置类:RibbonClientConfiguration
@Bean
@ConditionalOnMissingBean
public ILoadBalancer ribbonLoadBalancer(IClientConfig config,
ServerList<Server> serverList, ServerListFilter<Server> serverListFilter,
IRule rule, IPing ping, ServerListUpdater serverListUpdater) {
if (this.propertiesFactory.isSet(ILoadBalancer.class, name)) {
return this.propertiesFactory.get(ILoadBalancer.class, config, name);
}
//默认的负载均衡类
return new ZoneAwareLoadBalancer<>(config, rule, ping, serverList,
serverListFilter, serverListUpdater);
}
//DynamicServerListLoadBalancer
public DynamicServerListLoadBalancer(IClientConfig clientConfig, IRule rule, IPing ping,
ServerList<T> serverList, ServerListFilter<T> filter,
ServerListUpdater serverListUpdater) {
super(clientConfig, rule, ping);
this.serverListImpl = serverList;
this.filter = filter;
this.serverListUpdater = serverListUpdater;
if (filter instanceof AbstractServerListFilter) {
((AbstractServerListFilter) filter).setLoadBalancerStats(getLoadBalancerStats());
}
//初始化
restOfInit(clientConfig);
}
//方法
void restOfInit(IClientConfig clientConfig) {
boolean primeConnection = this.isEnablePrimingConnections();
// turn this off to avoid duplicated asynchronous priming done in BaseLoadBalancer.setServerList()
this.setEnablePrimingConnections(false);
enableAndInitLearnNewServersFeature();
updateListOfServers();
if (primeConnection && this.getPrimeConnections() != null) {
this.getPrimeConnections()
.primeConnections(getReachableServers());
}
this.setEnablePrimingConnections(primeConnection);
LOGGER.info("DynamicServerListLoadBalancer for client {} initialized: {}", clientConfig.getClientName(), this.toString());
}
负载均衡类:DynamicServerListLoadBalancer
//定时任务处理类
PollingServerListUpdater
@Override
public synchronized void start(final UpdateAction updateAction) {
if (isActive.compareAndSet(false, true)) {
final Runnable wrapperRunnable = new Runnable() {
@Override
public void run() {
if (!isActive.get()) {
if (scheduledFuture != null) {
scheduledFuture.cancel(true);
}
return;
}
try {
updateAction.doUpdate();
lastUpdated = System.currentTimeMillis();
} catch (Exception e) {
logger.warn("Failed one update cycle", e);
}
}
};
//默认时间 30秒 因此一个新的服务端上线后,客户端需要最多30秒才能进行使用
scheduledFuture = getRefreshExecutor().scheduleWithFixedDelay(
wrapperRunnable,
initialDelayMs,
refreshIntervalMs,
TimeUnit.MILLISECONDS
);
} else {
logger.info("Already active, no-op");
}
}
// 优化
#配置客户端拉取服务端 列表的时间间隔 便于快速获取服务端的上线 默认值30秒
ribbon:
ServerListRefreshInterval: 10000
2. Ribbon负载均衡策略
- RoundRobinRule(轮询策略,按照服务顺序依次循环调用) 默认
- WeightedResponseTimeRule(权重比策略,优先选择权重比高的服务,也就是服务响应时间比较短的,响应时间越长权重比越低)
- RandomRule(随机策略,服务提供者列表随机选择一个服务)
- BestAvailableRule(最小连接数策略,获取服务列表中连接数最小的服务实例)
- RetryRule(重试策略,重试获取已经失效的服务,指定时间没有获取到返回NULL)
- AvailabilityFilteringRule(可用性敏感策略,过滤非健康服务实例,选择lianji)
- ZoneAvoidanceRule(区域敏感策略)
3.具体的调用执行
//LoadBalancerFeignClient
@Override
public Response execute(Request request, Request.Options options) throws IOException {
try {
URI asUri = URI.create(request.url());
String clientName = asUri.getHost();
URI uriWithoutHost = cleanUrl(request.url(), clientName);
FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(
this.delegate, request, uriWithoutHost);
//获取配置 即初始化配置
IClientConfig requestConfig = getClientConfig(options, clientName);
//lbClient会创建client 此时才会加载服务端 每间隔30秒调用一次
return lbClient(clientName)
.executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();
}
catch (ClientException e) {
IOException io = findIOException(e);
if (io != null) {
throw io;
}
throw new RuntimeException(e);
}
}
4. Ribbon-eager-load(饥饿加载)模式
Ribbon 对于负载 Client 是在服务启动后,发生调用的时候才会去创建 Client,所以在第一次发生 http 请求调用的时候,不光要算上 http 的请求时间,还要算上 Client 的创建时间,所以第一次调用的时候才会很慢,具体内容参考第三点
Ribbon 的 DynamicServerListLoadBalancer 会将 feign 客户端进行负载,然后进行调用,第一次调用的时间就是会长一些,第二次调用直接进行请求可以看到调用时间很快。
5. 开启饥饿负载
ribbon:
nacos:
enabled: true # 开启naocos轮询
eager-load:
enabled: true # 开启Ribbon的饥饿加载模式(防止第一次请求超时的问题)
clients: Lxlxxx-system2 # 指定需要开启的服务(需要开启Ribbon的饥饿加载模式)
ReadTimeout: 10000
ConnectTimeout: 10000
MaxAutoRetries: 0
MaxAutoRetriesNextServer: 1
OkToRetryOnAllOperations: false
在项目启动的时候,可以从日志看到,已经把 Lxlxxx-system2 服务进行加载,从而避免了第一次请求超时的情况
其实这种饥饿加载模式,类似于“客户端负载预热”的一个操作,项目启动的时候进行加载,防止服务之间调用可以因为数据量、业务逻辑处理复杂性导致接口超时,如果你的服务之间调用业务处理比较复杂、且慢,不妨可以试试这种解决方式。