You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何测试WebClient的ConnectTimeoutException与WriteTimeoutException?

没问题!我来帮你搞定ConnectTimeout和WriteTimeout的测试方法,结合你已经配置的Reactor WebClient,下面是具体的实现思路和代码示例:

测试ConnectTimeout

ConnectTimeout是TCP连接建立阶段的超时,我们只需要模拟一个无法建立连接的目标地址(比如不存在的IP/未监听的端口),同时设置极短的超时时间,就能触发这个异常。

这里用JUnit 5 + Reactor的StepVerifier来验证异常:

import io.netty.channel.ChannelOption;
import io.netty.handler.timeout.ConnectTimeoutException;
import org.junit.jupiter.api.Test;
import reactor.netty.http.client.HttpClient;
import reactor.test.StepVerifier;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;

@Test
void testConnectTimeout() {
    // 配置一个极短的连接超时(100ms),确保快速触发异常
    HttpClient httpClient = HttpClient.create()
            .tcpConfiguration(client -> client
                    .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 100));

    WebClient testWebClient = WebClient.builder()
            .clientConnector(new ReactorClientHttpConnector(httpClient))
            .baseUrl("http://192.168.99.99:9999") // 不存在的地址,无法建立TCP连接
            .build();

    // 发起请求并验证异常
    Mono<String> request = testWebClient.get()
            .retrieve()
            .bodyToMono(String.class);

    StepVerifier.create(request)
            .expectError(ConnectTimeoutException.class)
            .verify();
}

原理:这个目标地址没有服务在监听,TCP握手无法完成,超过CONNECT_TIMEOUT_MILLIS设置的时间后,Netty会抛出ConnectTimeoutException,我们通过StepVerifier确认这个异常被正确抛出。

测试WriteTimeout

WriteTimeout是客户端向服务器发送数据时的超时,需要模拟一个接受连接但不读取任何数据的服务器,这样客户端的发送缓冲区会被填满,写操作会被阻塞直到超时。

我们可以用Netty快速搭建一个极简的测试服务器,配合StepVerifier验证异常:

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.timeout.WriteTimeoutException;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import reactor.netty.http.client.HttpClient;
import reactor.test.StepVerifier;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import java.net.InetSocketAddress;

public class WebClientTimeoutTests {
    private NioEventLoopGroup serverGroup;
    private Channel serverChannel;

    @BeforeEach
    void setupTestServer() throws InterruptedException {
        // 启动一个Netty服务器,只接受连接但不处理数据
        serverGroup = new NioEventLoopGroup();
        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(serverGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) {
                        // 空处理器,不读取任何请求数据
                        ch.pipeline().addLast(new ChannelInboundHandlerAdapter());
                    }
                });

        // 绑定随机可用端口
        ChannelFuture future = bootstrap.bind(0).sync();
        serverChannel = future.channel();
    }

    @AfterEach
    void teardownTestServer() {
        // 关闭服务器资源
        serverChannel.close().syncUninterruptibly();
        serverGroup.shutdownGracefully();
    }

    @Test
    void testWriteTimeout() {
        int writeTimeoutMillis = 100;
        String serverUrl = "http://localhost:" + ((InetSocketAddress) serverChannel.localAddress()).getPort();

        // 配置WebClient,设置短写超时
        HttpClient httpClient = HttpClient.create()
                .tcpConfiguration(client -> client
                        .doOnConnected(conn -> conn
                                .addHandlerLast(new WriteTimeoutHandler(writeTimeoutMillis))));

        WebClient testWebClient = WebClient.builder()
                .clientConnector(new ReactorClientHttpConnector(httpClient))
                .baseUrl(serverUrl)
                .build();

        // 发送一个大请求体,确保写操作持续足够长时间触发超时
        String largeRequestBody = new String(new char[1024 * 1024]).replace('\0', 'a');

        Mono<String> request = testWebClient.post()
                .bodyValue(largeRequestBody)
                .retrieve()
                .bodyToMono(String.class);

        StepVerifier.create(request)
                .expectError(WriteTimeoutException.class)
                .verify();
    }
}

原理:服务器接受连接后不读取数据,客户端发送大请求体时,数据会积压在发送缓冲区,超过WriteTimeoutHandler设置的时间后,Netty会抛出WriteTimeoutException,我们通过StepVerifier确认异常类型。

内容的提问来源于stack exchange,提问作者Anoop Hallimala

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:09:29