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

Jetty 9.4.8 HTTP客户端InputStreamResponseListener复制阻塞问题排查

我来帮你分析下这个阻塞问题,我之前在使用Jetty客户端时也遇到过类似的情况,咱们一步步来拆解原因和解决办法。

问题根源

你遇到的阻塞,核心原因是InputStreamResponseListener返回的InputStream在读完Content-Length指定的所有字节后,并没有正确返回-1(标准流结束标记),而是处于等待更多数据的挂起状态。而Apache Commons IO的IOUtils.copy()方法会一直尝试读取直到流返回-1,这就导致程序卡在复制步骤无法继续。

解决方案

这里提供两种可行的解决思路,你可以根据自己的代码场景选择:

方案1:用Jetty异步监听器直接处理内容(推荐)

Jetty HTTP客户端本身是异步优先的设计,直接利用Response.Listener的回调方法逐块写入文件,完全贴合其异步模型,从根源上避免流阻塞问题:

Request request = ...; // 你的请求对象
File tmpFile = TMP_FILE.toFile();

request.send(new Response.Listener.Adapter() {
    private FileOutputStream outputStream;

    @Override
    public void onBegin(Response response) throws IOException {
        if (response.getStatus() == 200) {
            tmpFile.createNewFile();
            outputStream = new FileOutputStream(tmpFile);
        }
    }

    @Override
    public void onContent(Response response, ByteBuffer content) throws IOException {
        if (outputStream != null) {
            byte[] bytes = new byte[content.remaining()];
            content.get(bytes);
            outputStream.write(bytes);
        }
    }

    @Override
    public void onComplete(Result result) {
        IOUtils.closeQuietly(outputStream);
        if (!result.isSucceeded()) {
            getLogService().log("请求处理失败: " + result.getFailure().getMessage());
            // 可选:清理未完成的临时文件
            tmpFile.delete();
        }
    }
});

这种方式会在响应开始时初始化输出流,每收到一块数据就写入文件,响应完成后自动关闭流,完全适配Jetty的异步机制,不会出现阻塞。

方案2:限定读取字节数(如果坚持用InputStreamResponseListener)

既然响应头里明确给出了Content-Length,你可以用IOUtils.copyLarge()指定读取的总字节数,读到指定长度后就停止,不会无限等待流结束:

InputStreamResponseListener streamResponseListener = new InputStreamResponseListener();
request.send(streamResponseListener);

Response response = streamResponseListener.get(5, TimeUnit.MINUTES);
if (response.getStatus() == 200) {
    long contentLength = response.getHeaders().getLongField(HttpHeader.CONTENT_LENGTH);
    // 使用try-with-resources自动关闭流,比手动关闭更安全
    try (InputStream inputStream = streamResponseListener.getInputStream();
         OutputStream outputStream = new FileOutputStream(TMP_FILE.toFile())) {
        TMP_FILE.toFile().createNewFile();
        // 只读取Content-Length指定的字节数
        IOUtils.copyLarge(inputStream, outputStream, 0, contentLength);
    } catch (IOException e) {
        getLogService().log("文件写入失败: " + e.getMessage());
    }
}

额外注意事项

  • 记得给Jetty请求设置合理的超时时间,比如request.timeout(10, TimeUnit.MINUTES),避免极端情况下的无响应等待。
  • 可以额外校验服务器返回的实际字节数是否和Content-Length一致,确保数据完整性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:50:57