当前位置: 首页 > news >正文

Spring Cloud 负载均衡(LoadBalancer)与服务调用(OpenFeign)详解

Spring Cloud 负载均衡(LoadBalancer)与服务调用(OpenFeign)详解(初学者指南)

本文将聚焦 “多实例分流”(LoadBalancer)“简化服务调用”(OpenFeign) 两大核心能力 —— 这两项是微服务从 “单实例运行” 走向 “高可用、易维护” 的关键。本文会从 “为什么需要”“是什么”“怎么实现”“背后逻辑” 逐步拆解,同时关联之前熟悉的 ServiceOne、ServiceThree 等案例,帮你建立完整的微服务调用链路认知。

1. 负载均衡 LoadBalancer:解决多实例 “分流” 问题

当一个服务(如 ServiceOne)单实例扛不住压力(比如双 11 订单量激增)时,我们会部署多个实例(比如 ServiceOne+ServiceOneCopy),而LoadBalancer 的作用就是把用户请求 “均匀分给” 这些实例,避免单个实例忙死、其他实例空闲

1.1 先搞懂:为什么需要负载均衡?

回顾之前的案例,ServiceOne 只有 1 个实例(端口 8001),所有请求都会打到这台机器上 —— 这会有两个致命问题:

  1. 性能瓶颈:如果每秒有 1000 个请求,单实例只能处理 500 个,剩下的 500 个会超时失败;

  2. 单点故障:这个实例宕机了,所有依赖 ServiceOne 的服务(如 ServiceThree)都会调用失败。

而负载均衡就是解决这两个问题的 “方案”:

  • 部署多个实例(如 ServiceOne:8001、ServiceOneCopy:8004),总处理能力提升到 1000+;

  • 用 LoadBalancer 把请求 “轮询” 或 “随机” 分给多个实例,即使一个实例宕机,其他实例还能继续工作。

生活类比:火车站检票 —— 单个人工窗口(单实例)会排长队,开多个窗口(多实例),再安排引导员(LoadBalancer)分流,乘客就能快速检票(请求快速处理)。

1.2 什么是 Spring Cloud LoadBalancer?

  • 定位:Spring Cloud 官方开发的客户端负载均衡器,用来替代停更的 Netflix Ribbon;

  • “客户端” 含义:负载均衡逻辑在 “服务消费者”(如 ServiceThree)这边,而不是在独立的服务器上;

  • 核心能力:

    1. 从 Nacos 获取目标服务的所有实例列表(如 service-one 的 8001 和 8004 实例);

    2. 按默认算法(轮询:依次分给每个实例)对请求进行分流;

    3. 自动排除不健康的实例(如 8001 宕机,就只分给 8004)。

1.3 LoadBalancer 实战:从配置到验证(基于之前的案例扩展)

该案例是在之前的 “父项目 + ServiceOne/Two/Three” 基础上,新增 ServiceOneCopy 实例,修改 ServiceThree 实现负载均衡。我们一步步拆解:

步骤 1:新建 “分流实例” ServiceOneCopy(关键:服务名必须和 ServiceOne 一致)

要实现负载均衡,多个实例必须用同一个服务名(Nacos 按服务名找实例列表),所以需要复制 ServiceOne 生成 ServiceOneCopy,并做 3 处关键修改:

注意:拷贝module模块文件夹比较麻烦,建议直接新建module模块再逐步拷贝相关代码

1.1 复制 ServiceOne 的所有内容

把 ServiceOne 的pom.xmlapplication.yml、主类(ServiceOneApplication)、控制器(ServiceOneController)全部复制到 ServiceOneCopy 子项目。

1.2 修改 ServiceOneCopy 的 application.yml(仅改端口,服务名不变)
 server:port: 8004  # 关键:同一台机器部署,端口必须和ServiceOne(8001)不同,避免冲突spring:application:name: service-one  # 核心:服务名必须和ServiceOne完全一致!否则Nacos不认作同一服务cloud:nacos:discovery:server-addr: 127.0.0.1:8848  # 和ServiceOne一致,指向同一个Nacosservice: ${spring.application.name}  # 复用服务名,不用改
  • 为什么服务名必须一致?

    LoadBalancer 的逻辑是:“根据服务名从 Nacos 拉取所有实例”—— 如果 ServiceOneCopy 的服务名是service-one-copy,Nacos 会把它当成另一个服务,无法和 ServiceOne 一起分流。

1.3 修改主类名(方便 IDEA 区分启动项)

把复制来的ServiceOneApplication改名为ServiceOneCopyApplication(仅名称变,代码不变):

@SpringBootApplication@EnableDiscoveryClientpublic class ServiceOneCopyApplication {  // 改这里的类名public static void main(String[] args) {SpringApplication.run(ServiceOneCopyApplication.class, args);}}
1.4 修改控制器返回值(方便验证分流效果)

ServiceOneCopyControllerserviceOne方法中,给message加 “COPY” 字样,这样调用时能区分是哪个实例返回的:

 @RestControllerpublic class ServiceOneController {@RequestMapping("/serviceOne")public JSONObject serviceOne(){JSONObject ret = new JSONObject();ret.put("code", 0);// 关键:加"COPY"标识,和ServiceOne的返回区分ret.put("message", "Service one \"COPY\" method return!");return ret;}}
步骤 2:修改 ServiceThree(开启负载均衡能力)

ServiceThree 是调用方,需要新增负载均衡依赖、配置 RestTemplate、修改调用方式:

2.1 引入 LoadBalancer 依赖(pom.xml)

在 ServiceThree 的pom.xml中添加负载均衡 starter,让 Spring Cloud 提供负载均衡能力:

 <!-- 负载均衡核心依赖:给RestTemplate赋予分流能力 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency>
2.2 给 RestTemplate 加 @LoadBalanced 注解(主类)

之前我们在 ServiceThree 主类中配置了RestTemplate,现在需要加@LoadBalanced注解 —— 这个注解会 “增强” RestTemplate,让它能自动通过服务名找实例并分流:

 @SpringBootApplication@EnableDiscoveryClientpublic class ServiceThreeApplication {@Bean@LoadBalanced  // 关键:开启RestTemplate的负载均衡能力public RestTemplate getRestTemplate(){return new RestTemplate();}​public static void main(String[] args) {SpringApplication.run(ServiceThreeApplication.class, args);}}
  • @LoadBalanced 的作用:

    没有这个注解时,RestTemplate 只能通过 “IP + 端口” 调用(如http://127.0.0.1:8001/serviceOne);加了之后,它能解析 “服务名”(如http://service-one/serviceOne),自动去 Nacos 查service-one的所有实例,然后按轮询算法选一个实例调用。

2.3 修改调用方式:从 “IP + 端口” 改为 “服务名”(控制器)

之前我们用DiscoveryClient获取实例 IP + 端口,现在不用了 —— 直接用服务名service-one拼接 URL,@LoadBalanced会帮你处理剩下的:

 
@RestControllerpublic class ServiceThreeController {@Autowiredprivate RestTemplate restTemplate;​@RequestMapping("/serviceThree_toOne")public JSONObject serviceThree_toOne(){// 关键:URL用服务名"service-one"替代IP+端口String serviceOneUrl = "http://service-one/serviceOne";// RestTemplate会自动找service-one的实例,分流调用String strRet = restTemplate.getForObject(serviceOneUrl, String.class);​JSONObject ret = new JSONObject();ret.put("code", 0);ret.put("message", "Service three to one method return!");ret.put("data", strRet);return ret;}​// 其他方法(serviceThree_toTwo、serviceThree)不变...}
  • 为什么不用 DiscoveryClient 了?

    因为@LoadBalanced已经封装了 “从 Nacos 获取实例列表” 的逻辑,不用自己写discoveryClient.getInstances("service-one")

    和选实例的代码,简化开发。

步骤 3:结果验证:看负载均衡是否生效

按以下顺序启动服务,然后验证:

3.1 启动顺序(重要)
  1. 启动 Nacos Server(确保 8848 端口可用);

  2. 启动 ServiceOne(8001)和 ServiceOneCopy(8004);

  3. 启动 ServiceThree(8003)。

3.2 查看 Nacos 服务列表

访问http://127.0.0.1:8848/nacos,在 “服务列表” 中查看service-one——实例数会变成 2(一个 8001,一个 8004),说明两个实例都注册成功了:

服务名实例数健康实例数
service-one22
service-three11
3.3 调用接口验证分流

访问 ServiceThree 的/serviceThree_toOne接口(http://localhost:8003/serviceThree_toOne),连续调用两次:

  • 第一次返回(来自 ServiceOne:8001):

     {"code": 0,"message": "Service three to one method return!","data": "{\"code\":0,\"message\":\"Service one method return!\"}"}
  • 第二次返回(来自 ServiceOneCopy:8004):

     {"code": 0,"message": "Service three to one method return!","data": "{\"code\":0,\"message\":\"Service one \\\"COPY\\\" method return!\"}"}
  • 结论:两次调用返回不同结果,证明 LoadBalancer 按 “轮询” 算法把请求分给了两个实例,负载均衡生效!

1.4 LoadBalancer 核心逻辑:它是怎么工作的?

用流程图帮你理解 ServiceThree 调用 ServiceOne 时,LoadBalancer 的完整流程:

  • 默认算法:轮询(依次选实例),Spring Cloud LoadBalancer 目前还支持 “随机” 算法,可通过配置修改。

2. OpenFeign:让服务调用像 “调用本地方法” 一样简单

虽然 LoadBalancer 解决了分流问题,但用RestTemplate调用仍有痛点:

  • 硬编码 URL(如http://service-one/serviceOne),接口改了要手动改 URL;

  • 没有统一的接口管理,多个地方调用同一个接口时,改接口要改多处;

  • 需要自己处理请求参数和响应解析(如把 String 转 JSONObject)。

OpenFeign 的出现就是为了解决这些问题—— 它是 “声明式服务调用框架”,让你通过 “接口 + 注解” 的方式调用远程服务,就像调用本地方法一样,不用写RestTemplate的代码。

2.1 为什么需要 OpenFeign?(对比 RestTemplate)

用表格直观展示两者的差异,你就懂 OpenFeign 的优势了:

对比维度RestTemplate(之前用的)OpenFeign(现在要学的)
调用方式硬编码 URL,手动调用getForObject定义接口,注入后像本地方法一样调用
接口管理无统一管理,改接口要改所有调用处接口集中定义,改接口只改一处
参数 / 响应处理手动处理(如 String 转 JSONObject)自动解析,支持 JSON/JavaBean
负载均衡集成需要手动加@LoadBalanced默认集成 LoadBalancer,不用额外配置
代码简洁度代码多,冗余代码少,仅需定义接口

举个例子:之前用 RestTemplate 调用 ServiceOne:

 // 硬编码URLString url = "http://service-one/serviceOne";// 手动调用+解析String strRet = restTemplate.getForObject(url, String.class);JSONObject data = JSONObject.parseObject(strRet);

用 OpenFeign 调用 ServiceOne:

 // 注入Feign接口@Autowiredprivate ServiceOneFeign serviceOneFeign;​// 像调用本地方法一样调用JSONObject data = serviceOneFeign.serviceOne();

2.2 什么是 OpenFeign?

  • 定位:Spring Cloud 官方推出的声明式服务调用框架,替代停更的 Netflix Feign;

  • 核心特点:

    1. 声明式:只定义接口,不用写实现类(Spring 会动态生成代理类);

    2. 兼容 Spring MVC 注解:支持@RequestMapping@GetMapping等,不用学新注解;

    3. 自动集成负载均衡:默认用 Spring Cloud LoadBalancer,不用额外加@LoadBalanced

    4. 自动解析请求 / 响应:支持 JSON、JavaBean 等格式,不用手动转。

2.3 OpenFeign 实战:从配置到调用(基于 ServiceThree 改造)

案例是在 LoadBalancer 的基础上,用 OpenFeign 替代 RestTemplate,简化 ServiceThree 的调用逻辑:

步骤 1:修改 ServiceThree 的 pom.xml(引入 OpenFeign 依赖)

在 ServiceThree 的pom.xml中添加 OpenFeign starter:

 <!-- OpenFeign核心依赖:提供声明式调用能力 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency>
  • 注意:OpenFeign 默认集成了 LoadBalancer,所以不用再单独引入spring-cloud-starter-loadbalancer(如果之前加了也没关系,不冲突)。

步骤 2:开启 OpenFeign 功能(主类加注解)

在 ServiceThree 的主类上添加@EnableFeignClients注解 —— 这个注解会让 Spring 扫描所有带@FeignClient的接口,生成代理类并交给 Spring 容器管理:

 @SpringBootApplication@EnableDiscoveryClient@EnableFeignClients  // 关键:开启OpenFeign功能public class ServiceThreeApplication {// 注意:用了OpenFeign后,RestTemplate不用了,可以注释掉// @Bean// @LoadBalanced// public RestTemplate getRestTemplate(){//     return new RestTemplate();// }​public static void main(String[] args) {SpringApplication.run(ServiceThreeApplication.class, args);}}
  • 为什么注释 RestTemplate?

    OpenFeign 已经封装了服务调用和负载均衡的逻辑,不用再手动用 RestTemplate 了,代码更简洁。

步骤 3:编写 Feign 接口(核心:映射服务提供者的接口)

Feign 接口的作用是 “映射服务提供者的接口”—— 接口名、路径、参数、返回值必须和服务提供者(如 ServiceOne)完全一致,这样 Spring 才能生成正确的 HTTP 请求。

3.1 新建 Feign 接口包和接口

在 ServiceThree 的com.zh.three包下新建feign子包,然后创建两个接口:ServiceOneFeign(映射 ServiceOne)和ServiceTwoFeign(映射 ServiceTwo)。

3.2 编写 ServiceOneFeign 接口(映射 ServiceOne 的 /serviceOne)
 package com.zh.three.feign;​import com.alibaba.fastjson.JSONObject;import org.springframework.cloud.openfeign.FeignClient;import org.springframework.web.bind.annotation.RequestMapping;​// @FeignClient:指定要调用的服务名(必须和ServiceOne的spring.application.name一致)@FeignClient("service-one")public interface ServiceOneFeign {// 方法定义:必须和ServiceOne的Controller方法完全一致(路径、参数、返回值)@RequestMapping("/serviceOne")  // 路径和ServiceOne的接口路径一致JSONObject serviceOne();  // 返回值和ServiceOne的接口返回值一致}
  • @FeignClient("service-one"):告诉 OpenFeign “这个接口对应 Nacos 中服务名为 service-one 的服务”。

  • 方法必须和服务提供者一致:如果 ServiceOne 的接口路径改了(如从/serviceOne改成/api/serviceOne),这里的@RequestMapping也要跟着改,否则会 404。

3.3 编写 ServiceTwoFeign 接口(映射 ServiceTwo 的 /serviceTwo)

逻辑和ServiceOneFeign完全一致,只是服务名和接口路径对应 ServiceTwo:

package com.zh.three.feign;​import com.alibaba.fastjson.JSONObject;import org.springframework.cloud.openfeign.FeignClient;import org.springframework.web.bind.annotation.RequestMapping;​@FeignClient("service-two")  // 服务名对应ServiceTwo的spring.application.namepublic interface ServiceTwoFeign {@RequestMapping("/serviceTwo")  // 路径对应ServiceTwo的接口路径JSONObject serviceTwo();  // 返回值对应ServiceTwo的接口返回值}
步骤 4:在 Controller 中注入 Feign 接口调用(替代 RestTemplate)

修改 ServiceThree 的 Controller,删除 RestTemplate 相关代码,直接注入 Feign 接口调用:

@RestController  // 之前是@Controller,这里用@RestTemplate更简洁,不用加@ResponseBodypublic class ServiceThreeController {// 1. 注入ServiceOneFeign接口(Spring自动生成代理类)@Autowiredprivate ServiceOneFeign serviceOneFeign;​// 2. 注入ServiceTwoFeign接口@Autowiredprivate ServiceTwoFeign serviceTwoFeign;​// 调用ServiceOne:像调用本地方法一样@RequestMapping("/serviceThree_toOne")public JSONObject serviceThree_toOne(){// 关键:直接调用Feign接口方法,不用写RestTemplate和URLJSONObject serviceOneRet = serviceOneFeign.serviceOne();​JSONObject ret = new JSONObject();ret.put("code", 0);ret.put("message", "Service three to one method return!");ret.put("data", serviceOneRet);return ret;}​// 调用ServiceTwo:同理@RequestMapping("/serviceThree_toTwo")public JSONObject serviceThree_toTwo(){JSONObject serviceTwoRet = serviceTwoFeign.serviceTwo();​JSONObject ret = new JSONObject();ret.put("code", 0);ret.put("message", "Service three to two method return!");ret.put("data", serviceTwoRet);return ret;}​// 自身接口不变@RequestMapping("/serviceThree")public JSONObject serviceThree(){JSONObject ret = new JSONObject();ret.put("code", 0);ret.put("message", "Service three method return!");return ret;}}
步骤 5:结果验证:OpenFeign 是否生效?

和 LoadBalancer 的验证步骤一致,启动所有服务后调用接口:

5.1 验证负载均衡(依然生效)

访问http://localhost:8003/serviceThree_toOne,连续两次:

  • 第一次返回 ServiceOne(8001)的结果;

  • 第二次返回 ServiceOneCopy(8004)的结果;

  • 结论:OpenFeign 默认集成了 LoadBalancer,所以负载均衡依然生效,不用额外配置。

5.2 验证 ServiceTwo 调用

访问http://localhost:8003/serviceThree_toTwo,返回 ServiceTwo 的结果:

 {"code": 0,"message": "Service three to two method return!","data": {"code": 0,"message": "Service two method return!"}}
  • 结论:Feign 接口正确映射 ServiceTwo 的接口,调用成功。

2.4 OpenFeign 核心逻辑:动态代理是关键

你可能会好奇:Feign 接口没有实现类,为什么能调用?核心是动态代理——Spring 启动时会给 Feign 接口生成一个 “代理实现类”,代理类里封装了所有服务调用的逻辑:

3. 重点 & 易错点总结(初学者必看)

这部分是我们后续编码时最容易踩坑的地方,务必牢记:

3.1 LoadBalancer 重点 & 易错点

重点 / 易错点说明解决方案
多实例服务名必须一致负载均衡按服务名找实例,若 ServiceOne 和 ServiceOneCopy 的服务名不同,Nacos 会当两个服务,无法分流确保所有实例的spring.application.name完全一致(如都为service-one
@LoadBalanced 注解不能漏没加这个注解,RestTemplate 无法解析服务名,会报 “未知主机” 错误给 RestTemplate 的 @Bean 方法加@LoadBalanced
端口不能冲突同一台机器部署多个实例,端口必须不同,否则启动失败每个实例的server.port单独设置(如 8001、8004)
实例必须健康Nacos 会剔除不健康的实例,若实例宕机,LoadBalancer 不会分给它请求确保所有实例启动成功,Nacos 中 “健康实例数” 等于 “实例数”

3.2 OpenFeign 重点 & 易错点

重点 / 易错点说明解决方案
@EnableFeignClients 必须加没加这个注解,Spring 不会扫描 Feign 接口,注入时会报 “找不到 Bean” 错误在 ServiceThree 主类上添加@EnableFeignClients
Feign 接口方法必须和服务提供者一致路径、参数、返回值有一个不一致,会报 404 或解析错误严格对照服务提供者的 Controller 方法,确保:1. @RequestMapping路径一致;2. 参数个数和类型一致;3. 返回值类型一致(如都是 JSONObject)
@FeignClient 的服务名不能错服务名错了,OpenFeign 找不到对应的服务,会报 “服务不可用” 错误确保@FeignClient的值和服务提供者的spring.application.name一致(如service-one
不用手动加 LoadBalancer 依赖OpenFeign 默认集成了 LoadBalancer,重复加依赖不报错,但冗余只加spring-cloud-starter-openfeign即可,不用额外加spring-cloud-starter-loadbalancer

4. 与之前案例的关联关系(梳理完整链路)

结合我们之前学的 Nacos、ServiceOne/Two/Three,现在整个微服务调用链路已经完整了:

  1. 服务注册:ServiceOne、ServiceOneCopy、ServiceTwo 启动后,注册到 Nacos(服务名分别为service-oneservice-oneservice-two);

  2. 服务发现与分流:ServiceThree 通过 OpenFeign(集成 LoadBalancer),按服务名从 Nacos 获取实例列表,然后轮询分流;

  3. 服务调用:ServiceThree 调用service-one时,OpenFeign 自动找实例并发起请求,像调用本地方法一样简单;

  4. 动态配置:后续若要修改 ServiceOne 的配置(如数据库地址),可放到 Nacos 配置中心,不用重启实例(之前学的 Nacos 配置中心能力)。

完整链路图

5. 后续学习方向

掌握了 LoadBalancer 和 OpenFeign 后,下一步可以学习:

  1. 服务熔断降级(Sentinel):当 ServiceOne 所有实例都宕机时,ServiceThree 调用不会一直等,而是返回 “友好提示”(如 “服务暂时不可用”),避免 ServiceThree 也挂掉;

  2. 服务网关(Spring Cloud Gateway):所有客户端请求先过网关,统一处理鉴权、路由、限流,不用每个服务都写鉴权逻辑;

  3. 分布式事务(Seata):当 ServiceThree 调用 ServiceOne 和 ServiceTwo 时,确保两个服务的操作要么全成功,要么全失败(如下单时扣库存和创建订单必须同时成功)。

通过此次学习,希望你能理解:LoadBalancer 解决 “多实例分流” 的性能问题,OpenFeign 解决 “服务调用繁琐” 的开发效率问题 —— 两者结合,让微服务既高效又易用。

http://www.dtcms.com/a/410017.html

相关文章:

  • 基于HTTP构建局域网内YUM网络源:详细操作指南(太细)
  • Java核心 之JVM
  • 通过 GAC Code 在国内使用ClaudeCode,Windows 用户配置指南!
  • iOS App 上架流程详解,苹果应用发布步骤、App Store 审核规则、ipa 文件上传与测试分发实战经验
  • 线程安全之《Sychronized的八锁案例》
  • 用户态的epoll实现思路?
  • TextMeshPro文字消失bug解决方案
  • 学习网站开发教程wordpress 五分钟
  • 联邦学习中的异质性问题
  • 将照片从iPhone传输到联想笔记本的6种方法
  • 东莞网站设计找哪里易商官方网站
  • 本机可以做网站的服务器互联斗士网站建站
  • 基于 GitLab 的自动化镜像构建
  • Spark核心Shuffle详解(一)ShuffleManager
  • Android 开发环境解析:从SDK、NDK到版本兼容性指南
  • 基于YOLO8+flask+layui的行人跌倒行为检测系统【源码+模型+数据集】
  • Mysql DBA学习笔记(日志)
  • 平替MongoDB:金仓多模数据库助力电子证照国产化实践
  • QT6中QGraphicsView功能与应用
  • WSL2搭建Hadoop伪分布式环境
  • 新闻媒体发稿平台排名Top5,聚合型新闻发稿服务平台推荐
  • Linux(4)|入门的开始:Linux基本指令(4)
  • (七)API 重构的艺术:打造优雅、可维护的 API
  • MAC idea 环境变量设置失效
  • 百度站长收录提交入口深圳设计网站源码
  • 2025Unity超详细《坦克大战3D》项目实战案例(上篇)——UI搭建并使用和数据持久化(附资源和源代码)
  • DenseNet:密集连接
  • 第一次学习Hardhat
  • 腾讯wordpress 建站自适应网站建设哪家便宜
  • 第八章 MyBatis及MyBatis-Plus