如何在 HttpResponse.BodyHandler 中使用 InputStream?

How to consume an InputStream in an HttpResponse.BodyHandler?

我正在尝试为 Java 11 HttpClient 构建一个 JSON 处理程序。 我遇到的问题是,尝试与 BodySubscribers.ofInputStream() 组合会导致永远不会从流中读取任何数据,并永远挂起。

@Override
public HttpResponse.BodySubscriber<Void> apply(HttpResponse.ResponseInfo responseInfo) {
    return HttpResponse.BodySubscribers.mapping(
            HttpResponse.BodySubscribers.ofInputStream(),
            inputStream -> {
                try {
                    System.out.print(inputStream.read());
                } catch (IOException e) {
                    throw new UncheckedIOException(e);
                }
                return null;
            }
    );
}

相比之下,返回流并在之后读取它是可行的。

InputStream inputStream = client.send(request, HttpResponse.BodyHandlers.ofInputStream()).body();
System.out.print(inputStream.read());  // out: 123

我错过了什么?

这里的问题在于在不应阻塞的线程中调用阻塞操作。实际上 - 有两个问题:

  1. Java11API 文档提示您可以这样做 - 这确实不是一个好主意。
  2. 实现未准备好处理映射器函数中的阻塞操作。

JDK-8217264: HttpClient: Blocking operations in mapper function do not work as documented

在 13 中修复了这两个问题

如果您查看 more recent version of the API documentation,您会发现它现已更新以涵盖此案例。 尽管从 13 开始这样做将不再楔入请求,但仍然不鼓励阻塞映射器的函数,因为它会阻塞 HttpClient 的执行线程之一,直到响应被完全接收,这可能导致线程饥饿。

为了补充我的回答 - 您应该考虑遵循较新的 API 文档和 return 的建议 Supplier<JSONObject> 而不是 JSONObject,这将允许延迟流的读取(和阻塞操作),直到 Supplier::get 被调用者调用。这使您可以灵活地选择哪个线程将阻塞等待响应。