无论是微服务还是SOA,都面临着服务间的远程调用。
常见的远程调用方式有以下2种:
如果项目全部采用 Java技术栈,那么使用Dubbo作为微服务架构是一个不错的选择。
如果项目的技术栈多样化,主要采用了Spring和SpringBoot框架,那么SpringCloud搭建微服务是不二之选,使用Http方式来实现服务间调用。
java开发中,使用http连接,访问第三方网络接口,通常使用的连接工具为RestTemplate、HttpClient和OKHttp。
HttpClient和OKHttp两种连接工具,使用起来比较复杂,如果使用spring框架,可以使用restTemplate来进行http连接请求。
restTemplate默认的连接方式是java中的HttpConnection,可以使用ClientHttpRequestFactory指定不同的HTTP连接方式。
依赖
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webartifactId>
<version>5.2.2.RELEASEversion>
dependency>
<dependency>
<groupId>org.apache.httpcomponentsgroupId>
<artifactId>httpclientartifactId>
<version>4.5.7version>
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 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<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;
}
普通访问
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);
普通访问接口
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
发送请求、接收响应很简单,一般需要如下几步即可:
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
对象,该对象包装了服务器的响应内容。程序可通过该对象获取服务器的响应内容依赖:
<dependency>
<groupId>org.apache.httpcomponentsgroupId>
<artifactId>httpclientartifactId>
<version>4.5.2version>
dependency>
<dependency>
<groupId>org.apache.httpcomponentsgroupId>
<artifactId>httpclient-cacheartifactId>
<version>4.5.2version>
dependency>
<dependency>
<groupId>org.apache.httpcomponentsgroupId>
<artifactId>httpmimeartifactId>
<version>4.5.2version>
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;
}
}