A Quick Guide to Timeouts in OkHttp – OkHttp中超时的快速指南

最后修改: 2019年 12月 1日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this quick tutorial, we’ll focus on different types of timeouts we can set for the OkHttp client.

在这个快速教程中,我们将重点介绍我们可以为OkHttp客户端设置的不同类型的超时。

For the more general overview of the OkHttp library, check our introductory OkHttp guide.

对于OkHttp库的更一般的概述,请查看我们的OkHttp介绍性指南

2. Connect Timeout

2.连接超时

A connect timeout defines a time period in which our client should establish a connection with a target host.

连接超时定义了一个时间段,我们的客户应该在这个时间段内与目标主机建立连接

By default, for the OkHttpClient, this timeout is set to 10 seconds.

默认情况下,对于OkHttpClient,该超时被设置为10秒

However, we can easily change its value using the OkHttpClient.Builder#connectTimeout method. A value of zero means no timeout at all.

然而,我们可以使用OkHttpClient.Builder#connectTimeout方法轻松地改变其值。值为0意味着完全没有超时。

Let’s now see how to build and use an OkHttpClient with a custom connection timeout:

现在让我们看看如何建立和使用一个带有自定义连接超时的OkHttpClient

@Test
public void whenConnectTimeoutExceeded_thenSocketTimeoutException() {
    OkHttpClient client = new OkHttpClient.Builder()
      .connectTimeout(10, TimeUnit.MILLISECONDS)
      .build();

    Request request = new Request.Builder()
      .url("http://203.0.113.1") // non routable address
      .build();

    Throwable thrown = catchThrowable(() -> client.newCall(request).execute());

    assertThat(thrown).isInstanceOf(SocketTimeoutException.class);
}

The above example shows that the client throws a SocketTimeoutException when the connection attempt exceeds the configured timeout.

上面的例子显示,当连接尝试超过配置的超时时,客户端抛出一个SocketTimeoutException

3. Read Timeout

3.读取超时

A read timeout is applied from the moment the connection between a client and a target host has been successfully established.

读取超时是从客户机和目标主机之间成功建立连接的那一刻开始的。

It defines a maximum time of inactivity between two data packets when waiting for the server’s response.

它定义了一个等待服务器响应时两个数据包之间的最大不活动时间

The default timeout of 10 seconds can be changed using OkHttpClient.Builder#readTimeout. Analogously as for the connect timeout, a zero value indicates no timeout.

可以使用OkHttpClient.Builder#readTimeout来改变默认的10秒超时。与连接超时类似,零值表示没有超时。

Let’s now see how to configure a custom read timeout in practice:

现在让我们看看如何在实践中配置自定义的读取超时。

@Test
public void whenReadTimeoutExceeded_thenSocketTimeoutException() {
    OkHttpClient client = new OkHttpClient.Builder()
      .readTimeout(10, TimeUnit.MILLISECONDS)
      .build();

    Request request = new Request.Builder()
      .url("https://httpbin.org/delay/2") // 2-second response time
      .build();

    Throwable thrown = catchThrowable(() -> client.newCall(request).execute());

    assertThat(thrown).isInstanceOf(SocketTimeoutException.class);
}

As we can see, the server doesn’t return the response within the defined timeout of 500 ms. As a result, the OkHttpClient throws a SocketTimeoutException.

我们可以看到,服务器并没有在定义的500毫秒的超时内返回响应。因此,OkHttpClient抛出一个SocketTimeoutException.

4. Write Timeout

4.写入超时

A write timeout defines a maximum time of inactivity between two data packets when sending the request to the server.

写入超时定义了向服务器发送请求时两个数据包之间的最大不活动时间

Similarly, as for the connect and read timeouts, we can override the default value of 10 seconds using OkHttpClient.Builder#writeTimeout. As a convention, a zero value means no timeout at all.

同样,对于连接和读取超时,我们可以使用OkHttpClient.Builder#writeTimeout来覆盖10秒的默认值。作为一个惯例,零值意味着完全没有超时。

In the following example, we set a very short write timeout of 10 ms and post a 1 MB content to the server:

在下面的例子中,我们设置了一个非常短的写入超时,即10毫秒,并向服务器发布一个1MB的内容。

@Test
public void whenWriteTimeoutExceeded_thenSocketTimeoutException() {
    OkHttpClient client = new OkHttpClient.Builder()
      .writeTimeout(10, TimeUnit.MILLISECONDS)
      .build();

    Request request = new Request.Builder()
      .url("https://httpbin.org/delay/2")
      .post(RequestBody.create(MediaType.parse("text/plain"), create1MBString()))
      .build();

    Throwable thrown = catchThrowable(() -> client.newCall(request).execute());

    assertThat(thrown).isInstanceOf(SocketTimeoutException.class);
}

As we see, due to the large payload, our client isn’t able to send a request body to the server within the defined timeout. Consequently, the OkHttpClient throws a SocketTimeoutException.

正如我们所看到的,由于大的有效载荷,我们的客户端不能够在定义的超时内向服务器发送一个请求体。因此,OkHttpClient抛出一个SocketTimeoutException

5. Call Timeout

5.呼叫超时

A call timeout is a bit different than the connect, read and write timeouts we already discussed.

调用超时与我们已经讨论过的连接、读和写超时有点不同。

It defines a time limit for a complete HTTP call. This includes resolving DNS, connecting, writing the request body, server processing, as well as reading the response body.

它定义了一个完整的HTTP调用的时间限制。这包括解析DNS、连接、编写请求体、服务器处理以及读取响应体。

Unlike other timeouts, it’s default value is set to zero which implies no timeout. But of course, we can configure a custom value using OkHttpClient.Builder#callTimeout method.

与其他超时不同,它的默认值被设置为零,这意味着没有超时。但当然,我们可以使用OkHttpClient.Builder#callTimeout方法配置一个自定义值。

Let’s see a practical usage example:

让我们看看一个实际的使用例子。

@Test
public void whenCallTimeoutExceeded_thenInterruptedIOException() {
    OkHttpClient client = new OkHttpClient.Builder()
      .callTimeout(1, TimeUnit.SECONDS)
      .build();

    Request request = new Request.Builder()
      .url("https://httpbin.org/delay/2")
      .build();

    Throwable thrown = catchThrowable(() -> client.newCall(request).execute());

    assertThat(thrown).isInstanceOf(InterruptedIOException.class);
}

As we can see, the call timeout is exceeded and the OkHttpClient throws an InterruptedIOException.

正如我们所看到的,调用超时,OkHttpClient抛出一个InterruptedIOException.

6. Per-Request Timeout

6.每个请求的超时

It’s recommended to create a single OkHttpClient instance and reuse it for all the HTTP calls across our application.

建议创建一个单一的OkHttpClient实例,并将其重复用于整个应用程序的所有HTTP调用

Sometimes, however, we know that a certain request takes more time than all the others. In this situation, we need to extend a given timeout only for that particular call.

然而,有时我们知道某个请求比其他所有请求花费更多时间。在这种情况下,我们需要只为那个特定的调用延长一个给定的超时时间

In such cases, we can use an OkHttpClient#newBuilder method. This builds a new client that shares the same settings. We can then use the builder methods to adjust timeout settings as needed.

在这种情况下,我们可以使用一个OkHttpClient#newBuilder方法。这将建立一个新的客户端,共享相同的设置。然后我们可以根据需要使用构建器方法来调整超时设置。

Let’s now see how to do this in practice:

现在让我们看看如何在实践中做到这一点。

@Test
public void whenPerRequestTimeoutExtended_thenResponseSuccess() throws IOException {
    OkHttpClient defaultClient = new OkHttpClient.Builder()
      .readTimeout(1, TimeUnit.SECONDS)
      .build();

    Request request = new Request.Builder()
      .url("https://httpbin.org/delay/2")
      .build();

    Throwable thrown = catchThrowable(() -> defaultClient.newCall(request).execute());

    assertThat(thrown).isInstanceOf(InterruptedIOException.class);

    OkHttpClient extendedTimeoutClient = defaultClient.newBuilder()
      .readTimeout(5, TimeUnit.SECONDS)
      .build();

    Response response = extendedTimeoutClient.newCall(request).execute();
    assertThat(response.code()).isEqualTo(200);
}

As we see the defaultClient failed to complete the HTTP call because of the exceeded read timeout.

正如我们所看到的,defaultClient未能完成HTTP调用,因为超过了读取超时。

That’s why we created the extendedTimeoutClient, adjusted the timeout value, and successfully executed the request.

这就是为什么我们创建了extendedTimeoutClient,调整了超时值,并成功执行了请求。

7. Summary

7.摘要

In this article, we explored different timeouts we can configure for the OkHttpClient.

在这篇文章中,我们探讨了我们可以为OkHttpClient配置的不同超时。

We also shortly described when the connect, read and write timeouts are applied during an HTTP call.

我们也很快描述了在HTTP调用过程中何时应用连接、读和写超时。

Additionally, we showed how easy it is to change a certain timeout value only for a single request.

此外,我们展示了只为单个请求改变某个超时值是多么容易

As usual, all the code examples are available over on GitHub.

像往常一样,所有的代码实例都可以在GitHub上找到