【httpclient】请求头accept-charset过大&cookie增长导致报问过大

1、问题现象

使用httpclient发送请求到外部web服务器(公网),目标地址网络通畅,刚开始请求也能发出去;一段时间后,请求报错,错误信息:

502 Bad Gateway

httpclient依赖如下


    org.apache.httpcomponents
    httpclient
    4.5.6

restTemplate构造方式如下(省略部分代码):

@Bean
public RestTemplate restTemplate(RestTemplateProperties prop) throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
    return new RestTemplate(new HttpComponentsClientHttpRequestFactory(httpClient(prop)));
}

private HttpClient httpClient(RestTemplateProperties prop) throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {

    Registry registry = RegistryBuilder.create()
            .register("http", PlainConnectionSocketFactory.getSocketFactory())
            .register("https", trustAllSslConnSocketFactory())
            .build();
    PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);

    connectionManager.setMaxTotal(prop.getMaxTotal());

    connectionManager.setDefaultMaxPerRoute(prop.getDefaultMaxPerRoute());
    RequestConfig requestConfig = RequestConfig.custom()
            .setSocketTimeout(prop.getSocketTimeout())
            .setConnectTimeout(prop.getConnectTimeout())
            .setConnectionRequestTimeout(prop.getConnectionRequestTimeout())
            .build();
    return HttpClientBuilder.create()
            .setDefaultRequestConfig(requestConfig)
            .setConnectionManager(connectionManager)
            .build();
}

private SSLConnectionSocketFactory trustAllSslConnSocketFactory() throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException {
    // 信任策略,信任全部
    TrustStrategy trustStrategy = (X509Certificate[] chain, String authType) -> true;
    SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, trustStrategy).build();
    return new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
}

2、问题排查

生产抓包发现,请求头Accept-charsetcookie 特别大,如下:

image.png

尝试减少报文大小,可以成功发送;多次测试,得出目标服务器接收的报文大小是 4K
大于4K的报文超过了目标服务的处理能力,直接返回了 502 bad gaeway

报问题过大有两方面原因:

  1. cookie 过大
  2. accept-charset过大
cookie 过大

观察cookie内容,发现其中有阿里云相关的东西;猜测在网络链路上经过了阿里云的相关代理,返回了一些cookie,按照默认行为,http客户端第二次请求时会带上这些cookie,导致cookie越来越大。

accept-charset过大

经过代码排查,restTemplate会使用 StringHttpMessageConverter 作为其中的一个MessageConverter;而这个Converter在转换请求消息时,有一个增加请求头-接收字符集的行为,如下:

@Override
protected void writeInternal(String str, HttpOutputMessage outputMessage) throws IOException {
    if (this.writeAcceptCharset) {
        outputMessage.getHeaders().setAcceptCharset(getAcceptedCharsets());
    }
    Charset charset = getContentTypeCharset(outputMessage.getHeaders().getContentType());
    StreamUtils.copy(str, charset, outputMessage.getBody());
}

getAcceptedCharsets() 方法的实现如下:

protected List getAcceptedCharsets() {
    List charsets = this.availableCharsets;
    if (charsets == null) {
        charsets = new ArrayList<>(Charset.availableCharsets().values());
        this.availableCharsets = charsets;
    }
    return charsets;
}

端点跟踪发现,Charset.availableCharsets().values() 竟有 170个:

image.png

3、 问题解决

从问题原因看,解决方法是:

  1. 禁用 httpclient的cookie
  2. 禁止 StringHttpMessageConverteraccept-charset

修改方式为:
1)构建HttpClient的时候,禁用cookie;

RequestConfig requestConfig = RequestConfig.custom()
    .setSocketTimeout(prop.getSocketTimeout())
    .setConnectTimeout(prop.getConnectTimeout())
    .setConnectionRequestTimeout(prop.getConnectionRequestTimeout())
    .setCookieSpec(CookieSpecs.IGNORE_COOKIES) // 忽略cookie
    .build();

2)禁止 StringHttpMessageConverter 写接受字符集;

@Bean
public RestTemplate restTemplate(RestTemplateProperties prop) throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
    RestTemplate restTemplate = new RestTemplate(new HttpComponentsClientHttpRequestFactory(httpClient(prop)));
    List> messageConverters = restTemplate.getMessageConverters();
    for (HttpMessageConverter c : messageConverters) {
        if (c instanceof StringHttpMessageConverter) {
            ((StringHttpMessageConverter) c).setWriteAcceptCharset(false);
        }
    }
    return restTemplate;
}

你可能感兴趣的:(【httpclient】请求头accept-charset过大&cookie增长导致报问过大)