TestNG测网络延迟就是将网络操作用Java标准库或第三方库实现,用TestNG来组织测试步骤、控制执行次数、收集延迟数据并断言是不是满足阈值。
提供两个可直接运行的示例:
TCP 连接延迟测试 — 测量 TCP 三次握手所需时间。
HTTP 请求延迟测试 — 测量完整的 HTTP 往返时间。
准备工作 (Maven 依赖)
xml
<!-- 仅需 TestNG -->
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.10.2</version>
<scope>test</scope>
</dependency>
TCP连接延迟测试
原理:记录new Socket() + connect(timeout) 的前后时间差,多次测试后输出统计值并断言。
java
import org.testng.annotations.*;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
public class TcpLatencyTest {
// 被测主机和端口
private static final String HOST = "www.zmtests.com";
private static final int PORT = 80; // HTTP 端口,也可换其他开放端口
private static final int CONNECT_TIMEOUT = 3000; // 连接超时(ms)
// 存储每次成功测试的延迟(ns)
private final List<Long> latencies = new ArrayList<>();
/**
* 预热:运行几次不记录结果,让 JIT 优化、操作系统缓存稳定
*/
@Test(invocationCount = 5, groups = "warmup")
public void warmup() throws Exception {
measureTcpConnect();
}
/**
* 正式测试:重复执行 50 次,每次记录 TCP 连接延迟
*/
@Test(invocationCount = 50, dependsOnGroups = "warmup")
public void tcpConnectLatencyTest() throws Exception {
long start = System.nanoTime();
measureTcpConnect(); // 建连操作
long latency = System.nanoTime() - start;
latencies.add(latency);
System.out.printf("TCP connect latency: %.2f ms%n", latency / 1_000_000.0);
}
/**
* 建连:创建 Socket 并建立连接
*/
private void measureTcpConnect() throws Exception {
try (Socket socket = new Socket()) {
socket.connect(new InetSocketAddress(HOST, PORT), CONNECT_TIMEOUT);
}
}
/**
* 测试结束后输出延迟统计,并断言平均延迟低于阈值
*/
@AfterClass
public void reportAndAssert() {
if (latencies.isEmpty()) {
return;
}
double avg = latencies.stream().mapToLong(Long::longValue).average().orElse(0);
long min = latencies.stream().mapToLong(Long::longValue).min().orElse(0);
long max = latencies.stream().mapToLong(Long::longValue).max().orElse(0);
double p99 = percentile(latencies, 99);
System.out.println("\n===== TCP Connect Latency Statistics =====");
System.out.printf("Samples: %d%n", latencies.size());
System.out.printf("Average: %.2f ms%n", avg / 1_000_000.0);
System.out.printf("Min: %.2f ms%n", min / 1_000_000.0);
System.out.printf("Max: %.2f ms%n", max / 1_000_000.0);
System.out.printf("P99: %.2f ms%n", p99 / 1_000_000.0);
// 断言:平均延迟应小于 500 ms(按实际环境调整)
assert avg / 1_000_000.0 < 500 :
"TCP connect average latency too high: " + avg / 1_000_000.0 + " ms";
}
// 简单百分位数计算
private double percentile(List<Long> list, double percentile) {
List<Long> sorted = new ArrayList<>(list);
sorted.sort(Long::compareTo);
int index = (int) Math.ceil(percentile / 100.0 * sorted.size()) - 1;
return sorted.get(Math.max(index, 0));
}
}
说明:
直接右键类名-Run As-TestNG Test。
可通过修改HOST和PORT测试其他服务(如数据库 3306、Redis 6379)。
HTTP请求延迟测试
用java.net.HttpURLConnection发送GET请求,测量从发起请求到读取完响应的总时间(TTLB / TTFB 可按需拆分)。
java
import org.testng.annotations.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
public class HttpLatencyTest {
private static final String REQUEST_URL = "https://www.zmtests.com";
private static final int CONNECT_TIMEOUT = 3000;
private static final int READ_TIMEOUT = 3000;
private final List<Long> latencies = new ArrayList<>();
@Test(invocationCount = 5, groups = "warmup")
public void warmup() throws Exception {
sendGetRequest();
}
@Test(invocationCount = 50, dependsOnGroups = "warmup")
public void httpRequestLatencyTest() throws Exception {
long start = System.nanoTime();
int status = sendGetRequest();
long latency = System.nanoTime() - start;
// 仅记录成功请求(状态码 2xx 或 3xx)
if (status >= 200 && status < 400) {
latencies.add(latency);
System.out.printf("HTTP latency: %.2f ms (status %d)%n", latency / 1_000_000.0, status);
} else {
System.err.printf("Non-success status %d%n", status);
}
}
private int sendGetRequest() throws Exception {
URL url = new URL(REQUEST_URL);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(CONNECT_TIMEOUT);
conn.setReadTimeout(READ_TIMEOUT);
conn.setInstanceFollowRedirects(true); // 跟随重定向
int status = conn.getResponseCode();
// 必须消耗完响应体,否则影响后续连接复用及时间测量
byte[] buffer = new byte[4096];
while (conn.getInputStream().read(buffer) != -1) { /* 读取完所有数据 */ }
conn.disconnect();
return status;
}
@AfterClass
public void reportAndAssert() {
if (latencies.isEmpty()) {
System.err.println("No successful HTTP requests recorded.");
return;
}
double avg = latencies.stream().mapToLong(Long::longValue).average().orElse(0);
long min = latencies.stream().mapToLong(Long::longValue).min().orElse(0);
long max = latencies.stream().mapToLong(Long::longValue).max().orElse(0);
double p99 = percentile(latencies, 99);
System.out.println("\n===== HTTP Request Latency Statistics =====");
System.out.printf("Successful samples: %d%n", latencies.size());
System.out.printf("Average: %.2f ms%n", avg / 1_000_000.0);
System.out.printf("Min: %.2f ms%n", min / 1_000_000.0);
System.out.printf("Max: %.2f ms%n", max / 1_000_000.0);
System.out.printf("P99: %.2f ms%n", p99 / 1_000_000.0);
// 断言:P99 延迟不得超过 2 秒
assert p99 / 1_000_000.0 < 2000 :
"HTTP P99 latency too high: " + p99 / 1_000_000.0 + " ms";
}
private double percentile(List<Long> list, double percentile) {
List<Long> sorted = new ArrayList<>(list);
sorted.sort(Long::compareTo);
int index = (int) Math.ceil(percentile / 100.0 * sorted.size()) - 1;
return sorted.get(Math.max(index, 0));
}
}
说明:
预热:前几次测试可能受 JIT 编译、DNS 缓存等原因影响,先跑 3‑5 次不记录数据,结果更稳定。
纳秒计时:System.nanoTime() 精度高,适合测量毫秒级甚至微秒级延迟。
超时设置:避免因网络不通导致测试线程永久阻塞。
TestNG 的 invocationCount:轻松控制重复执行次数,无需手写循环。
统计数据和断言:在 @AfterClass 中计算平均值、P99,并利用assert自动决定延迟是不是达标(可集成到 CI 流水线)。
运行方式
IDE 中:右键测试类 - Run as TestNG。
命令行:通过 TestNG 的 testng.xml 批量执行,或集成到 Maven/Gradle。
自定义目的:可通过 System.getProperty("target.host", "default") 动态传入被测地址。
这两个脚本可以直接复制到项目中使用,只需修改 HOST、PORT 或 REQUEST_URL 即可开始测量。如果需要测试其他协议(如 MQTT、gRPC),只需替换中间的网络操作代码,TestNG 的测试架构完全不变。