【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…

macOS 下安装brew、nvm

1、brew&#xff1a; /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)" brew -v 查看版本 示例&#xff1a; 安装jdk brew search jdk 查询可用的jdk版本 brew install openjdk11 安装制定版本jdk 更换源&#xff1…

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

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

vue之vue-base组件

功能描述 业务页面基类组件,提供给业务开发的继承基类 #方法 初始化方法 init(); 校验表单 validateForm(); 事件绑定,可覆盖原有事件 bindEvent(event, bindfunction, target); 解绑事件 unbindEvent(event); 跳转路径 link(linkUrl); 参数输入,将全局管道中 (session\…

设计模式之迭代器模式

写在前面 本文一起看下一种行为型设计模式&#xff0c;迭代器模式。 1&#xff1a;介绍 1.1&#xff1a;什么时候使用迭代器设计模式 当对象的内部有一批数据&#xff0c;希望能够对外暴露访问&#xff0c;但因为如下的原因不希望外部直接访问内部的具体存储数据的元素&…

[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…

微服务之配置中心

什么是配置中心 配置&#xff1a;就是springboot中的application.yml/properties文件 比如&#xff1a;项目名、端口号、数据库连接参数、启动参数等。 配置中心&#xff1a;配置中心就是用来管理项目当中所有配置的系统&#xff0c;也是微服务系统当中不可或缺的一部分。 只需…

C++---string模拟实现

string模拟实现 构造函数和析构函数begin和endreserve和resizepush_back和appendc_strempty&#xff0c;size&#xff0c;capacity&#xff0c;clear拷贝构造和赋值和比较大小[]重载insert和erasefind查找 前面我们已经对string进行了简单的介绍&#xff0c;只要会用各个函数即…

【Boost搜索引擎项目】

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

war包读取properties配置文件错误,返回null

现象&#xff1a;使用intellij Idea编写java项目&#xff0c;debug调试时&#xff0c;使用redis.properties文件的类A&#xff0c;A的代码可以使用 “InputStream in RedisUtils.class.getClass().getResourceAsStream("/" proPath);” 方式正常读取redis.propert…

【云驻共创】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区运行程序或是接收…