J'essaie de configurer le délai d'expiration lors d'un appel de service Web externe. J'appelle un service Web externe par Spring Rest Template dans mon service.
À des fins de test de délai de connexion, le service Web externe est arrêté et le serveur d'applications est arrêté.
J'ai configuré 10 secondes pour le délai d'attente, mais malheureusement, je reçois une exception de connexion refusée après une seconde.
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();
}
Veuillez corriger ma compréhension, le cas échéant.
Ce qui suit est lié au paramètre connectTimeout
.
Cas - Hôte inconnu
Si vous avez un hôte qui n'est pas accessible (par exemple: http://blablablabla/v1/timeout
), Vous recevrez UnknownHostException
dès que possible. AbstractPlainSocketImpl :: connect() :: !addr.isUnresolved() :: throw UnknownHostException
sans aucun timeout. L'hôte est résolu à l'aide de InetAddress.getByName(<Host_name>)
.
Cas - port inconnu
Si vous avez un hôte accessible mais qu'aucune connexion ne peut être établie, vous recevez ConnectException
- Connection refused: connect
Dès que possible. Il semble que cela se produise dans une méthode native DualStackPlainSocketImpl :: static native void waitForConnect(int fd, int timeout) throws IOException
qui est appelée depuis DualStackPlainSocketImpl :: socketConnect()
. Le délai d'attente n'est pas respecté.
Proxy? si un proxy est utilisé, les choses peuvent changer. Ayant un proxy accessible, vous pouvez obtenir le délai d'expiration.
Questions connexes cochez cette réponse comme cela est lié au cas que vous rencontrez.
Round-Robin DNS ayant le même domaine mappé à plusieurs adresses IP obligera le client à se connecter à chacune des adresses IP jusqu'à ce qu'une soit trouvée. Par conséquent, la connectTimeout()
ajoutera sa propre pénalité pour chacune des adresses IP de la liste qui ne fonctionnent pas. Lisez cet article pour en savoir plus.
Conclusion si vous souhaitez obtenir le connectTimeout
, vous devrez peut-être implémenter votre propre logique de nouvelle tentative ou utiliser un proxy.
Test connectTimeout
vous pouvez vous référer à cette réponse de différentes façons d'avoir un point de terminaison qui empêche une connexion socket de terminer ainsi obtenir un délai d'attente. En choisissant une solution, vous pouvez créer un test d'intégration dans Spring-Boot qui valide votre implémentation. Cela peut être similaire au prochain test utilisé pour tester le readTimeout
, juste que dans ce cas, l'URL peut être changée en une URL qui empêche une connexion socket.
Test readTimeout
Afin de tester le readTimeout
, il doit d'abord y avoir une connexion, donc le service doit être en place. Ensuite, un point d'extrémité peut être fourni qui, pour chaque demande, renvoie une réponse avec un retard important.
Les opérations suivantes peuvent être effectuées dans Spring-Boot afin de créer un test d'intégration:
1. Créez le 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. Configurer 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. Application Spring Boot qui sera exécutée au démarrage du test
@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);
}
}
Autres façons de configurer le RestTemplate
Configurer RestTemplate existant
@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;
}
}
Configurer un nouveau RestTemplate à l'aide de RequestConfig
@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);
}
}
Pourquoi ne pas se moquer en utilisant MockRestServiceServer
avec un RestTemplate
, remplace la fabrique de requêtes. Par conséquent, tous les paramètres RestTemplate
seront remplacés. Par conséquent, l'utilisation d'une véritable application pour les tests de temporisation peut être la seule option ici.
Remarque: vérifiez également cet article à propos de la configuration RestTemplate
qui inclut également la configuration du délai d'expiration.