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

Java中如何为API请求限制每秒字节数(bytes per second)以触发408响应(集成自动化测试场景)

Java中如何为API请求限制每秒字节数(bytes per second)以触发408响应(集成自动化测试场景)

嘿,我完全理解你在集成自动化测试里想要模拟慢请求触发408超时的需求——之前做性能边界测试时也折腾过类似的场景。你提到的OkHttp NetworkInterceptor确实是正确的方向,核心思路就是通过拦截请求,控制请求体的发送速率,让请求慢到触发服务器的超时阈值。下面给你具体的实现方案,不管是OkHttp还是基于它的RestTemplate都能用:

一、OkHttp 实现限速拦截器

我们需要自定义一个Interceptor,包装原有的RequestBody,重写它的writeTo方法,在写入字节流时加入延迟,从而控制每秒发送的字节数。

1. 自定义限速RequestBody包装类

这个类负责实际的速率控制逻辑:

import okhttp3.MediaType;
import okhttp3.RequestBody;
import okio.Buffer;
import okio.BufferedSink;
import okio.ForwardingSink;
import okio.Okio;
import okio.Sink;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

class ThrottledRequestBody extends RequestBody {
    private final RequestBody delegate;
    private final long bytesPerSecond;

    public ThrottledRequestBody(RequestBody delegate, long bytesPerSecond) {
        this.delegate = delegate;
        this.bytesPerSecond = bytesPerSecond;
    }

    @Override
    public MediaType contentType() {
        return delegate.contentType();
    }

    @Override
    public long contentLength() throws IOException {
        return delegate.contentLength();
    }

    @Override
    public void writeTo(BufferedSink sink) throws IOException {
        // 包装sink,加入速率控制
        Sink throttledSink = new ForwardingSink(sink) {
            long bytesWritten = 0;
            long startTime = System.nanoTime();

            @Override
            public void write(Buffer source, long byteCount) throws IOException {
                bytesWritten += byteCount;
                // 计算理论上需要的时间,和实际已用时间的差值,决定是否休眠
                long expectedTimeMs = (bytesWritten * 1000) / bytesPerSecond;
                long actualTimeMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime);
                if (actualTimeMs < expectedTimeMs) {
                    try {
                        Thread.sleep(expectedTimeMs - actualTimeMs);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        throw new IOException("Throttling interrupted", e);
                    }
                }
                super.write(source, byteCount);
            }
        };
        delegate.writeTo(Okio.buffer(throttledSink));
    }
}

2. 实现限速Interceptor

把上面的RequestBody包装逻辑放到Interceptor里,拦截所有请求并替换为限速的请求体:

import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;

public class ThrottlingInterceptor implements Interceptor {
    private final long bytesPerSecond;

    public ThrottlingInterceptor(long bytesPerSecond) {
        this.bytesPerSecond = bytesPerSecond;
    }

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request originalRequest = chain.request();
        if (originalRequest.body() == null) {
            // 如果没有请求体,直接放行(比如GET请求)
            return chain.proceed(originalRequest);
        }
        // 包装请求体为限速版本
        Request throttledRequest = originalRequest.newBuilder()
                .method(originalRequest.method(), new ThrottledRequestBody(originalRequest.body(), bytesPerSecond))
                .build();
        return chain.proceed(throttledRequest);
    }
}

3. 配置OkHttpClient使用拦截器

创建客户端时加入这个拦截器,就能让所有请求自动限速:

OkHttpClient client = new OkHttpClient.Builder()
        .addInterceptor(new ThrottlingInterceptor(100)) // 限制为100字节/秒
        .build();

二、RestTemplate 适配方案

如果你的项目用的是RestTemplate,可以把它的底层客户端替换为OkHttp,然后复用上面的拦截器:

// 先配置带限速的OkHttpClient
OkHttpClient okHttpClient = new OkHttpClient.Builder()
        .addInterceptor(new ThrottlingInterceptor(100))
        .build();
// 替换RestTemplate的ClientHttpRequestFactory
RestTemplate restTemplate = new RestTemplate(new OkHttp3ClientHttpRequestFactory(okHttpClient));
// 之后用这个restTemplate发请求就会自动限速了

三、注意事项

  • 服务器超时配置:要确保服务器的请求超时时间(比如ConnectionTimeout或ReadTimeout)比你的请求发送总时间短。比如请求体有1000字节,限速100字节/秒,发送需要10秒,那服务器超时要设为5秒左右才会触发408。
  • GET请求处理:上面的拦截器对没有请求体的GET请求直接放行,如果要限制GET请求的响应速率(不过你要的是触发请求超时,所以主要关注请求体发送速率即可),可以类似地包装ResponseBody。
  • 线程安全:上面的ThrottledRequestBody是每个请求实例化一次,所以线程安全没问题,适合测试场景。

备注:内容来源于stack exchange,提问作者Drummondst

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.22 14:14:30