2017-05-11 8 views
0

Ich versuche, Timeout beim externen Web-Service-Aufruf zu konfigurieren. Ich rufe externen Web Service von Spring Rest Template in meinem Service an.Spring RestTemplate Verbindung Timeout funktioniert nicht

Zum Testen des Verbindungszeitlimits wird der externe Webdienst gestoppt und der Anwendungsserver ist inaktiv.

Ich habe 10 Sekunden für Timeout konfiguriert, aber leider bekomme ich Verbindung abgelehnt Ausnahme nach einer Sekunde.

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(); 
} 

Bitte korrigieren Sie mein Verständnis, wenn vorhanden.

+0

können Sie eine Verbindung herstellen, ohne Timeout zu konfigurieren? – Vaibs

+0

@ Vaibs, Nein. Für Testzwecke habe ich keinen externen Webservice gestartet. Das angegebene Zeitlimit funktioniert nicht. – Easy2DownVoteHard2Ans

+0

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

Antwort

6

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.

Verwandte Themen