【问题背景】
最近一直在处理交接项目的遗留问题,在确定了商品同步方案之后,与商品服务对接,遇到了一个问题,请求Read time out。
【原调用方式】
和我交接项目的人,采用的是和旧系统一样的调用方式,写了个HttpClient工具类,之前和我对接接口就给我发了请求超时的错误信息,现在看来这个问题一直都没有处理。
【原方式处理方案】
其实这个问题很简单,既然是请求读取时间超时,那我们在发起请求的时候,将读取时间设置长一些,就解决了。项目中,使用的HttpClient版本是4.3,所以,我在他的请求工具类中增加了一段设置读取时间的代码:
//post方式请求
public String doPost(String url, Map<String, Object> map){
HttpPost httpPost = new HttpPost(url);
//设置代理主机
HttpHost proxy=new HttpHost("请求服务的ip地址", 36016);
//设置请求的连接超时时间
RequestConfig config=RequestConfig.custom().setProxy(proxy)
.setConnectTimeout(30000)
.setSocketTimeout(100000)
.build();
// 装载配置信息
httpPost.setConfig(config);
//装填参数
List<NameValuePair> nvps = new ArrayList<>();
if(map!=null){
for (Map.Entry<String, Object> entry : map.entrySet()) {
Object value = entry.getValue();
String valueStr = value==null?null:value.toString();
nvps.add(new BasicNameValuePair(entry.getKey(),valueStr ));
}
}
//设置header信息
//指定报文头【Content-type】、【User-Agent】
httpPost.setHeader("Content-type", "application/x-www-form-urlencoded");
httpPost.setHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
try{
//设置参数到请求对象中
httpPost.setEntity(new UrlEncodedFormEntity(nvps, "UTF-8"));
// 发起请求
CloseableHttpResponse response = this.httpClient.execute(httpPost);
int statusCode = response.getStatusLine().getStatusCode();
log.info("返回http状态码:"+statusCode);
// 判断状态码是否为200
if ( statusCode == 200) {
// 返回响应体的内容
HttpEntity httpEntity = response.getEntity();
if(httpEntity!=null && httpEntity.getContent()!=null){
return getResponseString(httpEntity);
}
}
return null;
}catch (Exception e){
e.printStackTrace();
return null;
}
}
连接超时的问题就解决了。但另一个问题是,这个项目调用的不仅仅是一个项目,我设置了商品调用的请求主机,但其他接口是调用另一个项目,这就不对了。要不然我就在工具类代码中在单独写一个post请求商品服务的方法,这样代码就重复了。想到既然用的是Spring Boot架构,我就将原生的HttpClient请求方式弃用了,直接使用Spring Boot中封装的更好的RestTemplate去处理。
【新调用方式配置】
采用RestTemplate方式调用其实也很简单,写一个全局配置,并且设置超时时间即可,代码如下:
@Configuration
public class RestTemplateConfig {
@Bean
@ConditionalOnMissingBean({ RestOperations.class, RestTemplate.class })
//Spring Boot的自动配置机制依靠@ConditionalOnMissingBean注解判断是否执行初始化代码,
// 即如果用户已经创建了bean,则相关的初始化代码不再执行。
public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
RestTemplate restTemplate = new RestTemplate(factory);
// 使用 utf-8 编码集的 conver 替换默认的 conver(默认的 string conver 的编码集为"ISO-8859-1")
List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
Iterator<HttpMessageConverter<?>> iterator = messageConverters.iterator();
while (iterator.hasNext()) {
HttpMessageConverter<?> converter = iterator.next();
if (converter instanceof StringHttpMessageConverter) {
iterator.remove();
}
}
messageConverters.add(new StringHttpMessageConverter(Charset.forName("UTF-8")));
//解决微信返回text/plain的解析
restTemplate.getMessageConverters().add(new WxMappingJackson2HttpMessageConverter());
return restTemplate;
}
@Bean
@ConditionalOnMissingBean({ClientHttpRequestFactory.class})
public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
//设置超时时间
factory.setReadTimeout(15000);// ms
factory.setConnectTimeout(15000);// ms
return factory;
}
}
【新调用方式使用】
项目中如何发起调用请求,核心代码如下:
//设置请求头
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
//调用参数
MultiValueMap<String, String> params= new LinkedMultiValueMap<>();
params.add("companyId",companyId.toString());
params.add("addGoodsList",new Gson().toJson(wmsGoodsDtos));
params.add("editGoodsList",new Gson().toJson(editWmsGoodsDtos));
params.add("deleteGoodsIdList",deleteList);
params.add("platformId",platformId.toString());
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(params, headers);
// 执行HTTP请求
// 最后的参数需要用String.class 使用其他的会报错
ResponseEntity<String> response = restTemplate.exchange("请求的接口地址", HttpMethod.POST, requestEntity, String.class);
String result = response.getBody();
【总结】
现在大多是Spring Boot,Spring Cloud服务架构,既然有了新的架构,为什么还要把旧系统的一些老东西拿过来使用呢,换了一种方式,更好地解决了问题,也学习到了一些新东西,何乐而不为呢。