【ribbon】Ribbon的负载均衡和扩展功能

Ribbon的核心接口

参考:org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration

  • IClientConfig:Ribbon的客户端配置,默认采用DefaultClientConfigImpl实现。
  • IRule:Ribbon的负载均衡策略,默认采用ZoneAvoidanceRule实现,该策略能够在多区域环境下选出最佳区域的实例进行访问。
  • IPing:Ribbon的实例检查策略,默认采用DummyPing实现,该检查策略是一个特殊的实现,实际上它并不会检查实例是否可用,而是始终返回true,默认认为所有服务实例都是可用的。
  • ServerList:服务实例清单的维护机制,默认采用ConfigurationBasedServerList实现。
  • ServerListFilter:服务实例清单过滤机制,默认采ZonePreferenceServerListFilter,该策略能够优先过滤出与请求方处于同区域的服务实例。
  • ILoadBalancer:负载均衡器,默认采用ZoneAwareLoadBalancer实现,它具备了区域感知的能力。

Ribbon负载均衡策略

  • RandomRule:随机选择一个Server。
  • RetryRule:对选定的负载均衡策略机上重试机制,在一个配置时间段内当选择Server不成功,则一直尝试使用subRule的方式选择一个可用的server。
  • RoundRobinRule:轮询选择,轮询index,选择index对应位置的Server。
  • AvailabilityFilteringRule:过滤掉一直连接失败的被标记为circuit tripped的后端Server,并过滤掉那些高并发的后端Server或者使用一个AvailabilityPredicate来包含过滤server的逻辑,其实就是检查status里记录的各个Server的运行状态。
  • BestAvailableRule:选择一个最小的并发请求的Server,逐个考察Server,如果Server被tripped了,则跳过。
  • WeightedResponseTimeRule:根据响应时间加权,响应时间越长,权重越小,被选中的可能性越低。
  • ZoneAvoidanceRule:默认的负载均衡策略,即复合判断Server所在区域的性能和Server的可用性选择Server,在没有区域的环境下,类似于轮询
  • NacosRule: 优先调用同一集群的实例,基于随机权重

修改默认负载均衡策略

全局配置

全局配置:所有调用的微服务一律使用指定的负载均衡策略,只需要向容器中注入IRule实例即可。

package com.morris.user.config;import com.alibaba.cloud.nacos.ribbon.NacosRule;
import com.netflix.loadbalancer.IRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class RibbonConfig {@Beanpublic IRule ribbonRule() {// 指定使用Nacos提供的负载均衡策略(优先调用同一集群的实例,基于随机权重)return new NacosRule();}
}

局部配置

局部配置:可以在配置文件中调用指定微服务时,使用对应的负载均衡策略。

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

自定义负载均衡策略

通过实现IRule接口可以自定义负载策略,主要的选择服务逻辑在choose方法中。

实现基于Nacos权重的负载均衡策略:

package com.morris.user.config;import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.NacosServiceManager;
import com.alibaba.cloud.nacos.ribbon.NacosServer;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.DynamicServerListLoadBalancer;
import com.netflix.loadbalancer.Server;
import lombok.extern.slf4j.Slf4j;import javax.annotation.Resource;@Slf4j
public class NacosRandomWithWeightRule extends AbstractLoadBalancerRule {@Resourceprivate NacosServiceManager nacosServiceManager;@Resourceprivate NacosDiscoveryProperties nacosDiscoveryProperties;@Overridepublic Server choose(Object key) {DynamicServerListLoadBalancer loadBalancer = (DynamicServerListLoadBalancer) getLoadBalancer();String serviceName = loadBalancer.getName();NamingService namingService = nacosServiceManager.getNamingService(nacosDiscoveryProperties.getNacosProperties());try {//nacos基于权重的算法Instance instance = namingService.selectOneHealthyInstance(serviceName);return new NacosServer(instance);} catch (NacosException e) {log.error("获取服务实例异常:{}", e.getMessage());e.printStackTrace();}return null;}@Overridepublic void initWithNiwsConfig(IClientConfig iClientConfig) {}
}

可以将NacosRandomWithWeightRule按照上面的全局配置或者局部配置。

饥饿加载

在进行服务调用的时候,如果网络情况不好,第一次调用会超时。Ribbon默认懒加载,意味着只有在发起调用的时候才会创建客户端。

应用启动后第一次请求会有2s左右的延时,如果此时有大量请求进来就会抛出大量异常。

2023-07-21 16:50:37.718  INFO 20488 --- [nio-8030-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2023-07-21 16:50:37.718  INFO 20488 --- [nio-8030-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2023-07-21 16:50:37.733  INFO 20488 --- [nio-8030-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 15 ms
2023-07-21 16:50:39.332  INFO 20488 --- [nio-8030-exec-1] c.netflix.loadbalancer.BaseLoadBalancer  : Client: order-service instantiated a LoadBalancer: DynamicServerListLoadBalancer:{NFLoadBalancer:name=order-service,current list of Servers=[],Load balancer stats=Zone stats: {},Server stats: []}ServerList:null
2023-07-21 16:50:39.346  INFO 20488 --- [nio-8030-exec-1] c.n.l.DynamicServerListLoadBalancer      : Using serverListUpdater PollingServerListUpdater
2023-07-21 16:50:39.534  INFO 20488 --- [nio-8030-exec-1] c.n.l.DynamicServerListLoadBalancer      : DynamicServerListLoadBalancer for client order-service initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=order-service,current list of Servers=[2.0.0.1:8020],Load balancer stats=Zone stats: {unknown=[Zone:unknown;	Instance count:1;	Active connections count: 0;	Circuit breaker tripped count: 0;	Active connections per server: 0.0;]
},Server stats: [[Server:2.0.0.1:8020;	Zone:UNKNOWN;	Total Requests:0;	Successive connection failure:0;	Total blackout seconds:0;	Last connection made:Thu Jan 01 08:00:00 CST 1970;	First connection made: Thu Jan 01 08:00:00 CST 1970;	Active Connections:0;	total failure count in last (1000) msecs:0;	average resp time:0.0;	90 percentile resp time:0.0;	95 percentile resp time:0.0;	min resp time:0.0;	max resp time:0.0;	stddev resp time:0.0]
]}ServerList:com.alibaba.cloud.nacos.ribbon.NacosServerList@fb090e

开启饥饿加载,解决第一次调用慢的问题:

ribbon:eager-load:enabled: trueclients: order-service

参数说明:

  • ribbon.eager-load.enabled:开启ribbon的饥饿加载模式
  • ribbon.eager-load.clients:指定需要饥饿加载的服务名,也就是你需要调用的服务,如果有多个服务,则用逗号隔开

看下效果,在启动过程中就已经初始化连接了:

2023-07-21 16:54:18.480  INFO 22040 --- [           main] com.morris.user.UserServiceApplication   : Started UserServiceApplication in 6.846 seconds (JVM running for 8.265)
2023-07-21 16:54:19.254  INFO 22040 --- [           main] c.netflix.loadbalancer.BaseLoadBalancer  : Client: order-service instantiated a LoadBalancer: DynamicServerListLoadBalancer:{NFLoadBalancer:name=order-service,current list of Servers=[],Load balancer stats=Zone stats: {},Server stats: []}ServerList:null
2023-07-21 16:54:19.260  INFO 22040 --- [           main] c.n.l.DynamicServerListLoadBalancer      : Using serverListUpdater PollingServerListUpdater
2023-07-21 16:54:19.286  INFO 22040 --- [           main] c.n.l.DynamicServerListLoadBalancer      : DynamicServerListLoadBalancer for client order-service initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=order-service,current list of Servers=[2.0.0.1:8020],Load balancer stats=Zone stats: {unknown=[Zone:unknown;	Instance count:1;	Active connections count: 0;	Circuit breaker tripped count: 0;	Active connections per server: 0.0;]
},Server stats: [[Server:2.0.0.1:8020;	Zone:UNKNOWN;	Total Requests:0;	Successive connection failure:0;	Total blackout seconds:0;	Last connection made:Thu Jan 01 08:00:00 CST 1970;	First connection made: Thu Jan 01 08:00:00 CST 1970;	Active Connections:0;	total failure count in last (1000) msecs:0;	average resp time:0.0;	90 percentile resp time:0.0;	95 percentile resp time:0.0;	min resp time:0.0;	max resp time:0.0;	stddev resp time:0.0]
]}ServerList:com.alibaba.cloud.nacos.ribbon.NacosServerList@432469

NacosRule源码分析

com.alibaba.cloud.nacos.ribbon.NacosRule#choose

@Override
public Server choose(Object key) {try {String clusterName = this.nacosDiscoveryProperties.getClusterName();String group = this.nacosDiscoveryProperties.getGroup();DynamicServerListLoadBalancer loadBalancer = (DynamicServerListLoadBalancer) getLoadBalancer();String name = loadBalancer.getName();// 获取NamingService,在我们的代码中也可以这么使用NamingService namingService = nacosServiceManager.getNamingService(nacosDiscoveryProperties.getNacosProperties());// 筛选出同一个group的实例,不同group之间不能通讯List<Instance> instances = namingService.selectInstances(name, group, true);if (CollectionUtils.isEmpty(instances)) {LOGGER.warn("no instance in service {}", name);return null;}List<Instance> instancesToChoose = instances;if (StringUtils.isNotBlank(clusterName)) {// 找出同一个集群的节点List<Instance> sameClusterInstances = instances.stream().filter(instance -> Objects.equals(clusterName,instance.getClusterName())).collect(Collectors.toList());if (!CollectionUtils.isEmpty(sameClusterInstances)) {instancesToChoose = sameClusterInstances;}else {LOGGER.warn("A cross-cluster call occurs,name = {}, clusterName = {}, instance = {}",name, clusterName, instances);}}// 带权重的随机选择一个节点Instance instance = ExtendBalancer.getHostByRandomWeight2(instancesToChoose);return new NacosServer(instance);}catch (Exception e) {LOGGER.warn("NacosRule error", e);return null;}
}

NacosRule是AlibabaNacos自己实现的一个负载均衡策略,可以在nacos平台中根据自定义权重进行访问。

基于Nacos元数据的版本控制自定义负载均衡

实际项目,我们可能还会有这样的需求:

一个微服务在线上可能多版本共存,且多个版本的微服务并不兼容。使用Nacos的自定义元数据,可以实现微服务的版本控制。

配置文件的格式: spring.cloud.nacos.discovery.metadata.{key}={value}

当前配置的版本: spring.cloud.nacos.discovery.metadata.version=V1

package com.morris.user.config;import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.NacosServiceManager;
import com.alibaba.cloud.nacos.ribbon.ExtendBalancer;
import com.alibaba.cloud.nacos.ribbon.NacosServer;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.Server;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;import javax.annotation.Resource;
import java.util.List;
import java.util.stream.Collectors;@Slf4j
public class ClusterMetaDataRibbonRule extends AbstractLoadBalancerRule {@Resourceprivate NacosDiscoveryProperties nacosDiscoveryProperties;@Resourceprivate NacosServiceManager nacosServiceManager;@Overridepublic void initWithNiwsConfig(IClientConfig iClientConfig) {}@Overridepublic Server choose(Object o) {log.info("-------key: {}", o);// 获取当前服务的集群名称String currentClusterName = nacosDiscoveryProperties.getClusterName();// 获取当前版本String currentVersion = nacosDiscoveryProperties.getMetadata().get("version");// 获取被调用的服务的名称BaseLoadBalancer baseLoadBalancer = (BaseLoadBalancer) getLoadBalancer();String serviceName = baseLoadBalancer.getName();// 获取nacos clinet的服务注册发现组件的apiNamingService namingService = nacosServiceManager.getNamingService(nacosDiscoveryProperties.getNacosProperties());try {// 获取所有被调用服务List<Instance> allInstances = namingService.getAllInstances(serviceName);// 过滤出相同版本且相同集群下的所有服务List<Instance> sameVersionAndClusterInstances = allInstances.stream().filter(x -> StringUtils.equalsIgnoreCase(x.getMetadata().get("version"), currentVersion)&& StringUtils.equalsIgnoreCase(x.getClusterName(), currentClusterName)).collect(Collectors.toList());Instance chooseInstance;if(sameVersionAndClusterInstances.isEmpty()) {// 过滤出所有相同版本的服务List<Instance> sameVersionInstances = allInstances.stream().filter(x -> StringUtils.equalsIgnoreCase(x.getMetadata().get("version"), currentVersion)).collect(Collectors.toList());if(sameVersionInstances.isEmpty()) {log.info("跨集群调用找不到对应合适的版本当前版本为:currentVersion:{}",currentVersion);throw new RuntimeException("找不到相同版本的微服务实例");}else {// 随机权重chooseInstance = ExtendBalancer.getHostByRandomWeight2(sameVersionInstances);log.info("跨集群同版本调用--->当前微服务所在集群:{},被调用微服务所在集群:{},当前微服务的版本:{},被调用微服务版本:{},Host:{},Port:{}",currentClusterName, chooseInstance.getClusterName(), chooseInstance.getMetadata().get("current-version"),chooseInstance.getMetadata().get("current-version"), chooseInstance.getIp(), chooseInstance.getPort());}}else {chooseInstance = ExtendBalancer.getHostByRandomWeight2(sameVersionAndClusterInstances);log.info("同集群同版本调用--->当前微服务所在集群:{},被调用微服务所在集群:{},当前微服务的版本:{},被调用微服务版本:{},Host:{},Port:{}",currentClusterName, chooseInstance.getClusterName(), chooseInstance.getMetadata().get("version"),chooseInstance.getMetadata().get("current-version"), chooseInstance.getIp(), chooseInstance.getPort());}return new NacosServer(chooseInstance);} catch (NacosException e) {log.error("error,", e);return null;}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/7503.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

iPortal 注册登录模块扩展开发

作者&#xff1a;yx 文章目录 前言一、示例代码简介二、对接 iPortal REST API 接口2.1、登录模块扩展开发2.2、注册模块扩展开发 三、页面内容及样式实现四、配置启用定制页面 前言 针对注册登录模块&#xff0c;iPortal 允许用户通过 iFrame 方式接入自行开发的页面&#xf…

经典面试题(力扣,接雨水)

接雨水 方法一思路测试代码复杂度测试结果 方法二思路测试代码复杂度测试结果 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 示例1&#xff1a; 输入&#xff1a;height [0,1,0,2,1,0,1,3,2,1,2,1]…

[JAVAee]线程安全

目录 线程安全的理解 线程不安全的原因 ①非原子性 ②可见性 ③代码重排序 体会何为不安全的线程 保证线程安全 一个代码在多线程的环境下就很容易出现错误. 线程安全的理解 线程安全是什么呢?通俗的来讲,线程安全就是在多线程的环境下,代码的结果是符合我们预期的,就…

MFC第二十一天 CS架构多页面开发与数据交互、CImageList图像列表介绍 、CListCtrl-SetItem设置列表项的方法

文章目录 CImageList图像列表介绍CListCtrl图标的原理CListCtrl列表图标设置CListCtrl-SetItem设置列表项的方法 CS架构多页面开发与数据交互添加用户实现向导多页数据交互pch.hCLientXq.h CAppCPage1.hCPage1.cppCPage2.hCPage2.cppCWorkerDlg .hCWorkerDlg.cpp 多页数据修改C…

【高危】Atlassian Confluence 远程代码执行漏洞

漏洞描述 Confluence 是由 Atlassian 开发的知识管理与协同软件&#xff0c;通常在企业内部用作wiki系统。 Confluence 7.19.8到8.2.0之前的版本中存在远程代码执行漏洞&#xff0c;具有登录权限的攻击者无需用户交互即可在 Confluence 服务器中执行任意命令。 漏洞名称Atlass…

【Boost搜索引擎项目】

文章目录 一、项目流程二、项目展示 一、项目流程 1.编写数据去标签模块–parser.cc 将去标签之后干净文档以title\3content\3url\ntitle\3content\3url\n格式放入同一文件中。 2.建立索引模块–index.hpp 读取处理好的行文本文件进行分词、权重计算等操作&#xff0c;在内存中…

【云驻共创】CodeArts Repo ---高效代码协同开发之旅

目录 一、代码托管发展史 1.1 第一代 1.2 第二代 1.3 第三代 二、CodeArts Repo 介绍 二、CodeArts Repo 功能架构 2.1 研发协同 2.2 代码管理功能 2.3 代码存储特性 三、CodeArts Repo 技术能力 三、华为云代码托管技术发展历程 四、CodeAr…

ubuntu挂载ext4文件系统

文章目录 1.虚拟机分配10G磁盘用来挂载ext4文件系统2.磁盘分区3.创建文件系统4.挂载文件系统5.卸载文件系统6.使用ior测试ext4三种日志模式&#xff08;1&#xff09;ordered&#xff08;2&#xff09;journal&#xff08;3&#xff09;writeback 1.虚拟机分配10G磁盘用来挂载e…

SpringBoot项目修改Tomcat版本号

SpringBoot项目修改Tomcat版本号 前言如果项目是以jar包形式打包部署如果项目是以war包形式打包部署示例 仰天大笑出门去&#xff0c;我辈岂是蓬蒿人 前言 Springboot项目,默认是使用内嵌Tomcat servlet容器形式打包部署。关于怎么修改默认的版本号&#xff0c;捣鼓了好久终于…

PostgreSQL 设置时区,时间/日期函数汇总

文章目录 前言查看时区修改时区时间/日期操作符和函数时间/日期操作符日期/时间函数&#xff1a;extract&#xff0c;date_part函数支持的field 数据类型格式化函数用于日期/时间格式化的模式&#xff1a; 扩展 前言 本文基于 PostgreSQL 12.6 版本&#xff0c;不同版本的函数…

基于STM32设计的人体健康监护系统(华为云IOT)

一、设计需求 1.1 设计需求总结 根据需求,要求设计一款基于 STM32 的人体健康监护系统。采用系统模块化思路进行,将多个数模传感器收集到的数据和操作指令一并送至 STM32 中心处理器进行处理分析。 该系统可以实时监测被测者的心率、体温以及周围环境的温度,也同时可以通…

(vue)vue项目中引入外部字体

(vue)vue项目中引入外部字体 效果&#xff1a; 第一步 放置字体包&#xff0c;在assets下创建一个fonts文件夹&#xff0c;放入下载的字体文件 第二步 创建一个font.css文件用于定义这个字体包的名字 第三步 在App.vue的css中将这个css文件引入 第四步 页面使用 font-famil…

Qt : day1

1.聊天界面 #include "widget.h"Widget::Widget(QWidget *parent): QWidget(parent) {qDebug() << this->size(); //获取当前页面尺寸this->setFixedSize(500, 600); //设置固定尺寸this->setWindowTitle("聊天框"); //设置窗口…

单Bank OTA升级:STM32G071 APP (二)

接上一篇文章&#xff1a;单Bank OTA升级&#xff1a;STM32G071 BootLoader (一)&#xff1a;跳转链接 什么是单Bank升级&#xff1a;将Flash划分为以下3个区域。 BootLoader区&#xff1a;程序进行升级的引导程序&#xff0c;根据Upade_Flag来判断跳转Bank区运行程序或是接收…

安卓开发后台应用周期循环获取位置信息上报服务器

问题背景 最近有需求&#xff0c;在APP启动后&#xff0c;退到后台&#xff0c;还要能实现周期获取位置信息上报服务器&#xff0c;研究了一下实现方案。 问题分析 一、APP退到后台后网络请求实现 APP退到后台后&#xff0c;实现周期循环发送网络请求。目前尝试了两种方案是…

Spring 中一个少见的引介增强 IntroductionAdvisor

我们平时做 AOP 开发的时候&#xff0c;基本上都是增强某一个方法&#xff0c;在某一个方法执行之前或者执行之后做一些事情&#xff0c;这种叫做 PointcutAdvisor&#xff0c;实际上&#xff0c;Spring 中的 Advisor 大致可以分为两种类型&#xff0c;除了 PointcutAdvisor 之…

Openlayers实战,Openlayers实现一个车辆转向运动轨迹动画,支持根据轨迹运动方向自动改变车头转向角度,无需定时器,丝滑小车转向运动效果

专栏目录: OpenLayers入门教程汇总目录 前言 本章作为OpenLayers入门文章《OpenLayers入门,OpenLayers动画效果实现,OpenLayers实现轨迹运动动画》的的增强进阶篇章,实现了入门教程中没有实现的小车车头方向根据运动方向自动转向的功能。 本章代码不使用任何setTimeout和…

金融中的数学:贝叶斯公式

1.贝叶斯定理 贝叶斯定理是概率论中的一项重要定理&#xff0c;用于在已知某一事件的条件下&#xff0c;求另一事件发生的概率。它是根据条件概率推导出来的&#xff0c;得名于英国数学家托马斯贝叶斯。 贝叶斯定理可以表示为&#xff1a; 这个式子就是贝叶斯公式&#xff0c…

解决FLink:Missing required options are: slot.name

[ERROR] Could not execute SQL statement. Reason: org.apache.flink.table.api.ValidationException: One or more required options are missing.Missing required options are:slot.name解决 https://ververica.github.io/flink-cdc-connectors/release-2.4/content/connec…

提升 API 可靠性的五种方法

API 在我们的数字世界中发挥着关键的作用&#xff0c;使各种不同的应用能够相互通信。然而&#xff0c;这些 API 的可靠性是保证依赖它们的应用程序功能正常、性能稳定的关键因素。本文&#xff0c;我们将探讨提高 API 可靠性的五种主要策略。 1.全面测试 要确保 API 的可靠性…