Die folgenden Angaben beziehen sich auf die Einstellung connectTimeout
.
Case - unknown host
Wenn Sie einen Host haben, die nicht erreichbar ist (zB: http://blablablabla/v1/timeout
), dann werden Sie UnknownHostException
so schnell wie möglich erhalten. AbstractPlainSocketImpl :: connect() :: !addr.isUnresolved() :: throw UnknownHostException
ohne Zeitüberschreitung. Der Host wird mit InetAddress.getByName(<host_name>)
aufgelöst.
Case - unbekannt Port
Wenn Sie einen Host haben, der erreichbar ist, aber keine Verbindung getan werden kann, dann erhalten Sie ConnectException
- Connection refused: connect
so schnell wie möglich. Es scheint, dass dies in einer nativen Methode DualStackPlainSocketImpl :: static native void waitForConnect(int fd, int timeout) throws IOException
geschieht, die von DualStackPlainSocketImpl :: socketConnect()
aufgerufen wird. Die Zeitüberschreitung wird nicht berücksichtigt.
Proxy? Wenn ein Proxy verwendet wird, können sich die Dinge ändern. Wenn Sie einen erreichbaren Proxy haben, erhalten Sie möglicherweise das Zeitlimit.
In Verbindung stehende Fragen überprüfen Sie this answer, wie im Zusammenhang mit dem Fall, den Sie begegnen.
DNS-Round-Robin mit der gleichen Domäne zugeordnet mehrere IP-Adressen wird dazu führen, dass der Client eine Verbindung zu jeder der IPs, bis eine gefunden wird. Daher fügt die connectTimeout()
ihre eigene Strafe für jede der IPs in der Liste hinzu, die nicht funktionieren. Lesen Sie this article für mehr.
Fazit Wenn Sie die connectTimeout
erhalten möchten, müssen Sie möglicherweise eine eigene Wiederholungslogik implementieren oder einen Proxy verwenden.
Testing connectTimeout
können Sie auf this answer verschiedener Arten beziehen einen Endpunkt, die einen Timeout somit eine Socket-Verbindung verhindert Abschluss zu erhalten. Wenn Sie eine Lösung auswählen, können Sie im Spring-Boot einen Integrationstest erstellen, der Ihre Implementierung validiert. Dies kann ähnlich dem nächsten Test sein, der für das Testen der readTimeout
verwendet wird, nur dass für diesen Fall die URL in eine geändert werden kann, die eine Socket-Verbindung verhindert.
Testing readTimeout
Um die readTimeout
dort zu testen, müssen Sie zunächst eine Verbindung sein, daher muss der Dienst bis sein. Dann kann ein Endpunkt bereitgestellt werden, der für jede Anforderung eine Antwort mit einer großen Verzögerung zurückgibt.
Folgendes kann im Frühjahr-Boot, um einen Integrationstest erstellen erfolgen:
1. Erstellen Sie den Test
@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. Konfigurieren 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 App, die ausgeführt wird, wenn der Teststern ts
@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);
}
}
Alternative Möglichkeiten, die RestTemplate Konfiguration
Configure bestehende 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;
}
}
Konfigurieren ein neues RestTemplate RequestConfig mit
@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);
}
}
Warum nicht spotten mit MockRestServiceServer
mit einer RestTemplate
, ersetzt die Anfrage Fabrik. Daher werden alle RestTemplate
Einstellungen ersetzt. Daher könnte die Verwendung einer echten App für Timeout-Tests hier die einzige Option sein.
Hinweis: Überprüfen Sie auch this article über RestTemplate
Konfiguration, die auch die Timeout-Konfiguration enthalten.
können Sie eine Verbindung herstellen, ohne Timeout zu konfigurieren? – Vaibs
@ Vaibs, Nein. Für Testzwecke habe ich keinen externen Webservice gestartet. Das angegebene Zeitlimit funktioniert nicht. – Easy2DownVoteHard2Ans
Nur mit Ihrem Code in meiner Umgebung getestet. Es funktioniert gut. Ihr Dienst sollte dies prüfen. Um dies zu erreichen, ist mein Vorschlag, den Verbindungszeitlimitwert zu minimieren. Wechsel zu 10ms. – Vaibs