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
相关产品推荐
相关产品推荐

