## 一、Ribbon负载均衡 Ribbon 的核心作用是**在微服务间调用时,实现客户端的负载均衡**。 #### 1、负载均衡的类型 ##### 服务器端负载均衡 在服务消费者和提供者之间有一个独立的负载均衡器(如 Nginx、F5)。消费者将请求发往负载均衡器,由它来分发流量。 `用户服务 -> Nginx -> [服务A实例1, 服务A实例2, 服务A实例3]` - **优点**:集中管理,配置简单。 - **缺点**:所有流量都经过一个中心点,可能成为性能瓶颈和单点故障。 ##### 客户端负载均衡(Ribbon 的模式) 负载均衡的逻辑**不在一个独立的服务器上,而是内嵌在每一个服务消费者客户端里**。 `用户服务(内置Ribbon) -> 直接从注册中心获取列表 -> 直接选择并调用 服务A实例2` - **优点**: - **去中心化**:没有单点瓶颈和故障风险。 - **更高效**:客户端直接与服务提供者通信,减少了一次网络跳转。 - **更智能**:客户端可以获取更丰富的服务器信息(如健康状况、元数据),实现更复杂的负载均衡策略。 #### 2、工作流程 当服务消费者(如 `consumer`)使用集成了 Ribbon 的组件(如 `RestTemplate` 或 `OpenFeign`)调用另一个服务(如 `provider`)时,Ribbon 会执行以下步骤: 1. **拦截请求**:Ribbon 会拦截到你的调用请求,例如对 `http://provider/user/id/{id}` 的调用。 2. **解析服务名**:Ribbon 从 URL 中提取服务标识(这里是 `provider`)。 3. **服务发现**:Ribbon 向服务注册中心(如 Eureka)查询名为 `provider` 的所有健康实例列表。 4. **负载均衡决策**:Ribbon 根据配置的负载均衡规则(如轮询、随机等),从实例列表中选择一个目标实例。 5. **重构请求**:Ribbon 将原始请求中的服务名(`provider`)替换为选中的真实服务器地址(如 `http://192.168.1.101:8001`)。 6. **发送请求**:最后,由 HTTP 客户端(如 RestTemplate)将请求发送到重构后的真实地址。 #### 3、创建多个微服务 复制原有的`provider`模块,并修改端口为`11001` ```yml spring: application: name: provider #两个服务名一致(服务名不能使用下划线) server: port: 11001 #eureka配置 eureka: client: serviceUrl: defaultZone: http://localhost:13000/eureka/,http://localhost:13001/eureka/ instance: #使用ip地址向Eureka注册(默认为主机名) prefer-ip-address: true #上面的配置已经可以使用ip注册了,但显示的还是主机名,所以这里设置显示的注册名 instance-id: ${spring.cloud.client.ip-address}:${server.port} ``` **注意**:两个服务的名称需要一致且服务名称不能使用下划线。 启动服务后查看服务列表  #### 4、使用Ribbon Eureka已经内置了Ribbon,所以不需要添加额外的依赖,只需在消费者`RestTemplate`的`Bean`上添加`@LoadBalanced`注解即可。 修改`消费者`主启动类中的`RestTemplate` ```java 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; @SpringBootApplication public class ConsumerApplication { /** * 使用spring提供的RestTemplate发送http请求调用微服务 * 这里创建RestTemplate对象交给容器管理 */ @Bean @LoadBalanced public RestTemplate getRestTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } ``` 修改`消费者`访问接口的地址,从IP改微服务的名称 ```java import cn.doooog.consumer.po.CommonResult; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @RequestMapping("/test") @RestController public class TestController { @Autowired private RestTemplate restTemplate; @RequestMapping("/id/{id}") public CommonResult test(@PathVariable("id") Integer id) { return restTemplate.getForObject( "http://provider/user/id/" + id, CommonResult.class); } } ``` #### 5、测试负载均衡 可以将两个微服务返回值更改以区分 修改`po/CommonResult.java` ```java // 11000 public static CommonResult success(T result) { CommonResult commonResult = new CommonResult<>(); commonResult.setCode(200); commonResult.setMessage("(11000)操作成功"); commonResult.setResult(result); return commonResult; } // 11001 public static CommonResult success(T result) { CommonResult commonResult = new CommonResult<>(); commonResult.setCode(200); commonResult.setMessage("(11001)操作成功"); commonResult.setResult(result); return commonResult; } ``` 访问`http://localhost:12000/test/id/112312`即可看到轮询负载结果   #### 6、负载均衡策略 ##### 分类 * **轮询策略**(com.netflix.loadbalancer.RoundRobinRule) * **随机策略**(com.netflix.loadbalancer.RandomRule) * **重试策略**(com.netflix.loadbalancer.RetryRule):在一个配置时间段内(超时时间),当选择服务实例不成功,则一直尝试选择一个可用的服务实例。 * **权重策略**(com.netflix.loadbalancer.WeightedResponseTimeRule):会计算每个服务的权重,越高的被调用的可能性越大。 * **最佳策略**(com.netflix.loadbalancer.BestAvailableRule):遍历所有的服务实例,过滤掉故障实例,并返回请求数最小的实例。 * **可用过滤策略**(com.netflix.loadbalancer.AvailabilityFilteringRule):过滤掉故障和请求数超过阈值的服务实例,再从剩下的实力中轮询调用。 ##### 修改 可以通过修改`消费者`的项目配置文件来进行修改 ```yml #需要调用的微服务名称 provider: ribbon: #设置使用哪一种负载均衡策略类(这里使用随机策略类) NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule ``` ## 二、Feign服务调用 Feign 的核心作用可以概括为:声明式的 HTTP 客户端。它让微服务之间的远程调用变得像调用本地方法一样简单直观。 #### 1、Feign有什么用 在没有 Feign 之前,我们通常使用 `RestTemplate` 来调用其他服务的 HTTP API。这种方式存在几个痛点: **代码繁琐**:需要手动构建 URL、设置 HTTP 方法、处理请求/响应头、序列化/反序列化对象。 **可读性差**:业务代码中混杂着大量的 HTTP 调用细节,如 `restTemplate.getForObject("http://provider/users/" + userId, User.class)`,真正的业务意图被技术细节掩盖。 **难以维护**:URL 路径、参数等都以字符串形式硬编码在代码中,容易出错,且修改起来麻烦。 **缺乏强类型检查**:参数和返回值的类型匹配错误只能在运行时发现。 Feign 的出现,就是为了消除这些样板代码,让开发者能专注于业务逻辑本身。 #### 2、添加依赖 在`消费者`模块中添加下面的起步依赖 ```xml org.springframework.cloud spring-cloud-starter-openfeign ``` #### 3、使用Feign ##### 主启动类 添加注解`@EnableFeignClients`并且注释掉`RestTemplate`的Bean ```java import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication //开启Spring Cloud Feign的支持功能 @EnableFeignClients public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } ``` ##### 创建接口 创建`feign/TestFeignClient.java`接口 ```java import cn.doooog.consumer.po.CommonResult; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @FeignClient(name = "provider") public interface TestFeignClient { //配置需要调用的挂号服务接口。与UserController中的方法定义一致 @GetMapping("/user/id/{userId}") CommonResult getUserById(@PathVariable("userId") Integer userId); } ``` ##### 修改控制器 修改`TestController.java`中的调用 ```java import cn.doooog.consumer.feign.TestFeignClient; import cn.doooog.consumer.po.CommonResult; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RequestMapping("/test") @RestController public class TestController { @Autowired private TestFeignClient testFeignClient; @RequestMapping("/id/{id}") public CommonResult test(@PathVariable("id") Integer id) { return testFeignClient.getUserById(id); } } ``` 启动项目即可使用 Loading... ## 一、Ribbon负载均衡 Ribbon 的核心作用是**在微服务间调用时,实现客户端的负载均衡**。 #### 1、负载均衡的类型 ##### 服务器端负载均衡 在服务消费者和提供者之间有一个独立的负载均衡器(如 Nginx、F5)。消费者将请求发往负载均衡器,由它来分发流量。 `用户服务 -> Nginx -> [服务A实例1, 服务A实例2, 服务A实例3]` - **优点**:集中管理,配置简单。 - **缺点**:所有流量都经过一个中心点,可能成为性能瓶颈和单点故障。 ##### 客户端负载均衡(Ribbon 的模式) 负载均衡的逻辑**不在一个独立的服务器上,而是内嵌在每一个服务消费者客户端里**。 `用户服务(内置Ribbon) -> 直接从注册中心获取列表 -> 直接选择并调用 服务A实例2` - **优点**: - **去中心化**:没有单点瓶颈和故障风险。 - **更高效**:客户端直接与服务提供者通信,减少了一次网络跳转。 - **更智能**:客户端可以获取更丰富的服务器信息(如健康状况、元数据),实现更复杂的负载均衡策略。 #### 2、工作流程 当服务消费者(如 `consumer`)使用集成了 Ribbon 的组件(如 `RestTemplate` 或 `OpenFeign`)调用另一个服务(如 `provider`)时,Ribbon 会执行以下步骤: 1. **拦截请求**:Ribbon 会拦截到你的调用请求,例如对 `http://provider/user/id/{id}` 的调用。 2. **解析服务名**:Ribbon 从 URL 中提取服务标识(这里是 `provider`)。 3. **服务发现**:Ribbon 向服务注册中心(如 Eureka)查询名为 `provider` 的所有健康实例列表。 4. **负载均衡决策**:Ribbon 根据配置的负载均衡规则(如轮询、随机等),从实例列表中选择一个目标实例。 5. **重构请求**:Ribbon 将原始请求中的服务名(`provider`)替换为选中的真实服务器地址(如 `http://192.168.1.101:8001`)。 6. **发送请求**:最后,由 HTTP 客户端(如 RestTemplate)将请求发送到重构后的真实地址。 #### 3、创建多个微服务 复制原有的`provider`模块,并修改端口为`11001` ```yml spring: application: name: provider #两个服务名一致(服务名不能使用下划线) server: port: 11001 #eureka配置 eureka: client: serviceUrl: defaultZone: http://localhost:13000/eureka/,http://localhost:13001/eureka/ instance: #使用ip地址向Eureka注册(默认为主机名) prefer-ip-address: true #上面的配置已经可以使用ip注册了,但显示的还是主机名,所以这里设置显示的注册名 instance-id: ${spring.cloud.client.ip-address}:${server.port} ``` **注意**:两个服务的名称需要一致且服务名称不能使用下划线。 启动服务后查看服务列表  #### 4、使用Ribbon Eureka已经内置了Ribbon,所以不需要添加额外的依赖,只需在消费者`RestTemplate`的`Bean`上添加`@LoadBalanced`注解即可。 修改`消费者`主启动类中的`RestTemplate` ```java 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; @SpringBootApplication public class ConsumerApplication { /** * 使用spring提供的RestTemplate发送http请求调用微服务 * 这里创建RestTemplate对象交给容器管理 */ @Bean @LoadBalanced public RestTemplate getRestTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } ``` 修改`消费者`访问接口的地址,从IP改微服务的名称 ```java import cn.doooog.consumer.po.CommonResult; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @RequestMapping("/test") @RestController public class TestController { @Autowired private RestTemplate restTemplate; @RequestMapping("/id/{id}") public CommonResult test(@PathVariable("id") Integer id) { return restTemplate.getForObject( "http://provider/user/id/" + id, CommonResult.class); } } ``` #### 5、测试负载均衡 可以将两个微服务返回值更改以区分 修改`po/CommonResult.java` ```java // 11000 public static <T> CommonResult<T> success(T result) { CommonResult<T> commonResult = new CommonResult<>(); commonResult.setCode(200); commonResult.setMessage("(11000)操作成功"); commonResult.setResult(result); return commonResult; } // 11001 public static <T> CommonResult<T> success(T result) { CommonResult<T> commonResult = new CommonResult<>(); commonResult.setCode(200); commonResult.setMessage("(11001)操作成功"); commonResult.setResult(result); return commonResult; } ``` 访问`http://localhost:12000/test/id/112312`即可看到轮询负载结果   #### 6、负载均衡策略 ##### 分类 * **轮询策略**(com.netflix.loadbalancer.RoundRobinRule) * **随机策略**(com.netflix.loadbalancer.RandomRule) * **重试策略**(com.netflix.loadbalancer.RetryRule):在一个配置时间段内(超时时间),当选择服务实例不成功,则一直尝试选择一个可用的服务实例。 * **权重策略**(com.netflix.loadbalancer.WeightedResponseTimeRule):会计算每个服务的权重,越高的被调用的可能性越大。 * **最佳策略**(com.netflix.loadbalancer.BestAvailableRule):遍历所有的服务实例,过滤掉故障实例,并返回请求数最小的实例。 * **可用过滤策略**(com.netflix.loadbalancer.AvailabilityFilteringRule):过滤掉故障和请求数超过阈值的服务实例,再从剩下的实力中轮询调用。 ##### 修改 可以通过修改`消费者`的项目配置文件来进行修改 ```yml #需要调用的微服务名称 provider: ribbon: #设置使用哪一种负载均衡策略类(这里使用随机策略类) NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule ``` ## 二、Feign服务调用 Feign 的核心作用可以概括为:声明式的 HTTP 客户端。它让微服务之间的远程调用变得像调用本地方法一样简单直观。 #### 1、Feign有什么用 在没有 Feign 之前,我们通常使用 `RestTemplate` 来调用其他服务的 HTTP API。这种方式存在几个痛点: **代码繁琐**:需要手动构建 URL、设置 HTTP 方法、处理请求/响应头、序列化/反序列化对象。 **可读性差**:业务代码中混杂着大量的 HTTP 调用细节,如 `restTemplate.getForObject("http://provider/users/" + userId, User.class)`,真正的业务意图被技术细节掩盖。 **难以维护**:URL 路径、参数等都以字符串形式硬编码在代码中,容易出错,且修改起来麻烦。 **缺乏强类型检查**:参数和返回值的类型匹配错误只能在运行时发现。 Feign 的出现,就是为了消除这些样板代码,让开发者能专注于业务逻辑本身。 #### 2、添加依赖 在`消费者`模块中添加下面的起步依赖 ```xml <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-openfeign --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> ``` #### 3、使用Feign ##### 主启动类 添加注解`@EnableFeignClients`并且注释掉`RestTemplate`的Bean ```java import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication //开启Spring Cloud Feign的支持功能 @EnableFeignClients public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } ``` ##### 创建接口 创建`feign/TestFeignClient.java`接口 ```java import cn.doooog.consumer.po.CommonResult; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @FeignClient(name = "provider") public interface TestFeignClient { //配置需要调用的挂号服务接口。与UserController中的方法定义一致 @GetMapping("/user/id/{userId}") CommonResult getUserById(@PathVariable("userId") Integer userId); } ``` ##### 修改控制器 修改`TestController.java`中的调用 ```java import cn.doooog.consumer.feign.TestFeignClient; import cn.doooog.consumer.po.CommonResult; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RequestMapping("/test") @RestController public class TestController { @Autowired private TestFeignClient testFeignClient; @RequestMapping("/id/{id}") public CommonResult test(@PathVariable("id") Integer id) { return testFeignClient.getUserById(id); } } ``` 启动项目即可使用 最后修改:2025 年 12 月 09 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 👆请简单粗暴地爱我。