How can I set connect/write/read timeout on HttpClient

43 Views Asked by At

I need to write a library works for spring-boot-2.1.6 which uses reactor-netty:0.8.9.RELEASE.

How can I set connect/write/read timeout on an instance of HttpClient and/or on an instance of TcpClient?

I just found that I can set a connection timeout like this.

final HttpClient httpClient = HttpClient.create()
        .tcpConfiguration(tc -> {
            Optional.ofNullable(getConnectTimeout())
                    .map(Duration::toMillis)
                    .map(Math::toIntExact)
                    .ifPresent(ct -> {
                        tc.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, ct);
                    });
            return tc;
        });

Is that right? And how can I set write/read time out?

2

There are 2 best solutions below

0
Malvin Lok On BEST ANSWER

you can set read/write timeout like this:

import io.netty.channel.Channel;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.handler.timeout.WriteTimeoutHandler;
import reactor.netty.http.client.HttpClient;
import reactor.netty.tcp.TcpClient;

import java.time.Duration;
import java.util.concurrent.TimeUnit;

public class TimeoutSetting {

    private HttpClient createHttpClient(Duration connectTimeout, Duration readTimeout, Duration writeTimeout) {
        return HttpClient.create()
                .tcpConfiguration(tcpClient -> tcpClient
                        .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, Math.toIntExact(connectTimeout.toMillis()))
                        .doOnConnected(connection -> {
                            connection.addHandlerLast(new ReadTimeoutHandler(Math.toIntExact(readTimeout.toMillis()), TimeUnit.MILLISECONDS));
                            connection.addHandlerLast(new WriteTimeoutHandler(Math.toIntExact(writeTimeout.toMillis()), TimeUnit.MILLISECONDS));
                        }));
    }

}

Use it like this:

    HttpClient httpClient = createHttpClient(
        Duration.ofSeconds(10), // Connect timeout
        Duration.ofSeconds(30), // Read timeout
        Duration.ofSeconds(20)  // Write timeout
    );
0
gmifflen On

First you need a config class that encapsulates the creation of the HTTPClient with timeouts:

public class HttpClientConfiguration {
  public HttpClient createHttpClient() {
    final HttpClient httpClient = HttpClient.create().tcpConfiguration(tc -> {
      // set connection timeout
      Optional.ofNullable(getConnectTimeout())
          .map(Duration::toMillis)
          .map(Math::toIntExact)
          .ifPresent(ct -> tc.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, ct));

      // add r/w timeout handlers
      tc.doOnConnected(conn -> {
        Optional.ofNullable(getReadTimeout())
            .map(Duration::getSeconds)
            .map(sec -> new ReadTimeoutHandler(sec, TimeUnit.SECONDS))
            .ifPresent(conn::addHandlerLast);

        Optional.ofNullable(getWriteTimeout())
            .map(Duration::getSeconds)
            .map(sec -> new WriteTimeoutHandler(sec, TimeUnit.SECONDS))
            .ifPresent(conn::addHandlerLast);
      });

      return tc;
    });

    return httpClient;
  }

  // example timeout retrieval
  private Duration getConnectTimeout() {
    return Duration.ofSeconds(10);
  }
  private Duration getReadTimeout() {
    return Duration.ofSeconds(5);
  }
  private Duration getWriteTimeout() {
    return Duration.ofSeconds(5);
  }
}

with this HTTPClient, you can now make a service to make HTTP calls:

@Service
public class ExternalApiService {
  private final HttpClient httpClient;

  public ExternalApiService(HttpClientConfiguration httpClientConfiguration) {
    this.httpClient = httpClientConfiguration.createHttpClient();
  }

  public Mono<String> fetchExternalData(String url) {
    return this.httpClient.get().uri(url).responseSingle(
        (response, byteBufMono) -> byteBufMono.asString());
  }
}

now you can use ExternalApiService in controllers to get external data:

@RestController
@RequestMapping("/api")
public class ExternalApiController {
  private final ExternalApiService externalApiService;

  public ExternalApiController(ExternalApiService externalApiService) {
    this.externalApiService = externalApiService;
  }

  @GetMapping("/fetch-data")
  public Mono<String> fetchData(@RequestParam String url) {
    return externalApiService.fetchExternalData(url);
  }
}