【SpringCloud】-Ribbon负载均衡

news2024/11/25 18:40:11

一、背景介绍

项目中使用到的SpringCloud Alibaba这一套微服务架构中服务注册与发现Nacos兼容了Feign,而Feign默认集成了Ribbon,当Nacos下使用Feign默认实现了负载均衡的效果。即使是默认集成了,也要追根溯源。


二、过程

负载均衡是什么?

将请求分摊到多个服务器上去执行

为什么要负载均衡?

分担压力,当开发的应用同时被成千上万,甚至更多用户同时访问的时候,并发问题就出现了,如果所有的请求都使用同一台机器,可能这个机器无法承受同时的高并发,这时候就可以将大量请求分发给不同的机器,应用的处理性能(吞吐量、网络处理能力)就需要得到提高
故障转移,实现高可用。如果某台服务器坏了(机器停机、进程异常等),其他服务器可以提供相同服务,顶替上岗
安全防护。可以实现过滤,如黑白名单处理等

一个请求是如何到达服务后端的?

在这里插入图片描述

在这里插入图片描述

①、Nginx:通过在前端接收到来自客户端的请求,将请求分发到后端服务器,实现负载均衡分发HTTP请求
假设我现在在浏览器访问【**https://internetbar.tech/root/user-service/api/login**】这个地址,通过HTTP请求到达【internetbar.tech】这个服务器的【Nginx】,Nginx接收到请求后根据请求的路径,在默认配置文件【nginx.conf】中找到对应的配置。 Nginx的配置文件中会有对GateWay的代理配置。

在这里插入图片描述

在http字段中配置的upstream api模块中定义了需要反向代理的服务器地址及端口,将要进行负载均衡的时候就会从这两个服务器进行选择。根据请求路径中的/root/进行匹配发现配置了proxy_pass模块,结合配置的负载均衡策略—8080端口的权重大于6688的权重,Nginx优先将请求转发到8080这个端口对应的服务器地址,在将请求转发给8080这个服务之前,Nginx还可以进行一些预处理操作,比方说请求的重定向、添加请求头等等

②、GateWay:路由转发、负载均衡
此时,8080端口的Gateway接收到通过Nginx代理的请求,GateWay与Nacos集成,并且GateWay配置的各个微服务的信息都通过Nacos做了服务配置。

请求到达GateWay服务的过滤器后,根据配置文件中预先定义的路由规则:

在这里插入图片描述

发现请求的Url中包含/user-service/,满足配置的断言规则,GateWay确定请求要转发到的目标服务是【internetbar-provider-user】。GateWay定时从Nacos注册中心拉取服务列表(Nacos中记录了服务名、ip地址、端口号等服务实例信息)动态地获取可用的服务实例,拉取到列表后发现此时【internetbar-provider-user】服务有多个实例。

③、Ribbon:GateWay与Ribbon来实现对服务的负载均衡
在转发请求时,GateWay会利用Ribbon进行负载均衡,利用Ribbon提供的负载均衡算法(默认是使用轮询算法,可以自定义策略)选择一个健康的服务实例来处理请求


根据负载均衡发生的位置不同分为两类

①、服务端负载均衡:发生在服务提供者一方,如nginx负载均衡

客户端发送请求到达Nginx,Nginx通过负载均衡策略选择其中的一个服务地址进行访问,此时Nginx作为了服务端

在这里插入图片描述

②、客户端负载均衡:发生在服务请求一方

在图中GateWay作为了客户端,通过集成ribbon根据负载均衡策略,在发送请求前通过负载均衡策略选择目标服务器,再将请求分发到不同的服务端。此时发送请求的GateWay作为客户端
在这里插入图片描述


Ribbon是什么?

是基于Netflix Ribbon实现的一套客户端负载均衡的工具。是Spring Cloud的一个组件,通过提供使用提供的注解我们就能自动化的实现负载均衡

Ribbon的作用是什么?

实现负载均衡和服务调用

如何使用Ribbon?

添加@LoadBalance注解

package cn.itcast.order;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
public class OrderApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }

    /**
     * @Description: 将RestTemplate注册到容器中
     * @Author: denglimei
     * @Date: 2023/3/13 11:28
     * @return: org.springframework.web.client.RestTemplate
     **/
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

此时我们通过浏览器进行四次访问,我们看看结果:
在这里插入图片描述

说明调用成功,此时我们看看idea中的四次访问分别是怎么样的一个顺序呢?
在这里插入图片描述

根据每次对userservice这个服务的访问来看,是采用了轮询的策略。内部到底如何进行负载均衡的?策略有哪些?是否默认采用用轮询,我们来看看源码如何说的!


Ribbon负载均衡的策略由哪些?

策略名策略描述实现说明
BestAvailableRule选择一个最小的并发请求的server逐个考察Server,如果Server被tripped了,则忽略,在选择其中ActiveRequestsCount最小的server
AvailabilityFilteringRule过滤掉因为连接失败被标记为circuit tripped的后端server,和高并发的后端server(active connections超过配置的阈值)使用一个AvailabilityPredicate来包含过滤server的逻辑,检查status里记录的各个server的运行状 态
WeightedResponseTimeRule根据相应时间分配一个weight,相应时间越长,weight越小,被选中的可能性越低一个后台线程定期的从status里面读取评价响应时间,为每个server计算一个weight。Weight的计算也比较简单,responsetime减去每个server自己平均的responsetime是server的权重。当刚开始运行,没有形成statas时,使用RoundRobinRule策略选择server。
RetryRule对选定的负载均衡策略进行重试机制在一个配置时间段内当选择server不成功,则一直尝试使用subRule的方式选择一个可用的server
RoundRobinRule轮询方式轮询选择server轮询index,选择index对应位置的server
RandomRule随机选择一个server在index上随机,选择index对应位置的server
ZoneAvoidanceRule复合判断server所在区域的性能和server的可用性选择server使用ZoneAvoidancePredicate和AvailabilityPredicate来判断是否选择某个server,前一个判断判定一个zone的运行性能是否可用,剔除不可用的zone(的所有server),AvailabilityPredicate用于过滤掉连接数过多的Server。

Ribbon自定义负载均衡策略

方式一:代码方式,定义随机策略

package cn.itcast.order;

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
public class OrderApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }

    /**
     * @Description: 将RestTemplate注册到容器中
     * @Author: denglimei
     * @Date: 2023/3/13 11:28
     * @return: org.springframework.web.client.RestTemplate
     **/
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    /**
     * @Description: 修改负载均衡策略方式
     * @Author: denglimei
     * @Date: 2023/3/13 19:56
     * @return: com.netflix.loadbalancer.IRule
     **/
    @Bean
    public IRule randomRule() {
        return new RandomRule();
    }
}

方式二:配置文件方式

userservice:
  ribbon:
    MFLoadBalancerRuleClassName: com.netlix.loadbalancer.RandomRule #负载均衡规则

不同:
代码方式:只要是order访问任何其他服务都采用随即方式
配置文件方式:只针对order这个服务

优缺点:
代码方式:配置灵活,但修改时需要重新打包发布
配置方式:直观,方便,无需重新打包发布,但是无法做全局配置

通过浏览器访问order服务,五次调用效果如下:
其中1-4次分别调用了userservice的8081这个端口;第5次调用了userservice的8082这个端口
在这里插入图片描述



Ribbon实现原理

1、获取请求信息(url、服务名称……)
	@Override
	public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
			final ClientHttpRequestExecution execution) throws IOException {
		final URI originalUri = request.getURI();
		String serviceName = originalUri.getHost();
		Assert.state(serviceName != null, 
				"Request URI does not contain a valid hostname: " + originalUri);
		return this.loadBalancer.execute(serviceName,
				this.requestFactory.createRequest(request, body, execution));
	}

在这里插入图片描述


实现负载均衡的方法—execute

public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
        return this.execute(serviceId, (LoadBalancerRequest)request, (Object)null);
}

对外暴露的接口,也是实现负载均衡的关键,具体实现逻辑如下:

    public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint) throws IOException {
       //拉取服务列表
        ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId);
        //选择服务
        Server server = this.getServer(loadBalancer, hint);
        if (server == null) {
            throw new IllegalStateException("No instances available for " + serviceId);
        } else {
            RibbonLoadBalancerClient.RibbonServer ribbonServer = new RibbonLoadBalancerClient.RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server));
            return this.execute(serviceId, (ServiceInstance)ribbonServer, (LoadBalancerRequest)request);
        }
    }

this.getLoadBalancer(serviceId)方法内部实现逻辑:
2、根据服务名称拉取服务列表

在这里插入图片描述
this.getServer(loadBalancer, hint)方法内部实现逻辑

protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
        return loadBalancer == null ? null : loadBalancer.chooseServer(hint != null ? hint : "default");
}

3、选择服务
    public Server chooseServer(Object key) {
        if (ENABLED.get() && this.getLoadBalancerStats().getAvailableZones().size() > 1) {
            Server server = null;

            try {
                LoadBalancerStats lbStats = this.getLoadBalancerStats();
                Map<String, ZoneSnapshot> zoneSnapshot = ZoneAvoidanceRule.createSnapshot(lbStats);
                logger.debug("Zone snapshots: {}", zoneSnapshot);
                if (this.triggeringLoad == null) {
                    this.triggeringLoad = DynamicPropertyFactory.getInstance().getDoubleProperty("ZoneAwareNIWSDiscoveryLoadBalancer." + this.getName() + ".triggeringLoadPerServerThreshold", 0.2D);
                }

                if (this.triggeringBlackoutPercentage == null) {
                    this.triggeringBlackoutPercentage = DynamicPropertyFactory.getInstance().getDoubleProperty("ZoneAwareNIWSDiscoveryLoadBalancer." + this.getName() + ".avoidZoneWithBlackoutPercetage", 0.99999D);
                }

                Set<String> availableZones = ZoneAvoidanceRule.getAvailableZones(zoneSnapshot, this.triggeringLoad.get(), this.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 = this.getLoadBalancer(zone);
                        server = zoneLoadBalancer.chooseServer(key);
                    }
                }
            } catch (Exception var8) {
                logger.error("Error choosing server using zone aware logic for load balancer={}", this.name, var8);
            }

            if (server != null) {
                return server;
            } else {
                logger.debug("Zone avoidance logic is not invoked.");
                return super.chooseServer(key);
            }
        } else {
            logger.debug("Zone aware logic disabled or there is only one zone");
        //选择服务
            return super.chooseServer(key);
        }
    }


4、轮询

super.chooseServer(key)内部具体业务逻辑
在这里插入图片描述
在这里插入图片描述


负载均衡实现后的返回如下结果:
在这里插入图片描述


Ribbon饥饿加载和懒加载

懒加载

默认是采用,即第一次访问时才会去创建LoadBalanceClient去拉取服务列表,第一次请求时间会很长,但后续请求时间会变短
在这里插入图片描述
我们在通过访问浏览器看看效果:
在这里插入图片描述


饥饿加载

急不可耐,当服务启动时就创建LoadBalanceClient去拉取服务列表,降低第一次访问的耗时
需要在配置文件中进行配置

ribbon:
  eager-load:
    enabled: true # 开启饥饿加载
    clients: userservice # 指定对userservice这个服务饥饿加载

当服务启动时查看,效果如下:
在这里插入图片描述


三、总结

Ribbon帮助我们在去选择对应策略的服务实例,SpringCloud中许多组件都集成了Ribbon,Ribbon提供的内置策略,我们也可以自定义策略,虽然知道SpringCloud的某些组件集成了Ribbon,但是怎么集成的Ribbon?Ribbon如何和其他组件关联的?我们还要知其所以然。

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

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

相关文章

00后卷王的自述,我真的很卷吗?

前言 前段时间去面试了一个公司&#xff0c;成功拿到了offer&#xff0c;薪资也从12k涨到了18k&#xff0c;对于工作都还没两年的我来说&#xff0c;还是比较满意的&#xff0c;毕竟一些工作3、4年的可能还没我高。 我可能就是大家说的卷王&#xff0c;感觉自己年轻&#xff…

设计模式2、抽象工厂模式 Abstract Factory

解释说明&#xff1a;提供一个创建一系列相关或相互依赖对象的接口&#xff0c;而无需指定他们具体的类。 简言之&#xff0c;一个工厂可以提供创建多种相关产品的接口&#xff0c;而无需像工厂方法一样&#xff0c;为每一个产品都提供一个具体工厂 抽象工厂&#xff08;Abstra…

【2251. 花期内花的数目】

来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 描述&#xff1a; 给你一个下标从 0 开始的二维整数数组 flowers &#xff0c;其中 flowers[i] [starti, endi] 表示第 i 朵花的 花期 从 starti 到 endi &#xff08;都 包含&#xff09;。同时给你一个下标从 0 开始…

jeecgboot-3.5.5本地安装部署

一、开发环境说明 1、jdk1.8&#xff1a;java运行环境 2、Maven&#xff1a;项目依赖管理 3、git&#xff1a;代码管理 4、mysql5.7:数据管理。也可以是orcale、pgsql等 华为云HECS云服务器docker环境下安装mysql_supersolon的博客-CSDN博客 5、redis&#xff1a;缓存管理…

Goby 漏洞发布|Cockpit 平台 upload 文件上传漏洞(CVE-2023-1313)

漏洞名称&#xff1a;Cockpit 平台 upload 文件上传漏洞&#xff08;CVE-2023-1313&#xff09; English Name&#xff1a; Cockpit File Upload Vulnerability(CVE-2023-1313) CVSS core:7.2 影响资产数&#xff1a;3185 漏洞描述&#xff1a; Cockpit 是一个自托管、灵活…

[C++ 网络协议] 重叠I/O模型

目录 1. 什么是重叠I/O模型 2. 重叠I/O模型的实现 2.1 创建重叠非阻塞I/O模式的套接字 2.2 执行重叠I/O的Send函数 2.3 执行重叠I/O的Recv函数 2.4 获取执行I/O重叠的函数的执行结果 2.5 重叠I/O的I/O完成确认 2.5.1 使用事件对象&#xff08;使用重叠I/O函数的第六个参…

【x265 源码分析系列】:概述

介绍 x265 也属于 VLC 的 project。 版本&#xff1a; x265-3.5&#xff08;TAG-208&#xff09; git&#xff1a; https://bitbucket.org/multicoreware/x265_git.git 编码特点&#xff1a; 研究了一段时间的 HEVC 编码标准&#xff0c;最近开始研究符合 HEVC 标准的开源…

STM32G070RBT6-MCU温度测量(ADC)

1、借助STM32CubeMX生成系统及外设相关初始化代码。 在以上配置后就可以生成相关初始化代码了。 /* ADC1 init function */ void MX_ADC1_Init(void) {/* USER CODE BEGIN ADC1_Init 0 *//* USER CODE END ADC1_Init 0 */ADC_ChannelConfTypeDef sConfig {0};/* USER COD…

MyBatis-Plus通用Service快速实现赠三改查[MyBatis-Plus系列] - 第489篇

历史文章&#xff08;文章累计480&#xff09; 《国内最全的Spring Boot系列之一》 《国内最全的Spring Boot系列之二》 《国内最全的Spring Boot系列之三》 《国内最全的Spring Boot系列之四》 《国内最全的Spring Boot系列之五》 《国内最全的Spring Boot系列之六》 M…

综述 | 关于点云配准的全面综述(二)

原创 | 文 BFT机器人 05 基于优化的配准方法 基于优化的方法的关键思想是开发复杂的优化策略来实现方程&#xff08;1&#xff09;中非线性问题的最优解。 由于同源挑战的影响&#xff0c;这个非线性问题变得具有挑战性。图&#xff08;2a&#xff09;总结了该类别的主要过程。…

从零开始之了解电机及其控制(11)实现空间矢量调制

广泛地说&#xff0c;空间矢量调制只是将电压矢量以及磁场矢量在空间中调制到任意角度&#xff0c;通常同时最大限度地利用整个电压范围。 其他空间矢量调制模式确实存在&#xff0c;并且根据您最关心的内容&#xff0c;它们可能值得研究。 如何实际执行这种所谓的交替反向序列…

看看属猴人性格及近几年的运势怎么样?

属猴的人五行主金&#xff0c;乃是申金之所在&#xff0c;金旺之人&#xff0c;外显懒散&#xff0c;内心富有主见&#xff0c;行事坚定&#xff0c; 有贯彻始终之斗志&#xff0c;与他人合作融洽&#xff0c;且得以财运颇多&#xff1b; 主金&#xff0c;杀伐果决、精明干练&a…

PHP各种老版本下载方式

最近因工作需要&#xff0c;要下载PHP7.3的最新版本版本。 PHP官网上提供了各种老版本下载地址&#xff1a; https://windows.php.net/downloads/releases/archives/ 下载速度不稳定&#xff0c;时快时慢。 使用前&#xff0c;给下载留足时间。 貌似晚上速度快一些。

gif怎么转换成视频MP4?

gif怎么转换成视频MP4&#xff1f;GIF动图已成为一种风靡网络的流行的特殊图片文件&#xff0c;其循环播放和逐帧呈现的特点使其在社交媒体、聊天应用等场合广泛应用&#xff0c;平时我们进行群聊是&#xff0c;大家总会一些gif动态表情的出现而感觉非常的开行&#xff0c;gif动…

Android12之容器类SortedVector、KeyedVector、Vector、VectorImpl总结(一百六十六)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

极致增长 | NetMarvel 程序化广告最大化广告变现收益

程序化广告已彻底改变广告主触达目标受众的方式。 从早期传统人工售卖流量&#xff0c;到流量平台推出广告联盟&#xff0c;从程序化交易到利用算法和机器学习实时计算买卖广告空间&#xff0c;通过逐渐精微的数据来测评不同渠道、不同受众的广告效果&#xff0c;提高广告主的…

datart:Invalid database configuration. Datart is running in demo mode

datart在IDEA配置好数据库连接之后&#xff0c;启动&#xff0c;报错&#xff1a; 【********* Invalid database configuration. Datart is running in demo mode *********】 原因是缺少一个变量 config 增加即可&#xff1a; 再次启动&#xff0c;就不会报无效数据库配置了…

百华鞋业董事长牛兴华应邀出席德国前总统武尔夫欢迎宴会

2023年9月25日&#xff0c;德国前总统克里斯蒂安-武尔夫&#xff08;Christian Wullff&#xff09;一行来华访问期间&#xff0c;于上海新华联索菲特举办2023中德交流领袖论坛暨武尔夫总统欢迎晚宴。百华鞋业董事长牛兴华先生受邀出席&#xff0c;并受到武尔夫的亲自接见。 山东…

关于NVIC 中断控制器的中断配置。

以下图片均来自NVIC控制器内容。 M3处理器仅实现了每个81个中断&#xff0c;每个中断的优先级由高4位控制。 这里的组优先级我认为是抢占式优先级。

多线程批量下载ERA5逐日数据

介绍 这篇博文主要是整了和ERA5官方参考文档和网上现有的代码&#xff0c;从而实现ERA5逐日数据的批量下载**&#xff08;可指定时区&#xff09;**。 先前准备 在使用python批量下载ERA5逐日数据前我们需要手动配置一下cdsapi 1.访问&#xff1a;CDS官网并注册账号 2.注册好…