Поток асинхронных ответов с помощью Apache Async Http client - PullRequest
0 голосов
/ 11 мая 2018

Я использую асинхронный HTTP-клиент Apache для потоковой передачи объектов из хранилища Azure.

Мне нужно только вернуть объект HttpResponse, с которым связан связанный поток. Моим клиентам на самом деле придется читать из этого потока, чтобы сохранить файл локально.

Таким образом, клиенты Apache Async используют BasicAsyncResponseConsumer, который фактически буферизует весь файл в локальной памяти перед вызовом завершенного обратного вызова.

Я пытаюсь создать свою собственную реализацию AbstractAsyncResponseConsumer, чтобы я мог передавать тело ответа вместо фактического его сохранения вначале, но до сих пор это не удавалось.

Вот класс потребителей для справки ->

public class MyConsumer extends` AbstractAsyncResponseConsumer<HttpResponse> {
@Override
protected void onResponseReceived(HttpResponse response) throws HttpException, IOException {

}

@Override
protected void onContentReceived(ContentDecoder decoder, IOControl ioctrl) throws IOException {

}

@Override
protected void onEntityEnclosed(HttpEntity entity, ContentType contentType) throws IOException {

}

@Override
protected HttpResponse buildResult(HttpContext context) throws Exception {
    return null;
}

@Override
protected void releaseResources() {

}

}

А вот код для отправки запроса и возврата ответа ->

public void getFile(HttpRequestBase request) {

    MyConsumer myConsumer = new MyConsumer();
    HttpAsyncRequestProducer producer = 
    HttpAsyncMethods.create(request);
    CompletableFuture<HttpResponse> future = new CompletableFuture<>();
    return Future<HttpResponse> responseFuture = 
    httpclient.execute(producer,myConsumer,                                                                                                                   
    new FutureCallback<HttpResponse>() {
      @Override
      public void completed(HttpResponse result) {
     //This is called only when all the response body has been read
     //future.complete(Result)

      }
      @Override                                                                      
      public void failed(Exception ex) {
      }
      @Override
      public void cancelled() {                                                                       
      }
   });

return future;

 }

Я буду возвращать CompletableFuture объекта HttpResponse своим клиентам.

Они не должны ждать, пока мой http-клиент сначала прочитает все тело ответа в локальном буфере.

В идеале они должны начинать копирование непосредственно из потока, предоставленного в объекте ответа.

Что мне добавить в мою реализацию потребителя, чтобы получить желаемый результат?

1 Ответ

0 голосов
/ 09 января 2019

Я не знаю, если у вас все еще есть эта проблема, но если вам нужен InputStream, который фактически передает данные, тогда вы захотите использовать блокирующую версию Apache HttpClient.

Встроенные в Java InputStream и OutputStream по сути являются блокирующими, поэтому возвращение CompletableFuture из InputStream по существу противоречит цели.BasicAsyncResponseConsumer буферизация всего ответа в памяти - это действительно правильная вещь, потому что это единственный способ сделать его по-настоящему неблокирующим.

Другой вариант, на который вы можете взглянуть, - HttpAsyncMethods.createZeroCopyConsumer.Что он делает, так это то, что он сохраняет содержимое в файл совершенно неблокирующим образом.Вот пример:

        try (CloseableHttpAsyncClient client = HttpAsyncClients.createDefault()) {
            client.start();
            final CompletableFuture<HttpResponse> cf = new CompletableFuture<>();
            client.execute(
                    HttpAsyncMethods.createGet("https://example.com"),
                    HttpAsyncMethods.createZeroCopyConsumer(new File("foo.html")),
                    new FutureCallback<HttpResponse>() {
                        @Override
                        public void completed(HttpResponse result) {
                            cf.complete(result);
                        }
                        @Override
                        public void failed(Exception ex) {
                            cf.completeExceptionally(ex);
                        }
                        @Override
                        public void cancelled() {
                            cf.cancel(true);
                        }
                    });
            // When cf completes, the file will be ready.
            // The InputStream inside the HttpResponse will be the FileInputStream of the created file.
        }
...