# 问题

http 请求发起后接收不到返回数据!!!【测试环境没出问题,发到正式环境就有问题】

项目中通过 restTemplate 发起请求:

log.info("请求入参:{}",JSON.toJSONString(request));// 打印日志 1
// 配置 http 请求的连接超时时间和读取超时时间
HttpsClientRequestFactory factory = new HttpsClientRequestFactory();
factory.setConnectTimeout(60 * 1000);
factory.setReadTimeout(5 * 60 * 1000);
RestTemplate restTemplate = new RestTemplate(factory);
Result<InventoryResult> result = restTemplate.postForObject(address.concat(inventoryUrl), request, Result.class);
log.info("库存同步,返回数据: {}", result);// 打印日志 2
  • 打印日志 1 内容为:

http 请求入参:{data=[{ productStatus=10,skuCode=null}], messageId=ewpfpr1t6ey5r6qj0su0w1h6rt73hr,token=vgvU5EJKuZbuHii7WH6pTINp40ZRicaqLz4dq5P7L6pDzWir8EEGZhCKPucQjljsw69EHasEy+iJfdTofDg==}

  • 打印日志 2 内容为:没有打印内容!!!

  • 最后发现是因为测试环境中数据量较小,http 请求后,很快能得到相应,而正式环境数据量较大,没有及时得到响应,连接或者读取超时!!!

# 解决方式

# 第一种
  1. 添加 HttpsClientRequestFactory 类,并继承 SimpleClientHttpRequestFactory
/**
 * 兼容调 Https 接口
 */
public class HttpsClientRequestFactory extends SimpleClientHttpRequestFactory {
 
    @Override
    protected void prepareConnection(HttpURLConnection connection, String httpMethod)
            throws IOException {
        if (connection instanceof HttpsURLConnection) {
            prepareHttpsConnection((HttpsURLConnection) connection);
        }
        super.prepareConnection(connection, httpMethod);
    }
 
    private void prepareHttpsConnection(HttpsURLConnection connection) {
        connection.setHostnameVerifier(new SkipHostnameVerifier());
        try {
            connection.setSSLSocketFactory(createSslSocketFactory());
        }
        catch (Exception ex) {
            // Ignore
        }
    }
 
    private SSLSocketFactory createSslSocketFactory() throws Exception {
        SSLContext context = SSLContext.getInstance("TLS");
        context.init(null, new TrustManager[] { new SkipX509TrustManager() },
                new SecureRandom());
        return context.getSocketFactory();
    }
 
    private class SkipHostnameVerifier implements HostnameVerifier {
 
        @Override
        public boolean verify(String s, SSLSession sslSession) {
            return true;
        }
 
    }
 
    private static class SkipX509TrustManager implements X509TrustManager {
 
        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[0];
        }
 
        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType) {
        }
 
        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType) {
        }
 
    }
}
  1. 使用 restTemplate 发起请求前先设置连接和超时时间或者通过容器加载配置类然后设置超时时间
// 配置 http 请求的连接超时时间和读取超时时间
HttpsClientRequestFactory factory = new HttpsClientRequestFactory();
factory.setConnectTimeout(60 * 1000);
factory.setReadTimeout(5 * 60 * 1000);
RestTemplate restTemplate = new RestTemplate(factory);
BaseResult<QueryInventoryResult> result = restTemplate.postForObject(address.concat(inventoryUrl), request, Result.class);
@Configuration
public class RestConfig {
 
    //60 * 1000
    @Value("${rest.connectTimeout:60000}")
    private int connectTimeout;
 
    //5 * 60 * 1000
    @Value("${rest.readTimeout:300000}")
    private int readTimeout;
 
    @Bean
    public RestTemplate restTemplate() {
        SimpleClientHttpRequestFactory simpleClientHttpRequestFactory = new SimpleClientHttpRequestFactory();
        simpleClientHttpRequestFactory.setConnectTimeout(connectTimeout);
        simpleClientHttpRequestFactory.setReadTimeout(readTimeout);
        RestTemplate restTemplate = new RestTemplate(simpleClientHttpRequestFactory);
        return restTemplate;
    }
# 第二种
@Configuration
public class RestConfig {
 
    //60 * 1000
    @Value("${rest.connectTimeout:60000}")
    private int connectTimeout;
 
    //5 * 60 * 1000
    @Value("${rest.readTimeout:300000}")
    private int readTimeout;  
 
    @Value("${rest.connectionRequestTimeout:300000}")
    private int connectionRequestTimeout;  
 
  /**
     * 使用 HttpComponentsClientHttpRequestFactory 创建 http 请求(推荐)
     */
    @Bean
    public RestTemplate restTemplate() {
        HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory();
        httpRequestFactory.setConnectionRequestTimeout(connectionRequestTimeout);
        httpRequestFactory.setConnectTimeout(connectTimeout);
        httpRequestFactory.setReadTimeout(readTimeout);
        return new RestTemplate(httpRequestFactory);
 
    }
}
# 第三种(基于第二种升级)
@Configuration
public class RestConfig {
 
  
    /**
     * 高并发采用 HttpClient 连接池
     */
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate(httpRequestFactory());
    }
 
 
    @Bean
    public ClientHttpRequestFactory httpRequestFactory() {
        return new HttpComponentsClientHttpRequestFactory(httpClient());
    }
 
 
    @Bean
    public HttpClient httpClient() {
        Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.getSocketFactory())
                .register("https", SSLConnectionSocketFactory.getSocketFactory())
                .build();
        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
        // 设置整个连接池最大连接数
        connectionManager.setMaxTotal(1000);
 			// 路由是对 maxTotal 的细分
        connectionManager.setDefaultMaxPerRoute(100);
        // 定义不活动的时间(毫秒),超过的连接从连接池拿取需要重新验证
        connectionManager.setValidateAfterInactivity(200);
        RequestConfig requestConfig = RequestConfig.custom()
                .setSocketTimeout(30000)  // 返回数据的超时时间
                .setConnectTimeout(20000) // 连接上服务器的超时时间
                .setConnectionRequestTimeout(1000) // 从连接池中获取连接的超时时间
                .build();
        return HttpClientBuilder.create()
                .setDefaultRequestConfig(requestConfig)
                .setConnectionManager(connectionManager)
                .evictIdleConnections(2, TimeUnit.SECONDS) // 保持空闲的最大时间
                .build();
    }
}