使用 Spring-ws 和 4.5.x Http 客户端忽略 DNS 循环以获得可靠的超时

RD_*_*_WF 1 java spring-ws apache-httpclient-4.x

我们将 Http Client 4.5.x 与 Spring-Ws 结合使用,并使用该webServiceTemplate.marshalSendAndReceive(requestObject)方法发出请求。我们希望有一个可靠的连接超时值,但目前遇到了第 8 节(DNS 循环)中描述的问题,其中尝试了多个 IP 地址,因此超时是不可预测的。是否有简单的方法可以仅使用 Spring-ws 和 Http Client 库在一定时间后设置硬超时,或者是否需要设置某种自定义超时?


案例:连接超时设置为 1 秒(方法的实际超时为 4 秒——是否可以使用 Spring/Http 客户端库将方法超时设置为 1 秒?)

应用程序日志(Http 客户端日志设置为DEBUG):

16:45:02 (org.apache.http.impl.execchain.MainClientExec) Opening connection {}->http://salesforce.com:448 
16:45:02 (org.apache.http.impl.conn.DefaultHttpClientConnectionOperator) Connecting to salesforce.com/96.43.149.26:448 
16:45:03 (org.apache.http.impl.conn.DefaultHttpClientConnectionOperator) Connect to salesforce.com/96.43.149.26:448 timed out. Connection will be retried using another IP address 
16:45:03 (org.apache.http.impl.conn.DefaultHttpClientConnectionOperator) Connecting to salesforce.com/96.43.145.26:448 
16:45:04 (org.apache.http.impl.conn.DefaultHttpClientConnectionOperator) Connect to salesforce.com/96.43.145.26:448 timed out. Connection will be retried using another IP address 
16:45:04 (org.apache.http.impl.conn.DefaultHttpClientConnectionOperator) Connecting to salesforce.com/96.43.144.26:448 
16:45:05 (org.apache.http.impl.conn.DefaultHttpClientConnectionOperator) Connect to salesforce.com/96.43.144.26:448 timed out. Connection will be retried using another IP address 
16:45:05 (org.apache.http.impl.conn.DefaultHttpClientConnectionOperator) Connecting to salesforce.com/96.43.148.26:448
16:45:06 (org.apache.http.impl.conn.DefaultManagedHttpClientConnection) http-outgoing-0: Shutdown connection 
Run Code Online (Sandbox Code Playgroud)

HTTP 客户端 bean:

<bean id="httpClientBean" class="org.apache.http.client.HttpClient" factory-bean="httpClientFactory" factory-method="getHttpClient" />
Run Code Online (Sandbox Code Playgroud)

Http Factory代码(通过Spring依赖注入设置连接超时值):

public class HttpFactory {
    private int connectionTimeout;

    public HttpFactory(int connectionTimeout, ...) {
        this.connectionTimeout = connectionTimeout;
        ...
    }

    ...

    public HttpClient getHttpClient() {
        HttpClientBuilder clientBuilder = HttpClientBuilder.create();
        ...
        RequestConfig.Builder configBuilder = RequestConfig.custom();
        configBuilder.setConnectTimeout(this.connectionTimeout);
        clientBuilder.setDefaultRequestConfig(configBuilder.build());
        ...

        return clientBuilder.build();
    }
}
Run Code Online (Sandbox Code Playgroud)

Web 服务模板 bean:

    <bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
        ...
        <property name="messageSender"> 
            <bean class="org.springframework.ws.transport.http.HttpComponentsMessageSender">
                <constructor-arg index="0">
                    <ref bean="httpClientBean" />
                </constructor-arg>
            </bean> 
        </property>
    </bean>
Run Code Online (Sandbox Code Playgroud)

服务代码(我们希望此方法调用需要 X 秒,而不是 2 倍或 3 倍秒):

// we want this method call to take ~1 second, not ~4 seconds (i.e. similar to the connection timeout value, not a multiplier)
Object obj = webServiceTemplate.marshalSendAndReceive(requestDocument);
Run Code Online (Sandbox Code Playgroud)

ok2*_*k2c 5

有两种选择

  1. 建立自定义ClientConnectionOperator

  2. 构建自定义DnsResolver. 这个选项要简单得多。

    CloseableHttpClient client = HttpClients.custom()
       .setDnsResolver(host -> new InetAddress[] { InetAddress.getByName(host) })
       .build();
    
    Run Code Online (Sandbox Code Playgroud)