深入理解微服务中的负载均衡算法与配置策略

server/2024/10/19 14:59:50/

负载均衡算法

我们首先来探讨一下默认情况下Ribbon使用的负载均衡算法。有些人可能会说它使用轮询算法,因为在本地测试时,我们经常会看到轮询的效果。然而,简单地依赖这种表面的观察来回答面试题是有风险的。实际上,忽略了深入理解源代码可能会导致严重的误解。

尽管实践是增长知识的一部分,但是在真实的生产环境中,尤其是跨多个数据中心部署的情况下,我们无法简单地将问题简化为本地集群的测试环境。

获取服务器ip

我们接着上一篇内容,讨论如何选择服务器的步骤如下复述:

    public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)throws IOException {ILoadBalancer loadBalancer = getLoadBalancer(serviceId);Server server = getServer(loadBalancer, hint);if (server == null) {throw new IllegalStateException("No instances available for " + serviceId);}RibbonServer ribbonServer = new RibbonServer(serviceId, server,isSecure(server, serviceId),serverIntrospector(serviceId).getMetadata(server));return execute(serviceId, ribbonServer, request);}

获取负载均衡器——ZoneAwareLoadBalancer

我们来看看getServer方法,突然间出现这么多负载均衡器,应该怎么处理呢?这时候最好的方法就是查看自动配置,看看哪些被注入进来了。

image

中间步骤大家就不用再找了,我已经事先找好了,就在这里:

image

这张图包含两个关键信息:首先是注入了一个IRule规则,其次是将该IRule规则应用到了ZoneAwareLoadBalancer负载均衡器中。好的,现在我们清楚了接下来的步骤。接下来我们继续查看

    public Server chooseServer(Object key) {if (!ENABLED.get() || getLoadBalancerStats().getAvailableZones().size() <= 1) {logger.debug("Zone aware logic disabled or there is only one zone");return super.chooseServer(key);}Server server = null;try {//省略多余代码Set<String> availableZones = ZoneAvoidanceRule.getAvailableZones(zoneSnapshot, triggeringLoad.get(), triggeringBlackoutPercentage.get());logger.debug("Available zones: {}", availableZones);if (availableZones != null &&  availableZones.size() < zoneSnapshot.keySet().size()) {String zone = ZoneAvoidanceRule.randomChooseZone(zoneSnapshot, availableZones);logger.debug("Zone chosen: {}", zone);if (zone != null) {BaseLoadBalancer zoneLoadBalancer = getLoadBalancer(zone);server = zoneLoadBalancer.chooseServer(key);}}} catch (Exception e) {logger.error("Error choosing server using zone aware logic for load balancer={}", name, e);}//省略多余代码}

如果是在我们本地环境,通常会执行第一个if分支;但如果是在生产环境并配置了多个区域,那么会执行下面的分支。让我们一起来看看。

无配置区域情况

让我们来看看第一种情况,即如果没有区域或者只有一个区域,负载均衡规则是如何应用的。我们将查看父类负载均衡器BaseLoadBalancer的代码。

public class BaseLoadBalancer extends AbstractLoadBalancer implementsPrimeConnections.PrimeConnectionListener, IClientConfigAware {   private final static IRule DEFAULT_RULE = new RoundRobinRule();protected IRule rule = DEFAULT_RULE;public Server chooseServer(Object key) {if (counter == null) {counter = createCounter();}counter.increment();if (rule == null) {return null;} else {try {return rule.choose(key);} catch (Exception e) {logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", name, key, e);return null;}}}void initWithConfig(IClientConfig clientConfig, IRule rule, IPing ping, LoadBalancerStats stats) {// 省略部分代码setRule(rule);// 省略部分代码}
}

这里可以看到是有默认的IRule规则的——RoundRobinRule,但是别冲动,因为我们Spring自动托管的IRule规则还没用上,不可能这么简单的走轮训。我们可以看到这里是有设置的地方的。我也抓出来了。

最后让我们再来看看我们的ZoneAwareLoadBalancer生成构造器,因为在注入时我们是会带入规则的。以下是相关的代码示例:

    public ZoneAwareLoadBalancer(IClientConfig clientConfig, IRule rule,IPing ping, ServerList<T> serverList, ServerListFilter<T> filter,ServerListUpdater serverListUpdater) {super(clientConfig, rule, ping, serverList, filter, serverListUpdater);}

在这里,当super父类构造器执行完毕后,最终会调用BaseLoadBalancer类的initWithConfig方法。我没有一一追踪下去,但最后ZoneAvoidanceRule的负载均衡代码也相当复杂。不过,你可以将其理解为在没有区域的情况下类似于轮询。

配置多区域情况

在这个阶段,程序将会执行第二个分支,实际上,主要的代码如下所示:

String zone = ZoneAvoidanceRule.randomChooseZone(zoneSnapshot, availableZones);
if (zone != null) {BaseLoadBalancer zoneLoadBalancer = getLoadBalancer(zone);server = zoneLoadBalancer.chooseServer(key);
}

目的仍然是选择一个服务器,但是限定在当前区域内。关于这部分的详细讨论略去,因为接下来的方法都是关于ZoneAvoidanceRule的负载均衡算法代码。

如何配置其他算法

在这种情况下,如果我想使用其他负载均衡算法而不是当前的算法,应该如何配置呢?实际上,可以查看注入的源代码,有两种方法可以实现这一点。首先,可以通过在配置类中添加一个配置项来指定所需的负载均衡算法

if (this.propertiesFactory.isSet(IRule.class, name)) {return this.propertiesFactory.get(IRule.class, config, name);
}

局部配置

在这里可以看到我们也是通过配置文件来进行配置的,不过配置文件的方式使我们能够进行局部微服务负载均衡的选择。让我们先来看一下源代码:

    public PropertiesFactory() {classToProperty.put(ILoadBalancer.class, "NFLoadBalancerClassName");classToProperty.put(IPing.class, "NFLoadBalancerPingClassName");classToProperty.put(IRule.class, "NFLoadBalancerRuleClassName");classToProperty.put(ServerList.class, "NIWSServerListClassName");classToProperty.put(ServerListFilter.class, "NIWSServerListFilterClassName");}public boolean isSet(Class clazz, String name) {return StringUtils.hasText(getClassName(clazz, name));}

在调用特定的微服务时,可以根据需要使用相应的负载均衡策略来配置 application.yml 文件。

#被调用的微服务名
mall‐order:ribbon:#指定使用Nacos提供的负载均衡策略(优先调用同一集群的实例,基于随机&权重)NFLoadBalancerRuleClassName:com.alibaba.cloud.nacos.ribbon.NacosRule

全局配置

在全局情况下更为简单,可以观察到在自动注入时使用了 @ConditionalOnMissingBean 注解。如果我们在Spring中手动加载了相应的bean,那么这个注解就不会生效了。

    @Beanpublic IRule ribbonRule() {// 指定使用Nacos提供的负载均衡策略(优先调用同一集群的实例,基于随机权重)return new NacosRule();}

相当简单了,那么这样的的话,其实我们也可以进行自定义一个策略的。毕竟照先有的抄下固定实现方法后,自己在实现方法内写上自己的业务逻辑不就完了。

自定义策略

看起来,对于实现其他的负载均衡算法策略,有几个关键点。首先,需要继承 AbstractLoadBalancerRule 父类,并且实现其抽象方法。接下来,我们可以开始编写我们的实现代码:

@Slf4j
public class XiaoYuRandomWithWeightRule extends AbstractLoadBalancerRule { @Overridepublic Server choose(Object key) {//这里实现自己的逻辑即可return server;}@Overridepublic void initWithNiwsConfig(IClientConfig clientConfig) {}
}

OK,剩下的就按照局部配置或者全局配置下,让我们的规则生效即可。

在这里只讲述了算法规则的配置和自定义方法,实际上负载均衡器的操作也是类似的套路。这里就不重复演示了。

总结

今天,我们主要补充了上一章关于微服务通信的内容,并深入探讨了负载均衡算法的重要性。我们首先详细讨论了Ribbon默认使用的负载均衡算法。尽管在本地测试时可能会观察到轮询的效果,但简单依赖这种表面的观察是不够的。在真实的生产环境中,特别是在跨多个数据中心部署时,负载均衡策略的选择需要更加深入的理解和分析。

我们进一步分析了如何通过配置和自定义负载均衡规则来灵活应对各种场景。不论是局部配置还是全局配置,我们都能根据具体需求调整负载均衡的行为。同时,我们展示了如何通过自定义算法扩展Ribbon的负载均衡能力,以更好地适应特定业务场景的需求。

在接下来的章节中,我们将深入探讨OpenFeign组件。我们的重点将是如何使开发人员能够更多关注业务逻辑代码,而不是被迫处理与RPC调用相关的繁琐细节。

文章转载自:努力的小雨

原文链接:https://www.cnblogs.com/guoxiaoyu/p/18347224

体验地址:引迈 - JNPF快速开发平台_低代码开发平台_零代码开发平台_流程设计器_表单引擎_工作流引擎_软件架构


http://www.ppmy.cn/server/108237.html

相关文章

Furion+SqlSugar环境配置与项目创建

一、速通一图流 二、安装 .NET 8 1. 下载与安装 .NET 8 SDK 访问 .NET 下载页面 并下载最新版本的 .NET 8 SDK。根据操作系统选择适合的安装包&#xff08;如 Windows、macOS 或 Linux&#xff09;&#xff0c;并按照提示完成安装。 2. 验证安装 打开终端或命令提示符&…

react-native框架下,集成字体并应用全局

一、存放字体文件 将自定义字体文件&#xff08;例如 .ttf 或 .otf 文件&#xff09;放入项目的 assets/fonts 目录中。如果没有这个目录&#xff0c;可以手动创建。 二、配置字体 在项目根目录下建一个文件&#xff1a;react-native.config.js&#xff0c;文件内容如下&…

【数学建模】层次分析法

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。前言 – 人工智能教程 在数学建模问题求解中什么时候用到层次分析法 在数学建模问题求解中&#xff0c;层次分析法&#xff08;Analytic…

速盾:企业在使用高防 IP 和 CDN 时如何确保数据的安全性?

企业在使用高防 IP&#xff08;Internet Protocol&#xff09;和 CDN&#xff08;Content Delivery Network&#xff09;时&#xff0c;需要采取一系列措施来确保数据的安全性。以下是一些建议&#xff1a; 使用高级防护技术&#xff1a;高防 IP 和 CDN 提供了一系列的安全功能…

CentOS全面停服,国产化提速,央国企信创即时通讯/协同门户如何选型?

01. CentOS停服带来安全新风险&#xff0c; 国产操作系统迎来新的发展机遇 2024年6月30日&#xff0c;CentOS 7版本全面停服&#xff0c;于2014年发布的开源类服务器操作系统——CentOS全系列版本生命周期画上了句号。国内大量基于CentOS开发和适配的服务器及平台&#xff0c…

python源码:基于fastapi+websocket双向信息通道的简易网页聊天室

前言 由于我的另一个想法&#xff0c;我需要使用双向通信&#xff0c;并最终选择了fastapi模块中的WebSocket方法来实现这个目的。 为了能够尽快掌握它&#xff0c;我设计了这个《基于fastapiwebsocket双向信息通道的简易网页聊天室》&#xff0c;并且具备以下功能&#xff1a;…

全能型AI vs 专业型AI:未来是草莓味的AI吗?

草莓&#xff1a;全能型AI的新宠儿&#xff1f; 根据最近的消息&#xff0c;OpenAI的“草莓”模型据说是一个全能型AI&#xff0c;无论是解数学题还是搞定主观营销策略&#xff0c;它都能轻松驾驭。这个AI不仅仅是能解决问题&#xff0c;更是能够跨越多个领域&#xff0c;展现出…

BH1750光照传感器详解(STM32)

目录 一、介绍 二、传感器原理 1.原理图 2.工作原理&#xff1a;结构框图 三、程序设计 main.c文件 bh1750.h文件 bh1750.c文件 四、实验效果 五、资料获取 项目分享 一、介绍 BH1750是一款数字型光照强度传感器&#xff0c;能够获取周围环境的光照强度。内置16bitAD转…