Ich habe einen Webservice, der eine POST-Methode mit XML akzeptiert. Es funktioniert einwandfrei, dann kann es gelegentlich nicht mit dem Server kommunizieren, der eine IOException mit einer Nachricht auslöst The target server failed to respond
. Die nachfolgenden Aufrufe funktionieren einwandfrei.
Es passiert meistens, wenn ich ein paar Anrufe tätige und dann meine Anwendung für etwa 10-15 Minuten im Leerlauf lasse. Der erste Aufruf, den ich danach tätige, gibt diesen Fehler zurück.
Ich habe ein paar Dinge ausprobiert ...
Ich richte den Retry-Handler wie ein
HttpRequestRetryHandler retryHandler = new HttpRequestRetryHandler() {
public boolean retryRequest(IOException e, int retryCount, HttpContext httpCtx) {
if (retryCount >= 3){
Logger.warn(CALLER, "Maximum tries reached, exception would be thrown to outer block");
return false;
}
if (e instanceof org.apache.http.NoHttpResponseException){
Logger.warn(CALLER, "No response from server on "+retryCount+" call");
return true;
}
return false;
}
};
httpPost.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, retryHandler);
aber dieser Wiederholungsversuch wurde nie aufgerufen. (Ja, ich verwende die richtige Instanz der Klausel). Beim Debuggen wird diese Klasse niemals aufgerufen.
Ich habe sogar versucht, es einzurichten, HttpProtocolParams.setUseExpectContinue(httpClient.getParams(), false);
aber es nützt nichts. Kann jemand vorschlagen, was ich jetzt tun kann?
WICHTIG Neben der Frage, warum ich die Ausnahme bekomme, ist eines der wichtigsten Anliegen, warum der Retryhandler hier nicht arbeitet.
quelle
Antworten:
Höchstwahrscheinlich sind dauerhafte Verbindungen, die vom Verbindungsmanager am Leben erhalten werden, veraltet. Das heißt, der Zielserver beendet die Verbindung an ihrem Ende, ohne dass HttpClient auf dieses Ereignis reagieren kann, während die Verbindung inaktiv ist, wodurch die Verbindung halb geschlossen oder "veraltet" wird. Normalerweise ist dies kein Problem. HttpClient verwendet verschiedene Techniken, um die Gültigkeit der Verbindung nach dem Leasing aus dem Pool zu überprüfen. Selbst wenn die Überprüfung der veralteten Verbindung deaktiviert ist und eine veraltete Verbindung zum Senden einer Anforderungsnachricht verwendet wird, schlägt die Anforderungsausführung normalerweise beim Schreibvorgang mit SocketException fehl und wird automatisch wiederholt. Unter bestimmten Umständen kann der Schreibvorgang jedoch ausnahmslos beendet werden, und der nachfolgende Lesevorgang gibt -1 (Ende des Streams) zurück.
Der einfachste Weg, um Abhilfe zu schaffen, besteht darin, abgelaufene Verbindungen und Verbindungen, die nach einer Zeit der Inaktivität länger als beispielsweise 1 Minute inaktiv waren, aus dem Pool zu entfernen. Weitere Informationen finden Sie in diesem Abschnitt des HttpClient-Tutorials .
quelle
connectionManager.setValidateAfterInactivity(500)
, dh eine halbe Sekunde, löste mein Problem. Ich denke, ein paar Sekunden würden genauso gut funktionieren, aber es ist mir egal.Akzeptierte Antwort ist richtig, aber es fehlt die Lösung. Um diesen Fehler zu vermeiden, können Sie setHttpRequestRetryHandler (oder setRetryHandler für Apache-Komponenten 4.4) für Ihren HTTP-Client wie in dieser Antwort hinzufügen .
quelle
HttpClient 4.4 hatte in diesem Bereich einen Fehler im Zusammenhang mit der Überprüfung möglicherweise veralteter Verbindungen, bevor es zum Anforderer zurückkehrte. Es wurde nicht überprüft, ob eine Verbindung veraltet war, und dies führt dann zu einer sofortigen Verbindung
NoHttpResponseException
.Dieses Problem wurde in HttpClient 4.4.1 behoben. Siehe diesen JIRA und die Versionshinweise
quelle
Heutzutage werden die meisten HTTP-Verbindungen als dauerhaft angesehen, sofern nicht anders angegeben . Um Serverressourcen zu sparen, wird die Verbindung jedoch selten für immer offen gehalten. Das Standard-Verbindungszeitlimit für viele Server ist eher kurz, z. B. 5 Sekunden für Apache httpd 2.2 und höher.
Der
org.apache.http.NoHttpResponseException
Fehler ist höchstwahrscheinlich auf eine dauerhafte Verbindung zurückzuführen, die vom Server geschlossen wurde.Es ist möglich, die maximale Zeit in Millisekunden festzulegen, um nicht verwendete Verbindungen im Apache Http-Clientpool offen zu halten.
Mit Spring Boot können Sie dies auf folgende Weise erreichen:
public class RestTemplateCustomizers { static public class MaxConnectionTimeCustomizer implements RestTemplateCustomizer { @Override public void customize(RestTemplate restTemplate) { HttpClient httpClient = HttpClientBuilder .create() .setConnectionTimeToLive(1000, TimeUnit.MILLISECONDS) .build(); restTemplate.setRequestFactory( new HttpComponentsClientHttpRequestFactory(httpClient)); } } } // In your service that uses a RestTemplate public MyRestService(RestTemplateBuilder builder ) { restTemplate = builder .customizers(new RestTemplateCustomizers.MaxConnectionTimeCustomizer()) .build(); }
quelle
Die akzeptierte Antwort ist zwar richtig, aber IMHO ist nur eine Problemumgehung.
Um es klar auszudrücken: Es ist eine ganz normale Situation, dass eine dauerhafte Verbindung veraltet sein kann. Leider ist es sehr schlecht, wenn die HTTP-Clientbibliothek nicht richtig damit umgehen kann.
Da dieses fehlerhafte Verhalten in Apache HttpClient viele Jahre lang nicht behoben wurde, würde ich definitiv lieber zu einer Bibliothek wechseln, die sich leicht von einem veralteten Verbindungsproblem erholen lässt, z. B. OkHttp.
Warum?
NoHttpResponseException
) sagen .Als ich zu OkHttp wechselte, verschwanden meine Probleme
NoHttpResponseException
für immer.quelle
Lösung : Ändern Sie die ReuseStrategy in nie
Da dieses Problem sehr komplex ist und es so viele verschiedene Faktoren gibt, die fehlschlagen können, war ich froh, diese Lösung in einem anderen Beitrag zu finden: So lösen Sie org.apache.http.NoHttpResponseException
Verbindungen niemals wiederverwenden: Konfigurieren in org.apache.http.impl.client.AbstractHttpClient:
httpClient.setReuseStrategy(new NoConnectionReuseStrategy());
Dasselbe kann auf einem org.apache.http.impl.client.HttpClientBuilder-Builder konfiguriert werden:
builder.setConnectionReuseStrategy(new NoConnectionReuseStrategy());
quelle
Dies kann passieren, wenn dies
disableContentCompression()
auf einem Pooling-Manager festgelegt ist, der Ihrem HttpClient zugewiesen ist, und der Zielserver versucht, die gzip-Komprimierung zu verwenden.quelle
org.springframework.cloud.netflix.zuul.filters.route.SimpleHostRoutingFilter
entfernen.disableContentCompression
Ich habe die Abhängigkeitsversion nicht geändert, da es ein ziemliches Durcheinander mit Gläsern gibtGleiches Problem für mich auf Apache http Client 4.5.5 Hinzufügen von Standard-Header
Verbindung: schließen
das Problem lösen
quelle
Ich habe das gleiche Problem, das ich durch Hinzufügen von "connection: close" als Erweiterung gelöst habe.
Schritt 1: Erstellen Sie eine neue Klasse ConnectionCloseExtension
import com.github.tomakehurst.wiremock.common.FileSource; import com.github.tomakehurst.wiremock.extension.Parameters; import com.github.tomakehurst.wiremock.extension.ResponseTransformer; import com.github.tomakehurst.wiremock.http.HttpHeader; import com.github.tomakehurst.wiremock.http.HttpHeaders; import com.github.tomakehurst.wiremock.http.Request; import com.github.tomakehurst.wiremock.http.Response; public class ConnectionCloseExtension extends ResponseTransformer { @Override public Response transform(Request request, Response response, FileSource files, Parameters parameters) { return Response.Builder .like(response) .headers(HttpHeaders.copyOf(response.getHeaders()) .plus(new HttpHeader("Connection", "Close"))) .build(); } @Override public String getName() { return "ConnectionCloseExtension"; } }
Schritt 2: Legen Sie die Erweiterungsklasse in wireMockServer wie folgt fest:
final WireMockServer wireMockServer = new WireMockServer(options() .extensions(ConnectionCloseExtension.class) .port(httpPort));
quelle