使用 Spring-ws 和 4.5.x Http 客户端忽略 DNS 循环以获得可靠的超时
Ignore DNS Round-Robin for a Reliable Timeout with Spring-ws and 4.5.x Http Client
我们正在使用 Http Client 4.5.x 和 Spring-Ws 并使用 webServiceTemplate.marshalSendAndReceive(requestObject)
方法发出请求。我们希望有一个可靠的连接超时值,但目前 运行 遇到描述的问题 here in section 8 (DNS round robin),其中尝试了多个 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
HTTP 客户端 bean:
<bean id="httpClientBean" class="org.apache.http.client.HttpClient" factory-bean="httpClientFactory" factory-method="getHttpClient" />
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();
}
}
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>
服务代码(我们希望此方法调用花费 X 秒,而不是 2x 或 3x 秒):
// 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);
有两种选择
建立自定义 ClientConnectionOperator
建立自定义 DnsResolver
。这个选项就简单多了。
CloseableHttpClient client = HttpClients.custom()
.setDnsResolver(host -> new InetAddress[] { InetAddress.getByName(host) })
.build();
我们正在使用 Http Client 4.5.x 和 Spring-Ws 并使用 webServiceTemplate.marshalSendAndReceive(requestObject)
方法发出请求。我们希望有一个可靠的连接超时值,但目前 运行 遇到描述的问题 here in section 8 (DNS round robin),其中尝试了多个 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
HTTP 客户端 bean:
<bean id="httpClientBean" class="org.apache.http.client.HttpClient" factory-bean="httpClientFactory" factory-method="getHttpClient" />
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();
}
}
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>
服务代码(我们希望此方法调用花费 X 秒,而不是 2x 或 3x 秒):
// 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);
有两种选择
建立自定义
ClientConnectionOperator
建立自定义
DnsResolver
。这个选项就简单多了。CloseableHttpClient client = HttpClients.custom() .setDnsResolver(host -> new InetAddress[] { InetAddress.getByName(host) }) .build();