Żądanie klienta Java HTTP ze zdefiniowanym limitem czasu

91

Chciałbym wykonać BIT (testy wbudowane) na kilka serwerów w mojej chmurze. Potrzebuję, aby żądanie zakończyło się niepowodzeniem po długim czasie oczekiwania

Jak mam to zrobić w Javie?

Wydaje się, że próbowanie czegoś takiego jak poniżej nie działa.

public class TestNodeAliveness {
 public static NodeStatus nodeBIT(String elasticIP) throws ClientProtocolException, IOException {
  HttpClient client = new DefaultHttpClient();
  client.getParams().setIntParameter("http.connection.timeout", 1);

  HttpUriRequest request = new HttpGet("http://192.168.20.43");
  HttpResponse response = client.execute(request);

  System.out.println(response.toString());
  return null;
 }


 public static void main(String[] args) throws ClientProtocolException, IOException {
  nodeBIT("");
 }
}

- EDYTUJ: wyjaśnij, która biblioteka jest używana -

Używam httpclient z Apache, tutaj jest odpowiednia sekcja pom.xml

 <dependency>
   <groupId>org.apache.httpcomponents</groupId>
   <artifactId>httpclient</artifactId>
   <version>4.0.1</version>
   <type>jar</type>
 </dependency>
Maxim Veksler
źródło
Jakiej biblioteki używasz do wszystkich funkcji HTTP?
nojo
Dziękuje za komentarz. Zaktualizowałem swój post.
Maxim Veksler

Odpowiedzi:

119
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;

...

    // set the connection timeout value to 30 seconds (30000 milliseconds)
    final HttpParams httpParams = new BasicHttpParams();
    HttpConnectionParams.setConnectionTimeout(httpParams, 30000);
    client = new DefaultHttpClient(httpParams);
laz
źródło
1
Co to robi? Limit czasu tego, co to ustawia? Zobacz stackoverflow.com/questions/3000767/…
Maxim Veksler
84
HttpClient Apache ma dwa oddzielne limity czasu: limit czasu oczekiwania na ustanowienie połączenia TCP i oddzielny limit czasu oczekiwania na kolejny bajt danych. HttpConnectionParams.setConnectionTimeout () jest pierwszym, a HttpConnectionParams.setSoTimeout () drugim.
benvolioT
7
Jaki jest domyślny limit czasu? Czy to jest 0 = nieskończone?
Tobia
1
http.connection.timeout Integer Limit czasu do ustanowienia połączenia. Wartość zero oznacza, że ​​limit czasu nie jest używany.
Maveroid
26
Do przyszłych czytelników: Należy zauważyć, że wspomniane tutaj klasy są obecnie przestarzałe (od 05.07.2016), chociaż prawdopodobnie była to dobra odpowiedź, gdy została opublikowana w 2010 r. Zobacz tę odpowiedź: stackoverflow.com/a/ 19153314/192801 na coś bardziej aktualnego.
FrustratedWithFormsDesigner
125

Jeśli używasz klienta HTTP w wersji 4.3 lub nowszej, powinieneś używać tego:

RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(30 * 1000).build();
HttpClient httpClient = HttpClientBuilder.create().setDefaultRequestConfig(requestConfig).build();
grzmot
źródło
4
Dzięki! Spędziłem przynajmniej 20 minut, próbując wymyślić, jak pozbyć się przestarzałych poleceń, aby to zrobić, zanim znalazłem odpowiedź.
vextorspace
Zapomniałem SocketTimeout. Ponadto, czy „default” oznacza, że ​​jest to wartość domyślna dla wszystkiego, HttpClientco funkcja HttpClientBuilderfrom create () da za pośrednictwem build()metody? Czy tylko te, które przechodzą setDefaultRequestConfig(requestConfig)? Czy mam sens?
ADTC,
@ADTC Twoje pytanie jest nieco zagmatwane :). Domyślna konfiguracja żądania będzie używana dla wszystkich wystąpień HttpPost, HttpGet, ... używających „tylko” tego HttpClient. Jeśli utworzono inne wystąpienie HttpClient, ta konfiguracja żądania nie będzie używana z tym klientem.
Thunder
35

HttpParams jest przestarzałe w nowej bibliotece Apache HTTPClient. Korzystanie z kodu dostarczonego przez Laz prowadzi do ostrzeżeń o wycofaniu.

Proponuję zamiast tego użyć RequestConfig w Twojej instancji HttpGet lub HttpPost:

final RequestConfig params = RequestConfig.custom().setConnectTimeout(3000).setSocketTimeout(3000).build();
httpPost.setConfig(params);
pmartin8
źródło
„używając powyższego kodu” << SO nie jest forum i odpowiedzi przesuwają się w górę / w dół z kilku powodów, więc nie wiemy, do którego kodu się odnosisz. Czy możesz powiedzieć coś w stylu „używając kodu z odpowiedzi xyz”?
ADTC,
4
Przy okazji, zgrabna odpowiedź. Ale w porównaniu z odpowiedzią Thunder, jaka jest różnica między ustawieniem RequestConfigw every HttpPosta ustawieniem go jako domyślnym w HttpClient?
ADTC,
2
Tak, masz rację, powyższy kod odnosi się do „zaakceptowanej odpowiedzi”, która nie powinna się zmienić w czasie. I tak zaktualizuję swoją odpowiedź ..
pmartin8
10

Wygląda na to, że używasz HttpClient API, o którym nic nie wiem, ale możesz napisać coś podobnego do tego, używając podstawowej Javy.

try {

   HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection();
   con.setRequestMethod("HEAD");
   con.setConnectTimeout(5000); //set timeout to 5 seconds
   return (con.getResponseCode() == HttpURLConnection.HTTP_OK);

} catch (java.net.SocketTimeoutException e) {
   return false;
} catch (java.io.IOException e) {
   return false;
}
dbyrne
źródło
2
Nie wpłynie to na limit czasu dla klienta Apache HTTP.
laz
9
Nigdy nie twierdziłem, że tak;)
dbyrne
5
Pojęcie limitu czasu w protokole HttpURLConnection jest niewystarczające. Jeśli serwer zaczyna odpowiadać, ale następnie zawiesza się, limit czasu nigdy nie zostanie osiągnięty. HttpClient Apache jest lepszym wyborem ze względu na tę różnicę.
benvolioT
7

Okazało się, że ustawienie limitu czasu HttpConnectionParamsi HttpConnectionManagernie rozwiązało naszej sprawy. Jesteśmy ograniczeni do używania org.apache.commons.httpclientwersji 3.0.1.

Skończyło się na użyciu java.util.concurrent.ExecutorServicedo monitorowania HttpClient.executeMethod()połączenia.

Oto mały, samodzielny przykład

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.EntityEnclosingMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.*;

/**
 * @author Jeff Kirby
 * @since <pre>Jun 17, 2011</pre>
 */
public class Example {

   private static final String SITE = "http://some.website.com/upload";
   private static final int TIME_OUT_SECS = 5;

   // upload a file and return the response as a string
   public String post(File file) throws IOException, InterruptedException {
      final Part[] multiPart = { new FilePart("file", file.getName(), file) };
      final EntityEnclosingMethod post = new PostMethod(SITE);
      post.setRequestEntity(new MultipartRequestEntity(multiPart, post.getParams()));
      final ExecutorService executor = Executors.newSingleThreadExecutor();
      final List<Future<Integer>> futures = executor.invokeAll(Arrays.asList(new KillableHttpClient(post)), TIME_OUT_SECS, TimeUnit.SECONDS);
      executor.shutdown();
      if(futures.get(0).isCancelled()) {
         throw new IOException(SITE + " has timed out. It has taken more than " + TIME_OUT_SECS + " seconds to respond");
      }
      return post.getResponseBodyAsString();
   }

   private static class KillableHttpClient implements Callable<Integer> {

      private final EntityEnclosingMethod post;

      private KillableHttpClient(EntityEnclosingMethod post) {
         this.post = post;
      }

      public Integer call() throws Exception {
         return new HttpClient().executeMethod(post);
      }
   }
}
Kirby
źródło
7

Wspomniana metoda z najwyższymi wynikami Laza jest przestarzała od wersji 4.3. Dlatego lepiej byłoby użyć obiektu konfiguracji żądania, a następnie zbudować klienta HTTP

    private CloseableHttpClient createHttpClient()
        {
        CloseableHttpClient httpClient;
        CommonHelperFunctions helperFunctions = new CommonHelperFunctions();
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
        cm.setMaxTotal(306);
        cm.setDefaultMaxPerRoute(108);
        RequestConfig requestConfig = RequestConfig.custom()
            .setConnectTimeout(15000)
            .setSocketTimeout(15000).build();
        httpClient = HttpClients.custom()
            .setConnectionManager(cm)
            .setDefaultRequestConfig(requestConfig).build();
        return httpClient;
        }

PoolingHttpClientConnectionManager to użytkownik, który ustawia maksymalną domyślną liczbę połączeń i maksymalną liczbę połączeń na trasę. Ustawiłem go odpowiednio na 306 i 108. W większości przypadków wartości domyślne nie będą wystarczające.

Aby ustawić limit czasu: użyłem obiektu RequestConfig. Możesz również ustawić właściwość Limit czasu żądania połączenia, aby ustawić limit czasu oczekiwania na połączenie z Menedżera połączeń.

Arun Thundyill Saseendran
źródło
5

Wspomniał o tym już w komentarzu Benvoliota powyżej. Ale myślę, że warto jest post na najwyższym poziomie, ponieważ z pewnością podrapał mnie po głowie. Wysyłam to na wypadek, gdyby to pomogło komuś innemu.

Napisałem prostego klienta testowego i CoreConnectionPNames.CONNECTION_TIMEOUTlimit czasu działa w tym przypadku idealnie. Żądanie zostanie anulowane, jeśli serwer nie odpowie.

W kodzie serwera, który faktycznie próbowałem przetestować, identyczny kod nigdy nie wygasa.

Zmiana go na CoreConnectionPNames.SO_TIMEOUTprzekroczenie limitu czasu aktywności połączenia gniazda ( ) zamiast połączenia HTTP ( CoreConnectionPNames.CONNECTION_TIMEOUT) rozwiązało problem.

Przeczytaj również uważnie dokumentację Apache: http://hc.apache.org/httpcomponents-core-ga/httpcore/apidocs/org/apache/http/params/CoreConnectionPNames.html#CONNECTION_TIMEOUT

Zwróć uwagę na fragment, który mówi

Należy pamiętać, że ten parametr może być stosowany tylko do połączeń powiązanych z określonym adresem lokalnym.

Mam nadzieję, że to uratuje komuś inne drapanie się po głowie, przez które przeszedłem. To nauczy mnie nie czytać dokładnie dokumentacji!

Dan Haynes
źródło
2

Op później stwierdził, że używają Apache Commons HttpClient 3.0.1

 HttpClient client = new HttpClient();
        client.getHttpConnectionManager().getParams().setConnectionTimeout(5000);
        client.getHttpConnectionManager().getParams().setSoTimeout(5000);
rtaft
źródło
1

HttpConnectionParams.setSoTimeout(params, 10*60*1000);// for 10 mins i have set the timeout

Możesz również zdefiniować wymagany czas.

Mohammed Irfan Tirupattur
źródło