直到数据完成才返回响应

Response not returned until data is complete

我在使用 Apache HttpClient4 通过 POST 操作读取大型响应流时遇到问题。正在发送响应,但长度未知。 当我用 curl 使用它时,它会立即开始使用,但是 HttpClient 它会等到收到整个响应后才开始使用。

对于 curl,activity 看起来像这样,输出几乎立即开始写入:

$ curl -v -X POST --data-binary @input.gz http://chemservices:8080/chem-services-cdk-basic/rest/v1/converters/dataset_to_sdf  -H 'Content-Type: application/x-squonk-dataset-molecule+json' -H 'Content-Encoding: gzip' -H 'Accept: chemical/x-mdl-sdfile' -o output.sdf -v
Note: Unnecessary use of -X or --request, POST is already inferred.
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0*   Trying 172.18.0.6...
* TCP_NODELAY set
* Connected to chemservices (172.18.0.6) port 8080 (#0)
> POST /chem-services-cdk-basic/rest/v1/converters/dataset_to_sdf HTTP/1.1
> Host: chemservices:8080
> User-Agent: curl/7.58.0
> Content-Type: application/x-squonk-dataset-molecule+json
> Content-Encoding: gzip
> Accept: chemical/x-mdl-sdfile
> Content-Length: 19397182
> Expect: 100-continue
> 
< HTTP/1.1 100 Continue
} [16384 bytes data]
* We are completely uploaded and fine
< HTTP/1.1 200 OK
< Server: Apache-Coyote/1.1
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Methods: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, CONNECT, PATCH
< Access-Control-Max-Age: 3600
< Access-Control-Allow-Headers: Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers
< Content-Type: chemical/x-mdl-sdfile
< Transfer-Encoding: chunked
< Date: Sun, 13 Jan 2019 12:30:18 GMT
< 
{ [1031 bytes data]
100  172M    0  154M  100 18.4M  7103k   851k  0:00:22  0:00:22 --:--:-- 7301k
* Connection #0 to host chemservices left intact

但是当我使用 HttpClient 时,execute() 操作会阻塞,直到整个响应被写入:

LOG.info("Posting commencing");
CloseableHttpResponse resp = httpclient.execute(httpPost);
LOG.info("Posting complete");

正在根据请求设置 Content-TypeContent-EncodingAcceptAccept-Encoding headers。

如果我将 SocketTimeout 参数设置为足够大的值,我可以获得响应,但显然这不是正确的解决方案!

关于如何正确处理这个问题有什么建议吗?

我相信您将需要使用HttpAsyncClient来实现异步请求处理。

考虑以下示例,它将以块的形式流式传输响应,您可以在覆盖的 onCharReceived 方法中处理这些块。为了这个例子的目的,我只是让它打印每个块的长度。

显然您需要修改端点,headers 并根据需要请求数据:

import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.nio.IOControl;
import org.apache.http.nio.client.methods.AsyncCharConsumer;
import org.apache.http.nio.client.methods.HttpAsyncMethods;
import org.apache.http.nio.protocol.HttpAsyncRequestProducer;
import org.apache.http.protocol.HttpContext;

import java.nio.CharBuffer;
import java.util.concurrent.CountDownLatch;

public class HttpAsyncTest {

    public static void main(String[] args) {
        try (CloseableHttpAsyncClient httpclient = HttpAsyncClients.createDefault()) {
            httpclient.start();

            final CountDownLatch latch = new CountDownLatch(1);
            final HttpPost request = new HttpPost("https://postman-echo.com/post");
            request.setEntity(new StringEntity("This is the request data"));
            request.setHeader("Content-Type", "application/x-www-form-urlencoded");

            HttpAsyncRequestProducer producer = HttpAsyncMethods.create(request);
            AsyncCharConsumer<HttpResponse> consumer = new AsyncCharConsumer<HttpResponse>() {
                HttpResponse response;

                @Override
                protected void onResponseReceived(final HttpResponse response) {
                    this.response = response;
                }

                @Override
                protected void onCharReceived(final CharBuffer buf, final IOControl ioctrl) {
                    System.out.printf("onCharReceived: %d\n", buf.length());
                }

                @Override
                protected HttpResponse buildResult(final HttpContext context) {
                    return this.response;
                }
            };

            httpclient.execute(producer, consumer, new FutureCallback<HttpResponse>() {
                public void completed(final HttpResponse response3) {
                    latch.countDown();
                    System.out.println(request.getRequestLine() + "->" + response3.getStatusLine());
                }

                public void failed(final Exception ex) {
                    latch.countDown();
                    System.out.println(request.getRequestLine() + "->" + ex);
                }

                public void cancelled() {
                    latch.countDown();
                    System.out.println(request.getRequestLine() + " cancelled");
                }
            });

            latch.await();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}