gpt4 book ai didi

java - Spring RestTemplate 连接超时不起作用

转载 作者:塔克拉玛干 更新时间:2023-11-03 04:00:19 25 4
gpt4 key购买 nike

我正在尝试配置外部 Web 服务调用超时。我在我的服务中通过 Spring Rest 模板调用外部 Web 服务。

出于连接超时测试目的,外部 Web 服务已停止且应用程序服务器已关闭。

我已经配置了 10 秒的超时,但不幸的是我在一秒钟后收到连接被拒绝的异常。

try {   
final RestTemplate restTemplate = new RestTemplate();

((org.springframework.http.client.SimpleClientHttpRequestFactory)
restTemplate.getRequestFactory()).setReadTimeout(1000*10);

((org.springframework.http.client.SimpleClientHttpRequestFactory)
restTemplate.getRequestFactory()).setConnectTimeout(1000*10);

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);

HttpEntity<String> entity = new HttpEntity<String>(reqJSON, headers);

ResponseEntity<String> response = restTemplate.exchange(wsURI, HttpMethod.POST, entity, String.class);

String premiumRespJSONStr = response.getBody();
}

如有理解请指正

最佳答案

以下与connectTimeout相关设置。

案例 - 未知宿主

如果您的主机无法访问(例如:http://blablablabla/v1/timeout),那么您将收到 UnknownHostException尽早。 AbstractPlainSocketImpl :: connect() :: !addr.isUnresolved() :: throw UnknownHostException没有任何超时。使用 InetAddress.getByName(<host_name>) 解析主机.

案例 - 未知端口

如果您有一个可访问的主机但无法建立连接,那么您会收到 ConnectException - Connection refused: connect尽早。这似乎发生在本地方法中 DualStackPlainSocketImpl :: static native void waitForConnect(int fd, int timeout) throws IOException这是从 DualStackPlainSocketImpl :: socketConnect() 调用的.不遵守超时。

代理?如果使用代理,情况可能会发生变化。拥有可访问的代理,您可能会超时。

相关问题查看this answer与您遇到的情况有关。

DNS Round-Robin 将同一域映射到多个 IP 地址将导致客户端连接到每个 IP,直到找到一个为止。因此 connectTimeout()将为列表中每个不起作用的 IP 添加自己的惩罚。阅读this article了解更多。

结论如果你想获得connectTimeout那么您可能需要实现自己的重试逻辑或使用代理。

测试 connectTimeout 可以引用this answer具有阻止套接字连接完成从而获得超时的端点的各种方法。选择一个解决方案,您可以在 spring-boot 中创建一个集成测试来验证您的实现。这可以类似于用于测试 readTimeout 的下一个测试,只是对于这种情况,可以将 URL 更改为阻止套接字连接的 URL。

测试 readTimeout

为了测试readTimeout首先需要有一个连接,因此服务需要启动。然后可以提供一个端点,对于每个请求,返回一个具有较大延迟的响应。

为了创建集成测试,可以在 spring-boot 中完成以下操作:

<强>1。创建测试

@RunWith(SpringRunner.class)
@SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = { RestTemplateTimeoutConfig.class, RestTemplateTimeoutApplication.class }
)
public class RestTemplateTimeoutTests {

@Autowired
private RestOperations restTemplate;

@LocalServerPort
private int port;

@Test
public void resttemplate_when_path_exists_and_the_request_takes_too_long_throws_exception() {
System.out.format("%s - %s\n", Thread.currentThread().getName(), Thread.currentThread().getId());

Throwable throwable = catchThrowable(() ->
restTemplate.getForEntity(String.format("http://localhost:%d/v1/timeout", port), String.class));

assertThat(throwable).isInstanceOf(ResourceAccessException.class);
assertThat(throwable).hasCauseInstanceOf(SocketTimeoutException.class);
}
}

<强>2。配置 RestTemplate

@Configuration
public class RestTemplateTimeoutConfig {

private final int TIMEOUT = (int) TimeUnit.SECONDS.toMillis(10);

@Bean
public RestTemplate restTemplate() {
return new RestTemplate(getRequestFactory());
}

private ClientHttpRequestFactory getRequestFactory() {
HttpComponentsClientHttpRequestFactory factory =
new HttpComponentsClientHttpRequestFactory();

factory.setReadTimeout(TIMEOUT);
factory.setConnectTimeout(TIMEOUT);
factory.setConnectionRequestTimeout(TIMEOUT);
return factory;
}
}

<强>3。测试开始时将运行的 Spring Boot 应用

@SpringBootApplication
@Controller
@RequestMapping("/v1/timeout")
public class RestTemplateTimeoutApplication {

public static void main(String[] args) {
SpringApplication.run(RestTemplateTimeoutApplication.class, args);
}

@GetMapping()
public @ResponseStatus(HttpStatus.NO_CONTENT) void getDelayedResponse() throws InterruptedException {
System.out.format("Controller thread = %s - %s\n", Thread.currentThread().getName(), Thread.currentThread().getId());
Thread.sleep(20000);
}
}

配置 RestTemplate 的替代方法

配置现有的 RestTemplate

@Configuration
public class RestTemplateTimeoutConfig {

private final int TIMEOUT = (int) TimeUnit.SECONDS.toMillis(10);

// consider that this is the existing RestTemplate
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}

// this will change the RestTemplate settings and create another bean
@Bean
@Primary
public RestTemplate newRestTemplate(RestTemplate restTemplate) {
SimpleClientHttpRequestFactory factory =
(SimpleClientHttpRequestFactory) restTemplate.getRequestFactory();

factory.setReadTimeout(TIMEOUT);
factory.setConnectTimeout(TIMEOUT);

return restTemplate;
}
}

使用 RequestConfig 配置一个新的 RestTemplate

@Configuration
public class RestTemplateTimeoutConfig {

private final int TIMEOUT = (int) TimeUnit.SECONDS.toMillis(10);

@Bean
public RestTemplate restTemplate() {
return new RestTemplate(getRequestFactoryAdvanced());
}

private ClientHttpRequestFactory getRequestFactoryAdvanced() {
RequestConfig config = RequestConfig.custom()
.setSocketTimeout(TIMEOUT)
.setConnectTimeout(TIMEOUT)
.setConnectionRequestTimeout(TIMEOUT)
.build();

CloseableHttpClient client = HttpClientBuilder
.create()
.setDefaultRequestConfig(config)
.build();

return new HttpComponentsClientHttpRequestFactory(client);
}
}

为什么不模拟使用MockRestServiceServerRestTemplate , 取代请求工厂。因此任何 RestTemplate设置将被替换。因此,使用真正的应用程序进行超时测试可能是这里唯一的选择。

注意:同时检查 this article关于RestTemplate配置,其中还包括超时配置。

关于java - Spring RestTemplate 连接超时不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43909219/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com