目录
RestTemplate Rest 模板概述
Http GET 请求
Http Post 请求
RestTemplate 超时时间
切换 OkHttpClient 客户端
带负载均衡(@LoadBalanced)的 RestTemplate 必须使用微服务名称发起请求,不能使用 ip:port
不带负载均衡(@LoadBalanced)的 RestTemplate 不能使用微服务名称发起请求,只能使用 ip:port
1、org.springframework.web.client.RestTemplate 类是 spring-web-x.x.x.RELEASE.jar 包下进行 HTTP 访问的 REST 客户端核心类。
2、Java 应用后台代码中如果想要向另外一个 Java 应用发起 Http 请求,通常使用 Apache 的 HttpClient 库来做。而 spring cloud 微服务架构中,各个微服务之间会频繁的发起 http 请求进行通信,所以 spring 对 http 请求进行了封装,以便请求使用起来更加方便。
3、RestTemplate 类能够以 rest 风格的方式,以 GET, POST, PUT, DELETE, HEAD, OPTIONS 等不同的方式向服务器发起HTTP 请求。
4、RestTemplate 对 http 的封装类似 JdbcTemplate 对 jdbc 的封装。本文环境:Java jdk 8 + Spring boot 2.1.3(spring 5.1.5)。
构造函数 | 描述 |
RestTemplate() | 默认使用 org.springframework.http.client.SimpleClientHttpRequestFactory 客户端,底层使用 jdk 标准的 API,即 java.net 包下的 API,如 java.net.HttpURLConnection 等。 |
RestTemplate(ClientHttpRequestFactory requestFactory) | ClientHttpRequestFactory 接口的实现类给出底层实现的第三方 HTTP 客户端软件。如 OkHttp3Client、以及 RibbonClient 等 |
RestTemplate(List |
HttpMessageConverter 接口的实现对象能够在HTTP消息与Java POJO之间进行数据转换。 |
5、使用 RestTemplate 非常简单,对于 Spring boot、Spring Cloud 项目,spring-boot-starter-web 组件内部已经包含了 spring-web-x.x.x.RELEASE.jar ,所以只需要将 RestTemplate 实例交由 Spring 容器管理,然后在需要的地方获取使用即可。
//可以直接在启动类(@SpringBootApplication) 中进行添加,或者在 @Configuration 标注的类中添加
@Bean
public RestTemplate restTemplate() {
//将 RestTemplate 实例交由 Spring 容器管理
return new RestTemplate();
}
//在使用 RestTemplate 的 Controller 或者其它地方自动注入,然后使用
@Resource
private RestTemplate restTemplate;
方法 | 描述 |
url:请求的地址,responseType:返回的数据类型,ResponseEntity 对象包含了http响应头信息、响应正文、响应状态等信息 | |
uriVariables:用于设置查询参数的可变值,map 的 key 值对于url中设置的可变参数值. | |
uriVariables:专门用于处理 url 路径中的参数 @PathVariable | |
getForObject 方法是重载的方法,区别在于返回值直接是响应正文,不含响应头信息以及响应状态. | |
getForEntity(URI url, Class
/** 接口提供方
* http://localhost:8080/getInfo?uid=3030&agencyId=20807
* @param uid
* @param agencyId
* @return
*/
@GetMapping("getInfo")
public String getInfo(@RequestParam String uid, String agencyId) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("code", 200);
jsonObject.put("uid", uid);
jsonObject.put("agencyId", agencyId);
return jsonObject.toString();
}
//接口调用方
@GetMapping("test1")
public String test1() {
String body = null;
try {
URI uri = new URI("http://localhost:8080/getInfo?uid=3030&agencyId=20807");
ResponseEntity forEntity = restTemplate.getForEntity(uri, String.class);
//http响应状态码,如成功时返回 200
int statusCodeValue = forEntity.getStatusCodeValue();
//http响应的正文内容,即对方返回的数据,如 {"uid":"3030","code":200,"agencyId":"20807"}
body = forEntity.getBody();
//http响应的头信息,如 Content-Type=[text/plain;charset=UTF-8], Content-Length=[44], Date=[Tue, 24 Mar 2020 12:45:43 GMT]}
HttpHeaders headers = forEntity.getHeaders();
System.out.println("headers=" + headers + "\nstatusCodeValue=" + statusCodeValue + "\nbody=" + body);
} catch (URISyntaxException e) {
e.printStackTrace();
}
return body;
}
getForEntity(String url, Class
/**
* 接口提供方
* http://localhost:8080/getData
* @param uid
* @param agencyId
* @return
*/
@GetMapping("getData/{agencyId}/{uid}")
public List
//接口调用方
@GetMapping("test2")
public List> test2() {
String uri = "http://localhost:8080/getData/{agency_id}/{u_id}?info=长沙";
//返回数据类型为 List 类型
ResponseEntity forEntity = restTemplate.getForEntity(uri, List.class, 20792, "999UP000");
//http响应状态码,如成功时返回 200
int statusCodeValue = forEntity.getStatusCodeValue();
//http响应的正文内容,即对方返回的数据,如 {"uid":"3030","code":200,"agencyId":"20807"}
List> body = forEntity.getBody();
//http响应的头信息,如 Content-Type=[text/plain;charset=UTF-8], Content-Length=[44], Date=[Tue, 24 Mar 2020 12:45:43 GMT]}
HttpHeaders headers = forEntity.getHeaders();
System.out.println("headers=" + headers + "\nstatusCodeValue=" + statusCodeValue + "\nbody=" + body);
return body;
}
getForObject(String url, Class
/**
* 接口提供方
* http://localhost:8080/getMessage
*
* @param uid
* @param agencyId
* @return
*/
@GetMapping("getMessage")
public JSONObject getMessage(String uid, String agencyId) {
List> dataMapList = new ArrayList<>(8);
Map map = new HashMap<>(8);
JSONObject jsonObject = new JSONObject();
jsonObject.put("code", 200);
jsonObject.put("uid", uid);
jsonObject.put("agencyId", agencyId);
return jsonObject;
}
//接口调用方
@GetMapping("test3")
public JSONObject test3() {
String uri = "http://localhost:8080/getMessage?uid={uid}&agencyId={agency_id}";
Map paramMap = new HashMap<>(4);
paramMap.put("uid", "9998UYT9");
paramMap.put("agency_id", 20802);
//getForObject 返回数据类型为 JSONObject 类型的响应内容,即直接获取返回值.
JSONObject body = restTemplate.getForObject(uri, JSONObject.class, paramMap);
System.out.println(body);
return body;
}
方法 | 描述 |
url:请求的地址; request:请求的正文(body)内容,即对方 @RequestBody 参数的值,可以为 null。 responseType:返回的数据类型, ResponseEntity 对象包含了http响应头信息、响应正文、响应状态等信息 |
|
uriVariables:用于设置查询参数的可变值,map 的 key 值对于url中设置的可变参数值. | |
uriVariables:专门用于处理 url 路径中的参数 @PathVariable | |
T postForObject(URI url, @Nullable Object request, Class |
getForObject 方法是重载的方法,区别在于返回值直接是响应正文,不含响应头信息以及响应状态. |
T postForObject(String url, @Nullable Object request, Class |
|
T postForObject(String url, @Nullable Object request, Class |
postForEntity(String url, @Nullable Object request,Class
/**接口提供方
* http://localhost:8080/postInfo?uid=44KKP990&agencyId=208071
*
* @param uid
* @param agencyId
* @return
*/
@PostMapping("postInfo")
public JSONObject postInfo(@RequestParam String uid, String agencyId) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("code", 200);
jsonObject.put("uid", uid);
jsonObject.put("agencyId", agencyId);
return jsonObject;
}
//接口调用方
@GetMapping("test11")
public JSONObject test11() {
JSONObject body = null;
String uri = "http://localhost:8080/postInfo?uid={uid}&agencyId=208071";
Map paramMap = new HashMap<>(4);
paramMap.put("uid", "8899HG9");
ResponseEntity forEntity = restTemplate.postForEntity(uri, null, JSONObject.class, paramMap);
//http响应状态码,如成功时返回 200
int statusCodeValue = forEntity.getStatusCodeValue();
//http响应的正文内容,即对方返回的数据,如 {"uid":"3030","code":200,"agencyId":"20807"}
body = forEntity.getBody();
//http响应的头信息,如 Content-Type=[text/plain;charset=UTF-8], Content-Length=[44], Date=[Tue, 24 Mar 2020 12:45:43 GMT]}
HttpHeaders headers = forEntity.getHeaders();
System.out.println("headers=" + headers + "\nstatusCodeValue=" + statusCodeValue + "\nbody=" + body);
return body;
}
postForObject(String url, @Nullable Object request, Class
/**
* 接口提供方
* http://localhost:8080/postData/{agencyId}
*/
@PostMapping("postData/{agencyId}")
public List> postData(@PathVariable String agencyId, @RequestBody List ids) {
List> dataMapList = new ArrayList<>(8);
Map map = new HashMap<>(8);
map.put("code", 200);
map.put("uid", ids.toArray());
map.put("agencyId", agencyId);
dataMapList.add(map);
return dataMapList;
}
//接口调用方
@GetMapping("test22")
public List> test22() {
String uri = "http://localhost:8080/postData/{agencyId}";
List ids = new ArrayList<>(4);
ids.add("3000L");
ids.add("99UH7");
ids.add("99JHG");
//返回数据类型为 List 类型
List> body = restTemplate.postForObject(uri, ids, List.class, 20792);
return body;
}
//其余操作都是同理
1、new RestTemplate() 时默认使用 SimpleClientHttpRequestFactory 工厂,它底层默认使用 Java 原生的 HttpURLConnection API 做 Http 请求,默认 connectTimeout = -1(连接超时),readTimeout = -1(读取超时)都没有做限制,即一直等待。
2、显然无限期的等待是绝不允许的,必须设置超时时间,以 SimpleClientHttpRequestFactory 工厂为例:
//设置 http 连接工厂,然后将 RestTemplate 实例交由 Spring 容器管理
@Bean
public RestTemplate restTemplate() {
//先设置 http 连接工厂的连接超时时间为 30 秒,读取超时时间为 120 秒
//请求发送后,对方数据量比较大,需要长时间处理数据时,读取超时时间需要设置的长一些,否则对方还未处理完,连接就超时了。
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setConnectTimeout(30 * 1000);
requestFactory.setReadTimeout(120 * 1000);
return new RestTemplate(requestFactory);
}
3、验证非常简单,请求 youtube ,铁定连不上而会超时,然后抛异常: java.net.SocketTimeoutException: Read timed out
String body = restTemplate.getForObject("https://ca.youtube.com/", String.class);
1、ClientHttpRequestFactory 有多个实现类,okhttp3 作为现在最热门 http 客户端,这里就以 OkHttpClient 为例。
2、第一步项目中引入 OkHttpClient 的依赖,否则会报:java.lang.NoClassDefFoundError: okhttp3/OkHttpClient
com.squareup.okhttp3
okhttp
3.14.5
版本不能随意引,org.springframework.http.client.OkHttp3ClientHttpRequestFactory 类的源码上面通常有提示说明对应的OkHttp 版本,如本文 spring-web-5.0.7.RELEASE.jar 提示的 OkHttp 版本是 3.X,如果引入 4.X 则启动会失败。
3、接下来就是创建 RestTemplate 对象的时候,传入 OkHttp3ClientHttpRequestFactory 工厂即可:
//设置 http 连接工厂,然后将 RestTemplate 实例交由 Spring 容器管理
@Bean
public RestTemplate restTemplate() {
//先设置 http 连接工厂的连接超时时间为 30 秒,读取超时时间为 120 秒
OkHttp3ClientHttpRequestFactory okRequestFactory = new OkHttp3ClientHttpRequestFactory();
okRequestFactory.setConnectTimeout(30 * 1000);
okRequestFactory.setReadTimeout(120 * 1000);
return new RestTemplate(okRequestFactory);
}
验证底层到底用的什么 http 客户端也很简单,仍然请求 youtube(https://ca.youtube.com) ,连接超时时控制层会打印异常,其中就可以看到 okhttp3.internal.xxxx 的错误信息。
4、其它如 Apache 的 HttpClient、以及 Netty、RibbonClient 等 http 客户端使用都是同理。
Ribbon 的负载均衡就是在 RestTemplate 上加上
@LoadBalanced
,可以参考 Netflix Ribbon 负载均衡扩展内容可以继续参考:https://blog.csdn.net/f641385712/article/details/100713622