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

