如何使用大缓冲区设置HTTP出站网关发送请求

How to set HTTP outbound gateway sending request using big buffer

我使用 Spring Integration 4.3.9.RELEASE 和 Apache HTTP Component HttpClient (4.5.2) 将包含 300k 上传文件的请求中继到后端服务。有时整个配置工作正常。但有时它的表现不是很好,发送请求和得到响应需要将近 10 分钟。我用纯Java写了一些测试(参考),结果如下。

+------------------------------+------------------------+
| Data block size | Pause      | Totally time consuming |
+------------------------------+------------------------+
| 2k              | 1 second   | ~6 minutes             |
| 2k              | 0.1 seocnd | ~33 seconds            |
| 4k              | 0.1 second | ~16 seconds            |
| 0.2k            | 0.1 second | ~6 minutes             |
+------------------------------+------------------------+

每2k停顿1s和每0.2k停顿0.1s的场景都有一个close time elapsed值。我想最有可能发生的是发送数据块较小 (0.2k) 但暂停间隔较短 (0.1s)。需要10分钟才能得到回应,这显然不能令人满意。 那怎么把buffer设置大一点才能保证性能呢?

我的配置如下

  <bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
    <constructor-arg>
      <bean class="org.springframework.http.client.HttpComponentsClientHttpRequestFactory">
        <constructor-arg>
          <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
            <property name="targetClass" value="org.apache.http.impl.client.HttpClients"/>
            <property name="targetMethod" value="createMinimal"/>
          </bean>
        </constructor-arg>
        <property name="connectTimeout" value="${wonders.cloud.api.request.timeout}" />
        <property name="readTimeout" value="${wonders.cloud.api.request.timeout}" />
      </bean>
    </constructor-arg>
    <property name="messageConverters">
      <list>
        <bean class="org.springframework.http.converter.StringHttpMessageConverter" />
        <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
        <bean class="org.springframework.http.converter.FormHttpMessageConverter">
        </bean>
      </list>
    </property>
  </bean>

  <bean id="objectMapper" class="org.springframework.integration.support.json.Jackson2JsonObjectMapper">
    <constructor-arg ref="jacksonObjectMapper" />
  </bean>
  <bean id="jacksonObjectMapper" class="com.fasterxml.jackson.databind.ObjectMapper" >
    <property name="dateFormat">
      <bean class="java.text.SimpleDateFormat">
        <constructor-arg index="0" type="java.lang.String" value="yyyy-MM-dd'T'HH:mm:ss" />
      </bean>
    </property>
    <property name="serializationInclusion" value="#{ T(com.fasterxml.jackson.annotation.JsonInclude.Include).NON_NULL }" />
  </bean>
  <bean class="org.springframework.beans.factory.config.MethodInvokingBean">
    <property name="targetObject" ref="jacksonObjectMapper"/>
    <property name="targetMethod" value="disable"/>
    <property name="arguments" value="#{ T(com.fasterxml.jackson.databind.DeserializationFeature).FAIL_ON_UNKNOWN_PROPERTIES }"/>
  </bean>
  <bean class="org.springframework.beans.factory.config.MethodInvokingBean">
    <property name="targetObject" ref="jacksonObjectMapper"/>
    <property name="targetMethod" value="enable"/>
    <property name="arguments" value="#{ T(com.fasterxml.jackson.databind.DeserializationFeature).READ_UNKNOWN_ENUM_VALUES_AS_NULL }"/>
  </bean>

  <int-http:inbound-gateway id="certificateInboundGateway"
                            path="/{uuid}/certificate"
                            supported-methods="POST"
                            request-channel="certificateRequestChannel"
                            reply-channel="certificateResponseChannel"
                            reply-key="fullway"
                            view-name="index">
    <int-http:header name="uuid" expression="#pathVariables.uuid" />
  </int-http:inbound-gateway>

  <int:channel id="certificateRequestChannel" />
  <int:channel id="certificateResponseChannel" />

  <int:chain id="certificateProcessChain"
             input-channel="certificateRequestChannel"
             output-channel="certificateResponseChannel">
    <int:header-enricher>
      <int:header name="multipartForm" expression="payload"/>
    </int:header-enricher>
    <int:transformer expression="headers.uuid" />
    <int:gateway request-channel="crmMemberInfoRetrieveChannel" />
    <int:filter expression="payload != null" />
    <int:transformer expression=" T(com.wd.fw.business.facade.huayan.transformer.WondersCloudObjectTransformer).buildCertificateForm(headers.multipartForm, payload.get('userid'), '${wonders.cloud.api.token}') " />
    <int:transformer ref="commonHeaderEnricher" method="transform" />
    <int:header-enricher>
      <int:header name="octopus_sid" expression="'${wonders.cloud.api.octopus.sid}'" overwrite="true" />
      <int:header name="Content-Type" expression="'multipart/form-data'" overwrite="true" />
      <int:header name="octopus_apiid" expression="'${wonders.cloud.api.certificate.octopus.apiid}'" />
    </int:header-enricher>
    <int-http:outbound-gateway url="${wonders.cloud.api.protocol}://${wonders.cloud.api.host}/${wonders.cloud.api.context.path}"
                               http-method="POST"
                               header-mapper="headerMapper"
                               rest-template="restTemplate"
                               charset="UTF-8"
                               expected-response-type="java.lang.String">
      <int-http:request-handler-advice-chain>
        <ref bean="retrier" />
      </int-http:request-handler-advice-chain>
    </int-http:outbound-gateway>
    <int:gateway request-channel="dataEncryptChannel" />
  </int:chain>

非常感谢。

您可以尝试这样的操作:

ConnectionConfig connConfig = ConnectionConfig.custom()
    .setBufferSize(DESIRED_BUFFER_SIZE)
    .build();

CloseableHttpClient client = HttpClients.custom()
        .setDefaultConnectionConfig(connConfig)
        .build();

但是为什么你认为问题不在服务器端呢?

增加缓冲区大小无助于提高性能。 最终,我必须创建一个服务激活器,它将通过我的问题中提到的纯 java 方法向远程发送请求。

I write some tests in pure Java (refer to Sending HTTP POST Request In Java) and results as following.

我也比较了发送到服务器的请求,没有发现任何差异。很奇怪。