Spring Cloud 框架 -- RestTemplate 与客户端负载均衡
RestTemplate 介绍
RestTemplate 是从 Spring3.0 开始支持的一个 http 请求工具,这个请求工具是 Spring 自带的,与 Spring Boot 和 Spring Cloud 都无关。
RestTemplate 提供了常见的 REST 请求方法模板,如 GET、POST、PUT、DELETE 请求以及一些通用的请求执行方法 exchange 和 execute 方法。
RestTemplate 本身实现了 RestOperations 接口,而在 RestOperations 接口中,定义了常见 RESTful 操作,这些操作在 RestTemplate 中都得到了很好的实现。
GET
首先,在 provider 中定义一个 hello2 接口:
@GetMapping("/hello2")
public String hello2(String name){
return "hello" +name;
}
接下来,在 consumer 中去访问这个接口,也就是调用 RestTemplate 中的 GET 请求。
可以看到,在 RestTemplate 中,关于 GET 请求,一共有两大类方法:
这两大类方法实际是重载的,唯一不同的,就是返回值类型。
-
getForObject 返回的是一个对象,这个对象就是服务端返回的具体值。
-
getForEntity 返回的是一个 ResponseEntity,这个 ResponseEntity 中除了服务端返回的具体数据外,还保留了 http 响应头的数据。
@GetMapping("/hello4")
public void hello4(){
String s1 = restTemplate.getForObject("http://PROVIDER/hello?name={1}", String.class, "youlinwei");
System.out.println(s1);
ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://PROVIDER/hello", String.class, "youlinwei");
String body = responseEntity.getBody();
System.out.println("body:" +body);
HttpStatus statusCode = responseEntity.getStatusCode();
System.out.println("HttpStatus:" + statusCode);
int statusCodeValue = responseEntity.getStatusCodeValue();
System.out.println("statusCodeValue:" +statusCodeValue);
HttpHeaders headers = responseEntity.getHeaders();
Set<String> keySet = headers.keySet();
System.out.println("----------- header ----------");
for (String s: keySet){
System.out.println(s + ":" + headers.get(s));
}
}
POST
首先在 provider 中提供两个 POST 接口,同时,因为 POST 请求可能需要传递 JSON ,所以,这里我们创建一个普通的 Maven 项目作为 commons 模块,然后这个 commons 模块被 provider 和 consumer 共同引用,这样我们就可以方便的传递 JSON 了。
commons 模块创建成功后,首先在 commons 模块中添加 User 对象,然后该模块分别被 provider 和 consumer 引用,即在 provider/pom.xml 和 consumer/pom.xml 中添加如下依赖:
<dependency>
<groupId>com.example</groupId>
<artifactId>commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
然后,我们在 provider 中提供两个 POST 接口:
// 以 key-value 形式传参
@PostMapping("/user1")
public User addUser1(User user){
return user;
}
// 以 json 形式传参
@PostMapping("/user2")
public User addUser2(@RequestBody User user){
return user;
}
这里定义了两个 User 添加的方法,两个方法代表了两种不同的传参方式。第一种方法是以 key-value 形式传参,第二种是以 json 形式传参。
定义完成后,接下来,我们在 commons 中调用这两个 POST 接口。
如上图,RestTemplate 的 POST 方法和前面的 GET 方法很像。只是多出来一个 postForLocation 方法。
这里,我们先使用 postForObject ,然后再看看 postForLocation。
@GetMapping("/hello6")
public void hello6(){
LinkedMultiValueMap<Object, Object> map = new LinkedMultiValueMap<>();
map.add("username","you");
map.add("password","123");
map.add("id",11);
// 以 key-value 形式传参
User user = restTemplate.postForObject("http://PROVIDER/user1", map, User.class);
System.out.println(user);
// 以 json 形式传参
user.setId(12);
user = restTemplate.postForObject("http://PROVIDER/user2", user, User.class);
System.out.println(user);
}
如果 postForObject 方法的第二个参数 MultiValueMap ,则参数是以 key-value 形式传递;如第二个参数是普通对象,则参数是以 json 形式来传递的。
最后再看看 postForLocation ,有的时候,当我执行完一个 post 请求之后,立马要进行重定向。比如注册的时候,注册是一个 post 请求,注册完成后,立马重定向到登录页面。这个时候就需要使用 postForLocation。
首先,在 provider 上提供一个用户注册接口:
@Controller
public class RegisterController {
@PostMapping("/register")
public String register(User user){
return "redirect:http://PROVIDER/loginPage?username=" + user.getUsername();
}
@GetMapping("/loginPage")
@ResponseBody
public String loginPage(String username){
return "loginPage:" +username;
}
}
注意,这里的 POST 接口 响应码一定是302 ,否则 postForLocation 无效。
注意,重定向的地址,一定要写出绝对路径。
@GetMapping("/hello7")
public void hello7(){
LinkedMultiValueMap<Object, Object> map = new LinkedMultiValueMap<>();
map.add("username","you");
map.add("password","123");
map.add("id",11);
URI uri = restTemplate.postForLocation("http://PROVIDER/register", map);
String s = restTemplate.getForObject(uri, String.class);
System.out.println(s);
}
调用 postForLocation 方法,返回的是 uri ,这个 uri 就是重定向的地址,拿到 uri 之后,就可以直接发送新的请求了。
客户端负载均衡
客户端负载均衡,就是相对服务端负载均衡而言的。
服务端负载均衡,就是传统的 Nginx 的方式。用 Nginx 做负载均衡,我们称之为服务端负载均衡。
如下图所示:
这种均衡,称为服务端负载均衡。它的一个特点就是,客户端并不知道此时为它提供服务的 server 到底是哪一个,它也不关心。反正请求发给 Nginx,Nginx 再将请求转发给 Tomcat,客户端只需要记住 Nginx 的地址即可。
客户端负载均衡则是另外一种情形:
客户端负载均衡,就是调用的客户端本身是知道所有 Server 的详细信息的,当需要调用 Server 上的接口时,客户端就从自身所维护的 Server 列表中,根据提前配置好的负载均衡策略,自己挑选一个 Server 来调用。此时,客户端知道它所调用的 Server 到底是哪一个。
在 RestTemplate 中,要想使用负载均衡功能,只需要给 RestTemplate 实例上添加一个 @LoadBalanced 注解即可,此时,RestTemplate 就会自动具备负载均衡功能,这个负载均衡就是客户端负载均衡。
@Bean
// 给 RestTemplate 实例添加 @LoadBalanced 注解,自动开启负载均衡
@LoadBalanced
RestTemplate restTemplate(){
return new RestTemplate();
}
负载均衡原理
在 Spring Cloud 中,实现负载均衡非常容易。只需要添加 @LoadBalanced 注解即可。只要添加了该注解,一个原本普通做 Rest 请求的工具 RestTemplate 就会自动具备负载均衡功能,这个是怎么实现的呢?
整体上来说,这个功能的实现就是三个核心点:
-从 Eureka Client 本地缓存的服务注册信息中,选择一个可以调用的服务。
-
根据 1 中所选择的服务,重构请求 URL 地址。
-
将 1、2 步的功能嵌入到 RestTemplate 中。
每天学习一点点,每天进步一点点。
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· 一次Java后端服务间歇性响应慢的问题排查记录
· dotnet 源代码生成器分析器入门
· ASP.NET Core 模型验证消息的本地化新姿势
· ThreeJs-16智慧城市项目(重磅以及未来发展ai)
· .NET 原生驾驭 AI 新基建实战系列(一):向量数据库的应用与畅想
· Ai满嘴顺口溜,想考研?浪费我几个小时
· Browser-use 详细介绍&使用文档
· 软件产品开发中常见的10个问题及处理方法