Spring Cloud Loadbalancer 的使用

一、默认负载均衡策略

Spring Cloud LoadBalancer 默认的负载均衡策略是轮询。

轮询效果示例

我们需要示例一个请求分发到不同的模块上,所以我们需要创建多模块项目。

新建 Spring Boot (3.0.2)的 Maven 项目(JDK 17)(父模块),添加依赖 Spring Web、Nacos Service Discovery、OpenFeign、Cloud LoadBalancer,然后删除 src 与 HELP.md 文档,修改 pom.xml (如删除模块运行)

创建子模块,然后在子模块的 pom.xml 中配置父类,再删除父 pom.xml 包含的声明,最后在父模块中配置子类声明。此时就可以在子模块的配置文件中配置配置中心。

我们先在子模块写一个服务UserController。

package org.example.provider.controller;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate ServletWebServerApplicationContext context;@RequestMapping("/getname")public String getName(@RequestParam("id") Integer id) {return "Provider-name-" + id +" | port: " + context.getWebServer().getPort();}
}

由于Loadbalancer默认是轮询的所以我们创建两个实例运行。

接着我们用同样的方式新建consumer子模块,然后在运行类中添加 OpenFeign 的注解(开启OpenFeign),再写声明服务(Service)与调用服务(Controller),然后我们就可以访问对应的服务,可以看到它的轮询效果。

package org.example.consumer.service;import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;@Service
@FeignClient("loadbalancer-service")
public interface UserService {@RequestMapping("/user/getname")String getName(@RequestParam("id") Integer id);
}
package org.example.consumer.controller;import org.example.consumer.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;@RestController
public class CallController {@Autowiredprivate UserService userService;@RequestMapping("/getname")public String getName(@RequestParam("id") Integer id) {return userService.getName(id);}}

Loadbalancer 轮询的源码

我们需要看 Spring Cloud LoadBalancer 的配置类 LoadBalancerClientConfiguration 的部分源码:

其中 ReactorLoadBalancer 方法非常重要:

    @Bean@ConditionalOnMissingBeanpublic ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {String name = environment.getProperty("loadbalancer.client.name");return new RoundRobinLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);}

可以看到默认设置的负载均衡策略是 RoundRobinLoadBalancer (可以直接看最下面的三行代码,即轮询负载均衡算法的实现)。

    public RoundRobinLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId, int seedPosition) {this.serviceId = serviceId;this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;this.position = new AtomicInteger(seedPosition);}public Mono<Response<ServiceInstance>> choose(Request request) {ServiceInstanceListSupplier supplier = (ServiceInstanceListSupplier)this.serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);return supplier.get(request).next().map((serviceInstances) -> {return this.processInstanceResponse(supplier, serviceInstances);});}
    private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier, List<ServiceInstance> serviceInstances) {Response<ServiceInstance> serviceInstanceResponse = this.getInstanceResponse(serviceInstances);if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {((SelectedInstanceCallback)supplier).selectedServiceInstance((ServiceInstance)serviceInstanceResponse.getServer());}return serviceInstanceResponse;}private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {if (instances.isEmpty()) {if (log.isWarnEnabled()) {log.warn("No servers available for service: " + this.serviceId);}return new EmptyResponse();} else if (instances.size() == 1) {return new DefaultResponse((ServiceInstance)instances.get(0));} else {int pos = this.position.incrementAndGet() & Integer.MAX_VALUE;ServiceInstance instance = (ServiceInstance)instances.get(pos % instances.size());return new DefaultResponse(instance);}}

二、随机负载均衡策略

Spring Cloud LoadBalancer 内置了两种负载均衡策略:

  1. 轮询负载均衡策略,默认负载均衡策略。
  2. 随机负载均衡策略。

而要实现随机负载均衡策略的步骤如下:

  1. 创建随机负载均衡策略。
  2. 设置随机负载均衡策略。

创建随机负载均衡器

和源码类似的,返回ReactorLoadBalancer,通过 new 随机负载均衡 创建出来。那么我们可以直接复制源码然后修改方法名就是创建了随机负载均衡器:

package com.example.comsumer.config;import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.RandomLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;public class RandomLoadBalancerConfig {@Beanpublic ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {String name = environment.getProperty("loadbalancer.client.name");return new RandomLoadBalancer(loadBalancerClientFactory.getLazyProvider(name,ServiceInstanceListSupplier.class), name);}
}

设置局部负载均衡策略

找到 comsumer.service 的接口,加注解 @LoadBalancerClient,然后设置对应的参数:

但是设置局部策略在一些版本是无效的,所以我们可以设置全局的负载均衡器

设置全局负载均衡策略

在启动类上设置注解 @LoadBalancerClients,然后设置对应的参数:

三、Nacos 权重负载均衡策略

新建 Nacos 负载均衡器

package com.example.comsumer.config;import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.loadbalancer.NacosLoadBalancer;
import jakarta.annotation.Resource;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;
import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;@LoadBalancerClients(defaultConfiguration = NacosLoadBalancerConfig.class)
public class NacosLoadBalancerConfig {@Resourceprivate NacosDiscoveryProperties nacosDiscoveryProperties;@Beanpublic ReactorLoadBalancer<ServiceInstance> nacosLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {String name = environment.getProperty("loadbalancer.client.name");return new NacosLoadBalancer(loadBalancerClientFactory.getLazyProvider(name,ServiceInstanceListSupplier.class), name, nacosDiscoveryProperties);}
}

设置局部负载均衡策略

关掉前面开启的随机全局负载均衡策略。

再在service中修改为nacos局部负载均衡策略 

此时要查看 Nacos 负载均衡策略的效果的话需要在 Nacos中设置权重。

四、自定义负载均衡

实现自定义负载均衡策略需要以下 3步:

  1. 创建自定义负载均衡器
  2. 封装自定义负载均衡器
  3. 为服务设置自定义负载均衡器

新建负载均衡类(创建自定义负载均衡器)

自定义的我们需要实现顶级类的接口 ReactorServiceInstanceLoadBalancer。

复制顶级类的代码,修改核心策略即可。

package com.example.comsumer.config;import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.DefaultResponse;
import org.springframework.cloud.client.loadbalancer.EmptyResponse;
import org.springframework.cloud.client.loadbalancer.Request;
import org.springframework.cloud.client.loadbalancer.Response;
import org.springframework.cloud.loadbalancer.core.*;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import reactor.core.publisher.Mono;import java.util.List;
import java.util.concurrent.ThreadLocalRandom;public class CustomLoadBalancer implements ReactorServiceInstanceLoadBalancer {private static final Log log = LogFactory.getLog(RandomLoadBalancer.class);private final String serviceId;private ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;public CustomLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId) {this.serviceId = serviceId;this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;}public Mono<Response<ServiceInstance>> choose(Request request) {ServiceInstanceListSupplier supplier = (ServiceInstanceListSupplier)this.serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);return supplier.get(request).next().map((serviceInstances) -> {return this.processInstanceResponse(supplier, serviceInstances);});}private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier, List<ServiceInstance> serviceInstances) {Response<ServiceInstance> serviceInstanceResponse = this.getInstanceResponse(serviceInstances);if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {((SelectedInstanceCallback)supplier).selectedServiceInstance((ServiceInstance)serviceInstanceResponse.getServer());}return serviceInstanceResponse;}private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {if (instances.isEmpty()) {if (log.isWarnEnabled()) {log.warn("No servers available for service: " + this.serviceId);}return new EmptyResponse();} else {// 核心: 自定义随机策略ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();HttpServletRequest request = attributes.getRequest();String ipAddress = request.getRemoteAddr();System.out.println("用户 IP: " + ipAddress);int hash = ipAddress.hashCode();// 自定义负载均衡策略[关键代码]int index = hash % instances.size();// 得到服务实例方法ServiceInstance instance = (ServiceInstance)instances.get(index);return new DefaultResponse(instance);}}
}

新建一个负载均衡对象(封装自定义负载均衡器)

package com.example.comsumer.config;import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.RandomLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;public class CustomLoadBalancerConfig {@Beanpublic ReactorLoadBalancer<ServiceInstance> customLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {String name = environment.getProperty("loadbalancer.client.name");return new CustomLoadBalancer(loadBalancerClientFactory.getLazyProvider(name,ServiceInstanceListSupplier.class), name);}
}

设置负载均衡策略()为服务设置自定义负载均衡器

为了防止冲突,注释掉Nacos的策略。

五、缓存 

Spring Cloud LoadBalancer 在获取实例时有两种选择:

  1. 即时获取: 每次从注册中心得到最新健康的实例,效果好、开销太大。
  2. 缓存服务列表: 每次得到服务列表之后,缓存一段时间,这样既能保证性能,同时也能兼容一定的及时性

而 Spring Cloud LoadBalancer 中默认开启了缓存服务列表的功能。

Spring Cloud LoadBalancer 默认缓存的重要特性有两项:

  1. 缓存的过期时间为 35s。
  2. 缓存保存个数为 256 个。

我们可以通过以下配置来改变这些配置:

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

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

相关文章

万界星空科技MES系统生产计划管理的功能

MES系统&#xff08;Manufacturing Execution System&#xff0c;制造执行系统&#xff09;的生产计划管理功能是其核心功能之一&#xff0c;旨在将企业的生产计划转化为实际的生产操作&#xff0c;并通过实时监控和调整来确保生产活动的顺利进行。以下是MES系统生产计划管理功…

STM32智能环境监测系统教程

目录 引言环境准备智能环境监测系统基础代码实现&#xff1a;实现智能环境监测系统 4.1 数据采集模块 4.2 数据处理与控制模块 4.3 通信与网络系统实现 4.4 用户界面与数据可视化应用场景&#xff1a;环境监测与管理问题解决方案与优化收尾与总结 1. 引言 智能环境监测系统通…

基于STM32智能电子锁设计

1.简介 随着时代的高速发展&#xff0c;家居安全也成为人们日常生活中的一个安全问题。目前传统的门锁使用的是机械密码&#xff0c;在安全性方面表现不佳。这些缺点可以通过改用智能电子密码锁来弥补。智能电子锁是一种使用了现代电子技术的高科技产品&#xff0c;它的出现解决…

ActiveMQ配置延迟投递和定时投递教程

配置activemq.xml中的<broker>标签添加schedulerSupport"true" schedulerSupport"true"更改完成重启生效 四大属性解释 Property nametypedescriptionAMQ_SCHEDULED_DELAYlong延迟投递的时间AMQ_SCHEDULED_PERIODlong重复投递的时间间隔AMQ_SCHEDU…

期权末日双买跨式策略-这才是末日轮稳定赚钱的方法吗?!

今天带你了解期权末日双买跨式策略-这才是末日轮稳定赚钱的方法吗&#xff1f;&#xff01;期权末日双买跨式策略是一种在期权到期日前预期市场会出现大幅波动时使用的策略。 期权双买跨式策略适合期权末日轮是因为它能利用临近到期日时市场潜在的大幅波动来获利。末日轮期权&…

AI数字人+数字孪生IOC智慧运营平台:提升业务场景智慧化运维水平

在人工智能时代&#xff0c;“AI数字人数字孪生IOC智慧运营平台”&#xff0c;不仅能够提升数字孪生系统的人机交互体验&#xff0c;还能实现高效的运维管理&#xff0c;可以有效推动多领域场景数字化转型和智能化升级。 案例分享 深圳新一代产业园NEXT PARK交流中心 深圳新一…

【中项第三版】系统集成项目管理工程师 | 第 5 章 软件工程① | 5.1 - 5.3

前言 第5章对应的内容选择题和案例分析都会进行考查&#xff0c;这一章节属于技术的内容&#xff0c;学习要以教材为准。 目录 5.1 软件工程定义 5.2 软件需求 5.2.1 需求的层次 5.2.2 质量功能部署 5.2.3 需求获取 5.2.4 需求分析 5.2.5 需求规格说明书 5.2.6 需求变…

使用C#实现无人超市管理系统——数据结构课设(代码+PPT+说明书)

说明&#xff1a;这是自己做的课程设计作业&#xff0c;得分情况98/100 如果想要获取私信我 本项目采用线性表中的链表来进行本次系统程序的设计。链表分为两条线&#xff0c;分别是存储用户信息和商品信息&#xff0c;并且都设为公共属性&#xff0c;方便对用户信息和商品信息…

C#数字医学影像系统(RIS/PACS)源码,Oracle数据库,C/S架构,运行稳定

数字医学影像系统&#xff08;RIS/PACS&#xff09;源码&#xff0c;三甲以下的医院都能满足。PACS 系统全套成品源码。 开发技术&#xff1a;C/S架构&#xff0c;C#开发语言&#xff0c;数据库服务器采用Oracle数据库。 医学影像存储与传输系统&#xff0c;融合了医学信息化…

【面试题】Golang 自动垃圾回收机制细节(第六篇)

目录 Go V1.3之前的标记清除&#xff08;mark and sweep&#xff09; Go V1.5三色标记法 Go V1.8混合写屏障机制 各版本之间的区别 Go V1.3之前的标记清除&#xff08;mark and sweep&#xff09; 流程 1.暂停程序业务逻辑&#xff0c;找出不可达的对象和可达对象 2.开始…

WebPlotDigitizer图像取点软件/在线网页

记录一下WebPlotDigitizer的使用方法。 网上一搜就能搜到官网&#xff0c;本文也把网址放在最后了。 1 上传待处理图像 2 选择图像的类型 每个选项都有示例图&#xff0c;不一一解释了。选好后点击 calibrate 3 确定横纵坐标 3.1 上一步完成后&#xff0c;左边栏会自动跳…

Elasticsearch:如何选择向量数据库?

作者&#xff1a;来自 Elastic Elastic Platform Team 向量数据库领域是一个快速发展的领域&#xff0c;它正在改变我们管理和搜索数据的方式。与传统数据库不同&#xff0c;向量数据库以向量的形式存储和管理数据。这种独特的方法可以实现更精确、更相关的搜索&#xff0c;并允…

物流智能锁在物流货运智能锁控管理中的深度应用与变革

一、物流货运锁控管理的痛点分析 &#xff08;一&#xff09;安全风险居高不下 1、传统锁具易被破解 常见的机械锁和简单电子锁结构相对简单&#xff0c;技术手段容易突破&#xff0c;给不法分子留下可乘之机&#xff0c;导致货物被盗或被篡改的风险增加。 2、缺乏实时监控…

生产车间人数统计牌,统计精准,显示内容全面

在现代制造业中&#xff0c;精细化管理和安全生产是企业持续发展的关键。随着工业4.0和智能制造的推进&#xff0c;各种智能工具和系统被广泛应用于生产管理&#xff0c;以提高效率、降低成本、保障安全。其中&#xff0c;生产车间人数统计牌作为一种重要的现场管理工具&#x…

前端小项目-强调鼠标悬停时效果的名片

前端练习小项目——动态效果名片 前言&#xff1a; 在学习完HTML和CSS之后&#xff0c;我们就可以开始做一些小项目了。本篇文章所讲的小项目为——动态效果名片。通过这个项目&#xff0c;你将学会如何使用HTML和CSS来创建一个具有动态效果的名片。 在开始学习之前&#xff0…

信任、创新和传承: 对威步创始人的深入采访

Reflecting on 35 years, what was the moment you realized your vision for the company was becoming a reality? 回顾 35 年的发展历程&#xff0c;您意识到自己对公司的愿景即将成为现实的那一刻是什么时候&#xff1f; Oliver Winzenried 我们看到市场上出现了首批数字…

什么是多源异构数据?如何处理多源异构数据?

目录 一、多源异构数据的定义 二、多源异构数据的种类 三、多源异构数据的处理方案 1.数据接入 2.数据转换 3.数据输出 4.数据同步 四、结语 随着数字化转型的深入&#xff0c;企业和社会产生了前所未有的海量数据。这些数据不仅量大&#xff0c;而且来源多样&#xff0c;结构各…

昇思25天学习打卡营第21天 | 基于MindSpore的红酒分类实验

内容简介 本实验介绍了使用MindSpore框架实现K近邻算法&#xff08;KNN&#xff09;对红酒数据集进行分类的全过程。通过数据读取、预处理、模型构建与预测&#xff0c;展示了KNN算法在红酒数据集上的应用。实验中详细解释了KNN的原理、距离度量方式及其在分类问题中的应用&…

项目实用linux 操作详解-轻松玩转linux

我之前写过完整的linux系统详解介绍&#xff1a; LInux操作详解一&#xff1a;vmware安装linux系统以及网络配置 LInux操作详解二&#xff1a;linux的目录结构 LInux操作详解三&#xff1a;linux实际操作及远程登录 LInux操作详解四&#xff1a;linux的vi和vim编辑器 LInux操作…

商业数据分析思维的培训PTT制作大纲分享

商业数据分析思维的培训PTT制作大纲: 基本步骤: 明确PPT的目的和主题 收集并整理相关内容资料 构思并确定PPT的框架大纲 编写PPT的内容文字 插入图片、图表等视觉元素 设计PPT的版式和模板 排练并修改PPT 输出并备份最终版本 目的:数据思维培养; 主题:商业数据分…