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

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

目录

概述

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连接方式。

依赖

<dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.2.2.RELEASE</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.7</version> </dependency> 配置类

基础配置

@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 poolingHttpClientConnectionManager() { /*// 注册http和https请求 Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>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<Header> 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<TempUser> implements Serializable { private static final long serialVersionUID = 1L; private String responseCode; private String responseMessage; private List<TempUser> 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<BaseResponse> 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<String, Object> 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<TempUser> httpEntity=new HttpEntity<>(param, headers); BaseResponse result = restTemplate.postForObject("http://localhost:8080/cs-admin/rest/getPostUser", httpEntity, BaseResponse.class);

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

Map<String, Object> 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"); // 设置请求内容 MultiValueMap<String, Object> param=new LinkedMultiValueMap<>(); param.add("file", file); // 其他参数 param.add("userName", "张三"); param.add("age", 18); // 组装请求信息 HttpEntity<MultiValueMap<String, Object>> 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)等方法可获取服务器的响应头;调用HttpResponsegetEntity()方法可获取HttpEntity对象,该对象包装了服务器的响应内容。程序可通过该对象获取服务器的响应内容

释放连接。无论执行方法是否成功,都必须释放连接

依赖:

<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.2</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient-cache</artifactId> <version>4.5.2</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpmime</artifactId> <version>4.5.2</version> </dependency>

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<String, String> paramsMap) throws Exception { logger.info("GET request url:{} params:{}", url, paramsMap); Long start = System.currentTimeMillis(); List<NameValuePair> 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<String, String> paramsMap) throws Exception { logger.info("POST request url:{} params:{}", url, paramsMap); Long start = System.currentTimeMillis(); List<NameValuePair> 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 retStr = ""; // 检验状态码,如果成功接收数据 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<NameValuePair> initParams(Map<String, String> paramsMap) { List<NameValuePair> params = new ArrayList<NameValuePair>(); if (paramsMap == null) return params; for (Map.Entry<String, String> entry : paramsMap.entrySet()) { params.add(new BasicNameValuePair(entry.getKey(), entry.getValue())); } return params; } }

到此这篇关于Java服务调用RestTemplate与HttpClient的使用详解的文章就介绍到这了,更多相关Java RestTemplate与HttpClient内容请搜索易知道(ezd.cc)以前的文章或继续浏览下面的相关文章希望大家以后多多支持易知道(ezd.cc)!

推荐阅读

    学习写字楼新选择6000元主流配置

    学习写字楼新选择6000元主流配置,,这种配置需要考虑双核心的办公和娱乐平台,充分考虑办公室的办公需求和娱乐需求,以约6000元的预算和cost-e

    酷睿I7 配置

    酷睿I7 配置,配置,玩家国度啦华硕 Rampage II Extreme(3800元)如果米不够,也可以把Extreme改为Gene,不过是小板内存推荐金士顿6G DDR3 2000骇

    提高3A四核羿龙II游戏配置的性能

    提高3A四核羿龙II游戏配置的性能,,以节能环保为主题的IT产业,目前3A低端平台处理器、主板芯片组、独立开发卡性能突出,特别是在与AMD的处理

    opporeno8参数配置及价格

    opporeno8参数配置及价格,面部,亿元,Oppo的荣誉2020年1月4日,接近屏幕关闭传感器是否支持双卡:支持oppor11splus什么时候上市的Oppo R11S P

    查看配置:酷睿i3530集展示办公平台

    查看配置:酷睿i3530集展示办公平台,,由于时间和精力的关系,我们不可能对所有的配置进行评论,希望我们能理解,我希望我们的评论能在那些需要帮

    3500元超额值学生娱乐结构的优化配置

    3500元超额值学生娱乐结构的优化配置,,作为一个DIY的主流用户领域的学生,每个用户51学生攒机的高峰。因为学生用户没有稳定的收入来源,攒机

    电脑配置快捷键|查看电脑配置快捷键

    电脑配置快捷键|查看电脑配置快捷键,,查看电脑配置快捷键1.在win10中,快捷键组合win+E已经变成了“快速访问”,不再是以前的“我的电脑了”2

    6000元教你黑盒5000+权限配置

    6000元教你黑盒5000+权限配置,,一系列的价格后,最经典的AMD双核处理器mdash;mdash;Athlon 64 X2 5000 +黑版mdash;mdash;目前的价格已经低至565