8.4RestTemplate与LoadBalancer

分类: Spring Cloud LoadBalancer

RestTemplate + LoadBalancer

RestTemplate 与 LoadBalancer 集成可以实现服务调用的负载均衡。本节将学习 RestTemplate + LoadBalancer。

本节将学习:@LoadBalanced 注解、RestTemplate 配置、服务调用示例,以及负载均衡验证。

@LoadBalanced 注解

注解说明

@LoadBalanced 注解:

  • 标记 RestTemplate Bean 启用负载均衡
  • 自动注入 LoadBalancerClient
  • 拦截 RestTemplate 请求
  • 替换服务名为实际实例地址

使用方式

@Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); }

RestTemplate 配置

基础配置

@Configuration public class RestTemplateConfig { @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } }

自定义配置

@Configuration public class RestTemplateConfig { @Bean @LoadBalanced public RestTemplate restTemplate(RestTemplateBuilder builder) { return builder .setConnectTimeout(Duration.ofSeconds(5)) .setReadTimeout(Duration.ofSeconds(10)) .build(); } }

多个 RestTemplate

@Configuration public class RestTemplateConfig { @Bean @LoadBalanced public RestTemplate loadBalancedRestTemplate() { return new RestTemplate(); } @Bean public RestTemplate restTemplate() { return new RestTemplate(); } }

在商城项目中的应用

订单服务中的实际应用

文件路径: mall-microservices/order-service/src/main/java/com/mall/orderservice/client/UserServiceClient.java

package com.mall.orderservice.client; import com.mall.orderservice.common.Result; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; @Component public class UserServiceClient { @Autowired private RestTemplate restTemplate; // 已配置 @LoadBalanced private static final String USER_SERVICE_URL = "http://user-service"; /** * 根据用户ID查询用户信息 * 使用服务名 user-service,LoadBalancer 会自动解析为实际地址 */ public Result getUserById(Long userId) { String url = USER_SERVICE_URL + "/api/users/" + userId; return restTemplate.getForObject(url, Result.class); } /** * 验证用户是否存在 */ public boolean userExists(Long userId) { try { Result result = getUserById(userId); return result != null && result.getCode() == 200; } catch (Exception e) { return false; } } /** * 根据用户名查询用户 */ public Result getUserByUsername(String username) { String url = USER_SERVICE_URL + "/api/users/username/" + username; return restTemplate.getForObject(url, Result.class); } }

商品服务客户端

文件路径: mall-microservices/order-service/src/main/java/com/mall/orderservice/client/ProductServiceClient.java

package com.mall.orderservice.client; import com.mall.orderservice.common.Result; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; @Component public class ProductServiceClient { @Autowired private RestTemplate restTemplate; private static final String PRODUCT_SERVICE_URL = "http://product-service"; /** * 根据商品ID查询商品信息 */ public Result getProductById(Long productId) { String url = PRODUCT_SERVICE_URL + "/api/products/" + productId; return restTemplate.getForObject(url, Result.class); } /** * 查询商品列表(带分页) */ public Result getProducts(Integer page, Integer size, Long categoryId) { String url = PRODUCT_SERVICE_URL + "/api/products?page=" + page + "&size=" + size; if (categoryId != null) { url += "&categoryId=" + categoryId; } return restTemplate.getForObject(url, Result.class); } }

POST 请求

public User createUser(User user) { String url = "http://user-service/api/users"; return restTemplate.postForObject(url, user, User.class); }

PUT 请求

public void updateUser(Long id, User user) { String url = "http://user-service/api/users/" + id; restTemplate.put(url, user); }

DELETE 请求

public void deleteUser(Long id) { String url = "http://user-service/api/users/" + id; restTemplate.delete(url); }

带参数的请求

public List<User> getUsers(String name, Integer age) { String url = "http://user-service/api/users?name={name}&age={age}"; Map<String, Object> params = new HashMap<>(); params.put("name", name); params.put("age", age); return restTemplate.getForObject(url, List.class, params); }

在商城项目中验证负载均衡

验证步骤

步骤1:启动多个用户服务实例

启动第一个实例(端口 8081):

cd mall-microservices/user-service mvn spring-boot:run

启动第二个实例(端口 8082):

修改 application.yml 或使用命令行参数:

# 方式1:修改 application.yml 中的 server.port 为 8082 # 方式2:使用命令行参数 mvn spring-boot:run -Dspring-boot.run.arguments=--server.port=8082

步骤2:添加测试接口

文件路径: mall-microservices/user-service/src/main/java/com/mall/userservice/controller/UserController.java

添加测试接口:

@GetMapping("/test/loadbalance") public Result<String> testLoadBalance() { String port = environment.getProperty("server.port"); String message = "Response from user-service, port: " + port; return Result.success(message); }

步骤3:创建测试 Controller

文件路径: mall-microservices/order-service/src/main/java/com/mall/orderservice/controller/LoadBalanceTestController.java

package com.mall.orderservice.controller; import com.mall.orderservice.client.UserServiceClient; import com.mall.orderservice.common.Result; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import java.util.ArrayList; import java.util.List; @RestController @RequestMapping("/api/test") public class LoadBalanceTestController { @Autowired private RestTemplate restTemplate; @Autowired private UserServiceClient userServiceClient; /** * 测试负载均衡 * 多次调用用户服务,观察请求分发情况 */ @GetMapping("/loadbalance") public Result<List<String>> testLoadBalance() { List<String> results = new ArrayList<>(); String url = "http://user-service/api/users/test/loadbalance"; // 调用10次,观察负载均衡效果 for (int i = 0; i < 10; i++) { try { Result result = restTemplate.getForObject(url, Result.class); if (result != null && result.getData() != null) { results.add("Request " + (i + 1) + ": " + result.getData()); } } catch (Exception e) { results.add("Request " + (i + 1) + ": Error - " + e.getMessage()); } } return Result.success(results); } }

步骤4:执行测试

# 调用测试接口 curl http://localhost:8083/api/test/loadbalance # 预期结果:应该看到请求被分发到不同的端口(8081 和 8082)

验证结果分析

预期结果:

如果负载均衡正常工作,10次请求应该大致均匀地分发到两个实例:

  • 大约5次请求到端口 8081
  • 大约5次请求到端口 8082

查看日志:

分别查看两个用户服务实例的日志,确认请求分发情况。

负载均衡策略验证

默认策略(轮询):

  • 第一次请求 → 实例1
  • 第二次请求 → 实例2
  • 第三次请求 → 实例1
  • 第四次请求 → 实例2
  • ...

验证方法:

多次调用测试接口,观察返回的端口号,应该看到端口号交替出现。

工作原理

请求拦截

请求拦截流程:

  1. RestTemplate 发起请求
  2. LoadBalancerInterceptor 拦截请求
  3. 从服务名解析服务实例列表
  4. 根据负载均衡策略选择实例
  5. 替换服务名为实际 IP:Port
  6. 执行 HTTP 请求

服务名解析

服务名解析:

  • http://user-service/api/usershttp://192.168.1.100:8080/api/users
  • 服务名必须与 Nacos 注册的服务名一致
  • 自动从服务发现组件获取实例列表

官方资源

本节小结

在本节中,我们学习了:

第一个是 @LoadBalanced 注解。 使用 @LoadBalanced 注解启用负载均衡。

第二个是 RestTemplate 配置。 如何配置 RestTemplate。

第三个是服务调用示例。 如何使用 RestTemplate 调用服务。

第四个是负载均衡验证。 如何验证负载均衡效果。

这就是 RestTemplate + LoadBalancer。使用 RestTemplate 和 LoadBalancer 可以实现服务调用的负载均衡。

在下一节,我们将学习自定义负载均衡器。