Spring Cloud之负载均衡之LoadBalance

作者:新绿MEHO日期:2025/10/6

目录

负载均衡

问题

步骤

现象

什么是负载均衡?

负载均衡的一些实现

服务端负载均衡

客户端负载均衡

使用Spring Cloud LoadBalance实现负载均衡

负载均衡策略

​编辑 ​编辑LoadBalancer原理

服务部署

准备环境和数据

服务构建打包

启动服务

上传Jar包到云服务器

启动服务

远程调用访问


负载均衡

问题

上面是我们之前的代码,是根据应用名称获取了服务实例列表,并从列表中选择了一个服务实例。

那如果一个服务对应多个实例呢?流量是否可以合理的分配到多个实例呢?

我们再启动两个product-service示例。

步骤

打开View->Tool Windows->Services

选中ProductServiceApplication,然后右键,选择Copy Configuration

然后改名,并点击Modify options

然后点击Add VM options

然后添加-Dserver.port=9091,然后Apply,OK

然后再重复上述步骤,再添加一个服务实例。

现象

启动上述所有实例后,可以在Eureka网站页面看到:

此时多次访问"http://127.0.0.1:8080/order/1",然后查看IDEA上的日志,可以看到,我们刚刚的多次访问,都访问到了同一台机器上,即第一个注册到Eureka的服务实例端口号为9092的机器。

这肯定不是我们想要的结果,我们启动多个服务实例,是希望可以分担其它机器的负荷,那么如何实现呢?

我们可以修改一下之前的order-service中的OrderService代码,把只请求服务列表第一台机器修改为轮询请求服务列表中的机器。

修改后的order-service中的OrderService代码如下:

1package order.service;
2
3import jakarta.annotation.PostConstruct;
4import lombok.extern.slf4j.Slf4j;
5import order.mapper.OrderMapper;
6import order.model.OrderInfo;
7import order.model.ProductInfo;
8import org.springframework.beans.factory.annotation.Autowired;
9import org.springframework.cloud.client.ServiceInstance;
10import org.springframework.cloud.client.discovery.DiscoveryClient;
11import org.springframework.stereotype.Service;
12import org.springframework.web.client.RestTemplate;
13
14import java.util.List;
15import java.util.concurrent.atomic.AtomicInteger;
16
17@Slf4j
18@Service
19public class OrderService {
20    @Autowired
21    private OrderMapper orderMapper;
22
23    @Autowired
24    private RestTemplate restTemplate;
25
26    @Autowired
27    private DiscoveryClient discoveryClient;
28
29    //计数器
30    private AtomicInteger count = new AtomicInteger(1);
31
32    private List<ServiceInstance> instances;
33
34    @PostConstruct
35    public void init(){
36        //从Eureka中获取服务列表
37        instances = discoveryClient.getInstances("product-service");
38    }
39
40    public OrderInfo selectOrderById(Integer orderId){
41        OrderInfo orderInfo = orderMapper.selectOrderById(orderId);
42        //计算轮流的实例idnex
43        int index= count.getAndIncrement() % instances.size();
44        //获取实例
45        String uri = instances.get(index).getUri().toString();
46        //拼接url
47        String url = uri+"/product/"+orderInfo.getProductId();
48        log.info("远程调用url:{}", url);
49        ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);
50        orderInfo.setProductInfo(productInfo);
51        return orderInfo;
52    }
53
54}

重启order-service,再次多次访问"127.0.0.1:8080/order/1",可以看到每个服务实例都有被请求到:

通过⽇志可以看到, 请求被均衡的分配在了不同的实例上, 这就是负载均衡.

什么是负载均衡?

负载均衡(Load Balance,简称 LB) , 是⾼并发, ⾼可⽤系统必不可少的关键组件.
当服务流量增⼤时, 通常会采⽤增加机器的⽅式进⾏扩容, 负载均衡就是⽤来在多个机器或者其他资源中, 按照⼀定的规则合理分配负载.

负载均衡的一些实现

上⾯的例⼦中, 我们只是简单的对实例进⾏了轮询, 但真实的业务场景会更加复杂. ⽐如根据机器的配置进⾏负载分配, 配置⾼的分配的流量⾼, 配置低的分配流量低等.

服务多机部署时, 开发⼈员都需要考虑负载均衡的实现, 所以也出现了⼀些负载均衡器, 来帮助我们实现负载均衡.

负载均衡分为服务端负载均衡和客⼾端负载均衡.

服务端负载均衡

在服务端进⾏负载均衡的算法分配.
⽐较有名的服务端负载均衡器是Nginx. 请求先到达Nginx负载均衡器, 然后通过负载均衡算法, 在多个服务器之间选择⼀个进⾏访问.

客户端负载均衡

在客⼾端进⾏负载均衡的算法分配.

把负载均衡的功能以库的⽅式集成到客⼾端, ⽽不再是由⼀台指定的负载均衡设备集中提供.
⽐如Spring Cloud的Ribbon, 请求发送到客⼾端, 客⼾端从注册中⼼(⽐如Eureka)获取服务列表, 在发送请求前通过负载均衡算法选择⼀个服务器,然后进⾏访问.
Ribbon是Spring Cloud早期的默认实现, 由于不维护了, 所以最新版本的Spring Cloud负载均衡集成的是Spring Cloud LoadBalancer(Spring Cloud官⽅维护)。

客⼾端负载均衡和服务端负载均衡最⼤的区别在于服务清单所存储的位置。

Spring Cloud LoadBalance

SpringCloud 从 2020.0.1 版本开始, 移除了Ribbon 组件,使⽤Spring Cloud LoadBalancer 组件来代替 Ribbon 实现客⼾端负载均衡。

使用Spring Cloud LoadBalance实现负载均衡

1. 给 RestTemplate 这个Bean添加 @LoadBalanced 注解就可以

1package order.config;
2
3import org.springframework.cloud.client.loadbalancer.LoadBalanced;
4import org.springframework.context.annotation.Bean;
5import org.springframework.context.annotation.Configuration;
6import org.springframework.web.client.RestTemplate;
7
8@Configuration
9public class BeanConfig {
10    @Bean
11    @LoadBalanced
12    public RestTemplate restTemplate(){
13        return new RestTemplate();
14    }
15}

2.修改后的order-service中的OrderService代码如下:

修改IP为服务端名称。

1package order.service;
2
3import jakarta.annotation.PostConstruct;
4import lombok.extern.slf4j.Slf4j;
5import order.mapper.OrderMapper;
6import order.model.OrderInfo;
7import order.model.ProductInfo;
8import org.springframework.beans.factory.annotation.Autowired;
9import org.springframework.stereotype.Service;
10import org.springframework.web.client.RestTemplate;
11
12
13@Slf4j
14@Service
15public class OrderService {
16    @Autowired
17    private OrderMapper orderMapper;
18
19    @Autowired
20    private RestTemplate restTemplate;
21
22
23    public OrderInfo selectOrderById(Integer orderId){
24        OrderInfo orderInfo = orderMapper.selectOrderById(orderId);
25        String url = "http://product-service/product/"+orderInfo.getProductId();
26        log.info("远程调用url:{}", url);
27        ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);
28        orderInfo.setProductInfo(productInfo);
29        return orderInfo;
30    }
31}

此时再次多次访问"127.0.0.1:8080/order/1",可以看到每个服务实例都有被请求到,且比例差不多:

负载均衡策略

负载均衡策略是⼀种思想, ⽆论是哪种负载均衡器, 它们的负载均衡策略都是相似的. Spring Cloud
LoadBalancer 仅⽀持两种负载均衡策略: 轮询策略 和 随机策略。

1. 轮询(Round Robin): 轮询策略是指服务器轮流处理⽤⼾的请求. 这是⼀种实现最简单, 也最常⽤的策略. ⽣活中也有类似的场景, ⽐如学校轮流值⽇, 或者轮流打扫卫⽣.
2. 随机选择(Random): 随机选择策略是指随机选择⼀个后端服务器来处理新的请求.

官方介绍

翻译:

Spring Cloud提供了自己的客户端负载均衡器抽象和实现。对于负载平衡机制,添加了ReactiveLoadBalancer接口,并为其提供了基于轮转和随机的实现。为了让实例从反应式ServiceInstanceListSupplier中进行选择,使用了该接口。目前,我们支持ServiceInstanceListSupplier的基于服务发现的实现,该实现使用类路径中可用的发现客户端从服务发现中检索可用实例。通过将Spring.Cloud.LoadBalancer.enabled的值设置为false,可以禁用Spring Cloud LoadBalancer。

1. 定义随机算法对象, 通过 @Bean 将其加载到 Spring 容器中
此处使⽤Spring Cloud LoadBalancer提供的 RandomLoadBalancer

1package order.config;
2
3import org.springframework.cloud.client.ServiceInstance;
4import org.springframework.cloud.loadbalancer.core.RandomLoadBalancer;
5import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
6import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
7import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
8import org.springframework.context.annotation.Bean;
9import org.springframework.core.env.Environment;
10
11public class CustomLoadBalancerConfiguration {
12    @Bean
13    ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,
14                                                            LoadBalancerClientFactory loadBalancerClientFactory) {
15        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
16        return new RandomLoadBalancer(loadBalancerClientFactory
17                .getLazyProvider(name, ServiceInstanceListSupplier.class),
18                name);
19    }
20}

注意: 该类需要满⾜:
1. 不⽤ @Configuration 注释
2. 在组件扫描范围内

2. 使⽤ @LoadBalancerClient 或者 @LoadBalancerClients 注解

在 RestTemplate 配置类上⽅, 使⽤ @LoadBalancerClient 或 @LoadBalancerClients 注解, 可以对不同的服务提供⽅配置不同的客⼾端负载均衡算法策略.
由于我们只有⼀个客户端服务提供者, 所以使⽤@LoadBalancerClient。

1package order.config;
2
3import org.springframework.cloud.client.loadbalancer.LoadBalanced;
4import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
5import org.springframework.context.annotation.Bean;
6import org.springframework.context.annotation.Configuration;
7import org.springframework.web.client.RestTemplate;
8
9
10@LoadBalancerClient(name = "product-service", configuration = CustomLoadBalancerConfiguration.class)
11@Configuration
12public class BeanConfig {
13    @Bean
14    @LoadBalanced
15    public RestTemplate restTemplate(){
16        return new RestTemplate();
17    }
18}

@LoadBalancerClient 注解说明
1. name: 该负载均衡策略对哪个服务⽣效(服务提供⽅)
2. configuration : 该负载均衡策略 ⽤哪个负载均衡策略实现.

此时再次多次访问"127.0.0.1:8080/order/1",可以看到每个服务实例都有被请求到,且比例随机:

LoadBalancer原理

LoadBalancer 的实现, 主要是 LoadBalancerInterceptor , 这个类会对 RestTemplate 的请
求进⾏拦截, 然后从Eureka根据服务id获取服务列表,随后利⽤负载均衡算法得到真实的服务地址信息,替换服务id。

我们来看看源码实现:

可以看到这⾥的intercept⽅法, 拦截了⽤⼾的HttpRequest请求,然后做了⼏件事:

1. request.getURI() 从请求中获取uri, 也就是 http://product-service
2. service/product/1001 originalUri.getHost() 从uri中获取路径的主机名, 也就是服务id, product-service
3. loadBalancer.execute 根据服务id, 进⾏负载均衡, 并处理请求

根据serviceId,和负载均衡策略, 选择处理的服务:

根据serviceId,和负载均衡策略, 选择处理的服务:

服务部署
准备环境和数据

安装好JDK17和MySQL,并在MySQL中建表且存放好数据信息。

修改配置文件中的数据库密码。

服务构建打包

采⽤Maven打包, 需要对3个服务分别打包:
eureka-server, order-service, product-service

启动服务
上传Jar包到云服务器

第一次上传需要安装 lrzsz

Centos:

yum install lrzsz

Ubantu:

apt install lrzsz

直接拖动文件到xshell窗口,上传成功。

启动服务

#后台启动eureka-server, 并设置输出⽇志到logs/eureka.log
nohup java -jar eureka-server.jar >logs/eureka.log &

#后台启动order-service, 并设置输出⽇志到logs/order.log
nohup java -jar order-service.jar >logs/order.log &

#后台启动product-service, 并设置输出⽇志到logs/order.log
nohup java -jar product-service.jar >logs/product-9090.log &

再多启动两台product-service实例

#启动实例, 指定端⼝号为9091
nohup java -jar product-service.jar --server.port=9091 >logs/product-9091.log &

#启动实例, 指定端⼝号为9092
nohup java -jar product-service.jar --server.port=9092 >logs/product-9092.log &

远程调用访问

可以看到,能够正常访问并响应。


Spring Cloud之负载均衡之LoadBalance》 是转载文章,点击查看原文


相关推荐


Qwen3 Omni 的“全模态”,到底和多模态有啥不一样?
飞哥数智谈2025/10/5

前一阵阿里云栖大会,其中有个发布内容是全模态大模型 Qwen3-Omni。 说实话,这是我第一次真正地审视“全模态大模型”这个概念,因为之前 Qwen2.5-Omni 发布的时候,有了解过,但不多。 简介 先从概念上简单介绍下什么是“全模态大模型”。 “Qwen3-Omni是新一代原生全模态大模型,能够无缝处理文本、图像、音频和视频等多种输入形式,并通过实时流式响应同时生成文本与自然语音输出。” 多模态大模型 vs 全模态大模型 接下来,为了更好地理解,我们与“多模态大模型”做个对比。 相同点是


【算法导论】PDD 0928 笔试题解
PAK向日葵2025/10/4

快递单号 多多在快递公司负责快递单号录入工作,这些单号有严格的格式要求: 快递单号由3部分组成:2位大写字母(A~Z) + 6位数字 + 1位校验位 校验位计算规则:取前8位(2 字母 + 6 数字)中每个字符的ASCII码之和,对26取余后,加上A的ASCII码,得到的字符即为校验位 现在有一批可能存在校验位错误的单号,请你编写程序: 若单号格式正确且校验位正确,返回原单号 若前 8 位格式正确但校验位错误,返回修复后(校正校验位)的单号 若前 8 位格式错误(非 2 字母 + 6 数字)或


chrome-devtools-mcp windows 环境安装
monkeySix2025/10/2

问题: Could not find Google Chrome executable for channel 'stable' at 'C:\Program Files\Google\Chrome\Application\chrome.exe'. 解决方案: "chrome-devtools": { "command": "npx", "args": [ "chrome-devtools-mcp@latest", "-


C语言趣味小游戏----猜数字小游戏
雨落在了我的手上2025/10/2

一:猜数字游戏的雏形 部分代码如下,这下面的代码只是一个初步的框架,还不能正式的进行猜数字游戏,因为我还没有将猜数字的代码写进去,这里我先给大家解释一下是下面代码的大致意思 首先呢这个猜数字游戏我们要运用do while循环以及switch语句来初步的进行我们的猜数字游戏,当然我们可以不用do while循环,可以用while循环还有就是 for循环,但是这里我推荐大家用do while循环,因为会更加的好理解以及使用 现在我分别拆开代码来和大家一一介绍 int main() { in


Python 的内置函数 aiter
IMPYLH2025/10/2

Python 内建函数列表 > Python 的内置函数 aiter 你是否曾经在异步编程中处理过异步迭代器(Async Iterators)?是否对 async for 循环背后的机制感到好奇?那么,aiter() 就是 Python 提供的一个关键工具,它允许我们更灵活地处理异步可迭代对象(Async Iterables)。 aiter 的函数原型如下: def aiter(async_iterable): ''' 获取异步可迭代对象的迭代器 :param a


改bug的一些体会
花王江不语10/1/2025

这篇摘要总结了代码调试的实用技巧:1)通过构造函数断点快速了解类使用方式;2)代码比对时重点关注首次差异点;3)不熟悉代码时先寻找正确案例对比;4)调试和日志要灵活运用,循环场景适合日志,测试机问题可通过动态库加日志解决;5)平衡调试与代码阅读,以理解代码为目的;6)始终围绕线索展开分析,避免偏离核心问题。这些方法强调务实、聚焦和工具选择的灵活性,帮助开发者高效定位和解决问题。


【开题答辩全过程】以 SpringBootVue的旅游租车管理系统为例,包含答辩的问题和答案
毕设源码-钟学长9/30/2025

这是一篇关于毕业设计答辩的纪实内容,主要记录了&quot;基于SpringBoot+Vue的旅游租车管理系统&quot;的答辩过程。答辩学生详细介绍了系统功能(租车+旅游推荐)、技术架构(SpringBoot+Vue+MySQL)和关键实现(库存控制、登录认证等)。评委老师针对系统设计、数据一致性、推荐算法等提出专业问题,学生给出了具体的技术解决方案。最后评委肯定选题价值和技术实现,建议完善旅游信息模块。文末提供开题报告参考和毕设指导服务,适合处于开题阶段的学生参考使用。全文突出了系统设计细节和答辩应对技巧


【鸿蒙生态共建】一文说清复杂类型数据的非预期输入转换与兜底-《精通HarmonyOS NEXT :鸿蒙App开发入门与项目化实战》读者福利
俩毛豆2025/10/7

在客户端开发中,你是否曾遇到过这样的困扰:一次看似寻常的网络数据解析,却导致了出人意料的崩溃;一个本该正常的文件读取操作,却返回了难以理解的数据错误。这些问题的根源,往往指向同一环节——数据类型转换。当应用面对网络传输、文件I/O等不可控的数据源时,如何稳健、准确地进行数据解析与转换,就成为保障应用稳定性的第一道防线。 本篇内容是《精通HarmonyOS NEXT :鸿蒙App开发入门与项目化实战》这本书第四章内容的延续,是咱这本书读者的福利,在本篇内容中以模拟多种数据输入,向复杂类型(类、数


Seata分布式事务框架详解与项目实战
IT橘子皮2025/10/9

一、Seata核心架构与原理 Seata(Simple Extensible Autonomous Transaction Architecture)是阿里巴巴开源的分布式事务解决方案,旨在为微服务架构提供高性能、易用性的分布式事务支持。其核心设计理念是"化繁为简",通过封装传统分布式事务模式的复杂性,降低分布式一致性问题的解决门槛。 ​核心组件​: ​TC(Transaction Coordinator)​​:事务协调者,维护全局事务和分支事务的状态,负责协调全局事务提交或回滚 ​TM(


我用亲身经历告诉你,为什么程序员千万别不把英语当回事
oioihoii2025/10/10

年轻人,如果你现在觉得写代码只需要认识 if/else 和 for 循环里的那几个英文单词就够了,那你简直像极了十年前的我。而今天的我,多想回到过去,给那个骄傲自满的自己一记响亮的耳光。 我不是以成功者的姿态来教导你,而是以一个踩过坑、吃过亏、肠子都悔青了的过来人身份,跟你聊聊我最后悔的一件事——没有早点学好英语。 一、工作里吃的哑巴亏,都是我当年脑子进的水 1. “啃”二手资料的酸楚 还记得那次,团队要引入一个热门的新框架。我兴冲冲地找了几篇中文博客,照猫画虎地搞了起来。结果,掉进了一个坑里,

首页编辑器站点地图

Copyright © 2025 聚合阅读

License: CC BY-SA 4.0