Java服务调用RestTemplate与HttpClient的使用详解

网友投稿 782 2022-07-21


目录概述RestTemplate概述及依赖配置类使用GET请求POST请求上传文件HttpClient概述使用

概述

常见的远程调用方式有以下2种:

RPC: Remote Produce Call远程过程调用,类似的还有RMI(remote method invoke)。自定义数据格式,基于原生TCP通信,速度快,效率高。早期的webservice,现在热门的dubbo,都是RPC的典型代表。Http: http其实是一种网络传输协议,基于TCP,规定了数据传输的格式。 现在客户端浏览器与服务端通信基本都是采用Http协议,也可以用来进行远程服务调用。缺点是消息封装臃肿,优势是对服务的提供和调用方没有任何技术限定,自由灵活,更符合微服务理念。现在热门的Rest风格,就可以通过http协议来实现。

如果项目全部采用 java技术栈,那么使用Dubbo作为微服务架构是一个不错的选择。

如果项目的技术栈多样化,主要采用了Spring和SpringBoot框架,那么SpringCloud搭建微服务是不二之选,使用Http方式来实现服务间调用。

java开发中,使用http连接,访问第三方网络接口,通常使用的连接工具为RestTemplate、HttpClient和OKHttp。

RestTemplate

概述及依赖

HttpClient和OKHttp两种连接工具,使用起来比较复杂,如果使用spring框架,可以使用restTemplate来进行http连接请求。

restTemplate默认的连接方式是java中的HttpConnection,可以使用ClientHttpRequestFactory指定不同的HTTP连接方式。

依赖

org.springframework

spring-web

5.2.2.RELEASE

org.apache.httpcomponents

httpclient

4.5.7

配置类

基础配置

@Configuration

public class RestTemplateConfig {

@Bean

public RestTemplate restTemplate(ClientHttpRequestFactory factory) {

return new RestTemplate(factory);

}

@Bean

public ClientHttpRequestFactory simpleClientHttpRequestFactory() {

SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();

factory.setReadTimeout(150 * 1000); // ms

factory.setConnectTimeout(150 * 1000); // ms

return factory;

}

}

进阶配置

import org.apache.http.client.HttpClient;

import org.apache.http.conn.HttpClientConnectionManager;

import org.apache.http.impl.client.HttpClientBuilder;

import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.http.client.ClientHttpRequestFactory;

import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;

import org.springframework.web.client.RestTemplate;

@Configuration

public class RestTemplateConfig {

/**

* http连接管理器

*/

@Bean

public HttpClientConnectionManager poolingHttpClientConnectiongwvjUSManager() {

/*// 注册http和https请求

Registry registry = RegistryBuilder.create()

.register("http", PlainConnectionSocketFactory.getSocketFactory())

.register("https", SSLConnectionSocketFactory.getSocketFactory())

.build();

PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(registry);*/

PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager();

// 最大连接数

poolingHttpClientConnectionManager.setMaxTotal(500);

// 同路由并发数(每个主机的并发)

poolingHttpClientConnectionManager.setDefaultMaxPerRoute(100);

return poolingHttpClientConnectionManager;

}

/**

* HttpClient

* @param poolingHttpClientConnectionManager

*/

@Bean

public HttpClient httpClient(HttpClientConnectionManager poolingHttpClientConnectionManager) {

HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();

// 设置http连接管理器

httpClientBuilder.setConnectionManager(poolingHttpClientConnectionManager);

/*// 设置重试次数,默认是3次,没有开启

httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(3, true));*/

// 设置默认请求头

/*List

headers = new ArrayList<>();

headers.add(new BasicHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.16 Safari/537.36"));

headers.add(new BasicHeader("Accept-Encoding", "gzip,deflate"));

headers.add(new BasicHeader("Accept-Language", "zh-CN"));

headers.add(new BasicHeader("Connection", "Keep-Alive"));

headers.add(new BasicHeader("Content-type", "application/json;charset=UTF-8"));

httpClientBuilder.setDefaultHeaders(headers);*/

return httpClientBuilder.build();

}

/**

* 请求连接池配置

* @param httpClient

*/

@Bean

public ClientHttpRequestFactory clientHttpRequestFactory(HttpClient httpClient) {

HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory();

// httpClient创建器

clientHttpRequestFactory.setHttpClient(httpClient);

// 连接超时时间/毫秒(连接上服务器(握手成功)的时间,超出抛出connect timeout)

clientHttpRequestFactory.setConnectTimeout(5 * 1000);

// 数据读取超时时间(socketTimeout)/毫秒(服务器返回数据(response)的时间,超过抛出read timeout)

clientHttpRequestFactory.setReadTimeout(10 * 1000);

// 从连接池获取请求连接的超时时间,不宜过长,必须设置/毫秒(超时间未拿到可用连接,会抛出org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool)

clientHttpRequestFactory.setConnectionRequestTimeout(10 * 1000);

return clientHttpRequestFactory;

}

/**

* rest模板

*/

@Bean

public RestTemplate restTemplate(ClientHttpRequestFactory clientHttpRequestFactory) {

// 配置请求工厂

return new RestTemplate(clientHttpRequestFactory);

}

}

使用

实体类

@Data

@Builder

@NoArgsConstrutor

@AllArgsConstrutor

public class BaseResponse implements Serializable {

private static final long serialVersionUID = 1L;

private String responseCode;

private String responseMessage;

private List responseData;

}

@Data

@Builder

@NoArgsConstrutor

@AllArgsConstrutor

public class TempUser implements Serializable {

private static final long serialVersionUID = 1L;

private String userName;

private Integer age;

}

GET请求

普通访问

BaseResponse result = restTemplate.getForObject(

"http://localhost:8080/cs-admin/rest/getUser?userName=张三&age=18", BaseResponse.class);

返回HTTP状态

ResponseEntity responseEntity = restTemplate.getForEntity(

"http://localhost:8080/cs-admin/rest/getUser?userName=张三&age=18", TempUser.class);

// 获取状态对象

HttpStatus httpStatus = responseEntity.getStatusCode();

// 获取状态码

int statusCodeValue = responseEntity.getStatusCodeValue();

// 获取headers

HttpHeaders httpHeaders = responseEntity.getHeaders();

// 获取body

BaseResponse result = responseEntity.getBody();

映射请求参数

Map paramMap = new HashMap<>();

paramMap.put("userName", "张三");

paramMap.put("age", 18);

BaseResponse result = restTemplate.getForObject(

"http://localhost:8080/cs-admin/rest/getUser?userName={userName}&age={age}",

BaseResponse.class, paramMap);

POST请求

普通访问接口

TempUser param = new TempUser();

param.setUserName("张三");

param.setAge(18);

BaseResponse result = restTemplate.postForObject("http://localhost:8080/cs-admin/rest/getPostUser",

param, BaseResponse.class);

带HEAD访问接口

// 请求头信息

HttpHeaders headers = new HttpHeaders();

//headers.setContentType(MediaType.valueOf("application/json;charset=UTF-8"));

headers.setContentType(MediaType.APPLICATION_JSON_UTF8);

//headers.add("headParam1", "headParamValue");

// 请求体内容

TempUser param = new TempUser();

param.setUserName("张三");

param.setAge(18);

// 组装请求信息

HttpEntity httpEntity=new HttpEntity<>(param, headers);

BaseResponse result = restTemplate.postForObject("http://localhost:8080/cs-admin/rest/getPostUser",

httpEntity, BaseResponse.class);

无请求体的访问:仅method为post,传参方式仍然为get的param方式

Map paramMap = new HashMap<>();

paramMap.put("userName", "张三");

paramMap.put("age", 18);

BaseResponse result = restTemplate.postForObject(

"http://localhost:8080/cs-admin/rest/getPostUserNoBody?userName={userName}&age={age}",

null, BaseResponse.class, paramMap);

System.out.println(result);

上传文件

后台接口代码:

@RequestMapping("uploadFile")

public TempUser uploadFile(HttpServletRequest request, TempUser form) {

MultipartHttpServletRequest multipartHttpServletRequest = (MultipartHttpServletRequest) request;

//获取文件信息

MultipartFile multipartFile = multipartHttpServletRequest.getFile("file");

TempUser tempUser = new TempUser();

if (multipartFile != null) {

tempUser.setUserName(form.getUserName() + " " + multipartFile.getOriginalFilename());

}

if(form!=null){

tempUser.setAge(form.getAge());

}

return tempUser;

}

访问方式:

// 文件

FileSystemResource file=new FileSystemResource("D:\\Elasticsearch权威指南(中文版).pdf");

// 设置请求内容

gwvjUSMultiValueMap param=new LinkedMultiValueMap<>();

param.add("file", file);

// 其他参数

param.add("userName", "张三");

param.add("age", 18);

// 组装请求信息

HttpEntity> httpEntity=new HttpEntity<>(param);

// 发送请求

TempUser result = restTemplate.postForObject("http://localhost:8080/cs-admin/rest/uploadFile",

httpEntity, TempUser.class);

HttpClient

概述

HttpClient 通过连接池创建连接:

管理连接的基本单位是Route(路由),每个路由上都会维护一定数量的HTTP连接每次调用后必须执行 releaseConnection路由可以理解为客户端机器到目标机器的一条线路如果不给 httpclient配置指定的连接管理器,httpclient会自动使用PoolingHttpClientConnectionManager作为连接管理器。PoolingHttpClientConnectionManager默认的maxConnPerRoute和maxConnTotal分别是是2和20。也就是对于每个服务器最多只会维护2个连接,看起来有点少。所以,在日常使用时我们尽量使用自己配置的连接管理器比较好。

连接池:

连接池技术作为创建和管理连接的缓冲池技术。连接池管理的对象是长连接有长连接的优势

**长连接:**是指客户端与服务器端一旦建立连接以后,可以进行多次数据传输而不需重新建立连接,

优势:

省去了每次数据传输连接建立的时间开销资源的访问控制

**短连接:**每次数据传输都需要客户端和服务器端建立一次连接

使用

使用HttpClient发送请求、接收响应很简单,一般需要如下几步即可:

创建HttpClient对象创建请求方法的实例,并指定请求URL。如果需要发送GET请求,创建HttpGet对象;如果需要发送POST请求,创建HttpPost对象。如果需要发送请求参数,可调用HttpGet、HttpPost共同的setParams(HetpParams params)方法来添加请求参数;对于HttpPost对象而言,也可调用setEntity(HttpEntity entity)方法来设置请求参数,参数则必须用NameValuePair[]数组存储调用HttpClient对象的execute(HttpUriRequest request)发送请求,该方法返回一个HttpResponse调用HttpResponse的getAllHeaders()、getHeaders(String name)等方法可获取服务器的响应头;调用HttpResponse的getEntity()方法可获取HttpEntity对象,该对象包装了服务器的响应内容。程序可通过该对象获取服务器的响应内容释放连接。无论执行方法是否成功,都必须释放连接

依赖:

org.apache.httpcomponents

httpclient

4.5.2

org.apache.httpcomponents

httpclient-cache

4.5.2

http:// org.apache.httpcomponents

httpmime

4.5.2

java工具类

import com.alibaba.fastjson.JSON;

import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang3.StringUtils;

import org.apache.http.Consts;

import org.apache.http.HttpResponse;

import org.apache.http.NameValuePair;

import org.apache.http.client.HttpClient;

import org.apache.http.client.config.RequestConfig;

import org.apache.http.client.entity.UrlEncodedFormEntity;

import org.apache.http.client.methods.HttpGet;

import org.apache.http.client.methods.HttpPost;

import org.apache.http.entity.StringEntity;

import org.apache.http.impl.client.HttpClients;

import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;

import org.apache.http.message.BasicNameValuePair;

import org.apache.http.util.EntityUtils;

import org.slf4j.Logger;

import java.net.URI;

import java.nio.charset.StandardCharsets;

import java.util.ArrayList;

import java.util.List;

import java.util.Map;

/**

* HttpClient 工具类

*/

@Slf4j

public class HttpClientUtil {

public static final String APPLICATION_JSON_VALUE = "application/json";

private static final Logger logger = log;

private static final Integer CONN_TIME_OUT = 3000;// 超时时间豪秒

private static final Integer SOCKET_TIME_OUT = 10000;

/** 每个路由的最大请求数,默认2 */

private static final Integer DEFAULT_MAX_PER_ROUTE = 40;

/** 最大连接数,默认20 */

private static final Integer MAX_TOTAL = 400;

private static HttpClient httpClient;

static {

// 请求配置

RequestConfig requestConfig = RequestConfig.custom()

.setConnectTimeout(CONN_TIME_OUT)

.setConnectionRequestTimeout(CONN_TIME_OUT)

.setSocketTimeout(SOCKET_TIME_OUT)

.build();

// 管理 http连接池

PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();

cm.setDefaultMaxPerRoute(DEFAULT_MAX_PER_ROUTE);

cm.setMaxTotal(MAX_TOTAL);

httpClient = HttpClients.custom()

.setConnectionManager(cm)

.setDefaultRequestConfig(requestConfig)

.build();

}

/**

* Get请求

*/

public static String requestGet(String url, Map paramsMap) throws Exception {

logger.info("GET request url:{} params:{}", url, paramsMap);

Long start = System.currentTimeMillis();

List params = initParams(paramsMap);

// Get请求

HttpGet httpGet = new HttpGet(url);

try {

// 设置参数

String str = EntityUtils.toString(new UrlEncodedFormEntity(params, StandardCharsets.UTF_8));

String uriStr = StringUtils.isEmpty(str) ?

httpGet.getURI().toString() : httpGet.getURI().toString() + "?" + str;

httpGet.setURI(new URI(uriStr));

// 发送请求

HttpResponse response = httpClient.execute(httpGet);

logger.info("GET request url:{} response:{} time:{}",

url, response, System.currentTimeMillis() - start);

// 获取返回数据

return getSuccessRetFromResp(response, url, JSON.toJSONString(paramsMap));

} finally {

// 必须释放连接,不然连接用完后会阻塞

httpGet.releaseConnection();

}

}

/**

* Post请求,Map格式数据

*/

public static String requestPost(String url, Map paramsMap) throws Exception {

logger.info("POST request url:{} params:{}", url, paramsMap);

Long start = System.currentTimeMillis();

List params = initParams(paramsMap);

HttpPost httpPost = new HttpPost(url);

try {

httpPost.setEntity(new UrlEncodedFormEntity(params, Consts.UTF_8));

HttpResponse response = httpClient.execute(httpPost);

logger.info("POST request url:{} response:{} time:{}",

url, response, System.currentTimeMillis() - start);

String retStr = getSuccessRetFromResp(response, url, JSON.toJSONString(paramsMap));

return retStr;

} finally {

httpPost.releaseConnection();

}

}

/**

* Post请求,json格式数据

*

*/

public static String requestPostJsonStr(String url, String json) throws Exception {

logger.info("POST request url:{} params:{}", url, json);

long start = System.currentTimeMillis();

HttpPost httpPost = new HttpPost(url);

try {

StringEntity entity = new StringEntity(json, Consts.UTF_8);

entity.setContentType(APPLICATION_JSON_VALUE);

httpPost.setEntity(entity);

HttpResponse response = httpClient.execute(httpPost);

logger.info("POST request url:{} response:{} time:{}",

url, response, System.currentTimeMillis() - start);

return getSuccessRetFromResp(response, url, json);

} finally {

// 资源释放

httpPost.releaseConnection();

}

}

/**

* post Object格式数据

*/

public static String requestPostJson(String url, Object obj) throws Exception {

String params = JSON.toJSONString(obj);

return requestPostJsonStr(url, params);

}

private static String getSuccessRetFromResp(HttpResponse response, String url, String params) throws Exception {

String rehttp://tStr = "";

// 检验状态码,如果成功接收数据

int code = response.getStatusLine().getStatusCode();

if (code == 200) {

retStr = EntityUtils.toString(response.getEntity(), Consts.UTF_8);

} else {

throw new RuntimeException(String.format("Http request error:%s, url:%s, params:%s", response, url, params));

}

logger.info("Http request retStr:{}. url:{}", retStr, url);

return retStr;

}

private static List initParams(Map paramsMap) {

List params = new ArrayList();

if (paramsMap == null)

return params;

for (Map.Entry entry : paramsMap.entrySet()) {

params.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));

}

return params;

}

}


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:蔚来汽车股权披露:高瓴清仓淡马锡减持!
下一篇:springboot创建的web项目整合Quartz框架的项目实践
相关文章

 发表评论

暂时没有评论,来抢沙发吧~