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

Jetty Client 11如何实现单请求维度的收发字节数统计

Jetty Client 11 单请求字节统计(含请求/响应头)实现方案

之前方案的问题定位

  • ConnectionStatistics方案失效原因:
    • 它是连接级累计统计组件,统计的是整个TCP连接从建立到关闭的所有字节(含所有复用该连接的请求),而非单请求维度。
    • 你在request.send()后立即取统计值,此时请求刚发起,连接的字节传输事件尚未触发;且keep-alive场景下请求结束后连接不关闭,统计值不会更新,因此始终返回0。
  • onRequestContent方案GET请求统计为0的原因:
    • 该钩子仅在请求有内容体时触发,GET请求无请求体,因此永远不会触发。
    • 即使有请求体,该钩子也只统计请求体片段,不包含请求行、请求头的字节量。

正确实现方案(适配HTTP/2 GET请求)

核心逻辑:监听请求的所有报文序列化/反序列化事件(含头、体),而非仅监听内容体事件,用线程安全容器累加单请求的字节数。

// 单请求字节统计容器(线程安全)
class RequestByteCounter {
    final AtomicLong sentBytes = new AtomicLong(0);
    final AtomicLong receivedBytes = new AtomicLong(0);
}

// 初始化HTTP/2 HttpClient(按Jetty 11规范配置,此处略去启动逻辑)
HttpClient httpClient = new HttpClient(new HttpClientTransportOverHTTP2());
httpClient.start();

// 发起单请求并绑定统计
RequestByteCounter counter = new RequestByteCounter();
httpClient.newRequest("https://target-uri")
    .version(HttpVersion.HTTP_2)
    // 1. 统计请求行+请求头的发送字节(HTTP/2为HPACK压缩后的载荷,用Jetty内置生成器计算)
    .onRequestHeaders(req -> {
        HttpGenerator generator = new HttpGenerator();
        ByteBuffer headerBuf = ByteBuffer.allocate(2048);
        generator.generateRequest(
            req.getMethod(),
            req.getRequestURI(),
            req.getVersion(),
            req.getHeaders(),
            null, // 无Trailers
            0, // 无请求体(GET场景)
            headerBuf
        );
        counter.sentBytes.addAndGet(generator.getPreparedBytes());
    })
    // 2. 统计请求体发送字节(GET请求无此步,仅POST/PUT等有体请求触发)
    .onRequestContent((req, buf) -> counter.sentBytes.addAndGet(buf.remaining()))
    // 3. 统计响应状态行+响应头的接收字节
    .onResponseHeaders(res -> {
        // 计算HTTP/2伪头(:status)+ 普通响应头的字节数
        int headerSize = 0;
        headerSize += (":status: " + res.getStatus() + "\r\n").getBytes(StandardCharsets.UTF_8).length;
        for (HttpField field : res.getHeaders()) {
            headerSize += (field.getName() + ": " + field.getValue() + "\r\n").getBytes(StandardCharsets.UTF_8).length;
        }
        headerSize += 2; // 头终止符\r\n
        counter.receivedBytes.addAndGet(headerSize);
    })
    // 4. 统计响应体接收字节(你之前的已验证方案)
    .onResponseContent((res, buf) -> counter.receivedBytes.addAndGet(buf.remaining()))
    // 请求完成后输出统计值
    .send(new BufferingResponseListener() {
        @Override
        public void onComplete(Result result) {
            System.out.printf("单请求发送字节: %d, 接收字节: %d%n",
                counter.sentBytes.get(), counter.receivedBytes.get());
        }
    });

补充说明

  • 若需要实际网络传输的HPACK压缩后字节数(而非应用层未压缩字节),需绑定当前Stream的HPACK.Decoder/Encoder实例,监听其输出字节数。
  • 若使用HTTP/1.1,可简化为请求前后连接级统计差值(因HTTP/1.1连接串行,无并发复用干扰)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.30 22:36:03