JAVA에서 비동기 HTTP 요청을 어떻게 생성합니까?


81

저는 Java를 처음 접하기 때문에 일부 사람들에게는 이것이 분명해 보일 수 있습니다. 저는 이벤트 기반의 액션 스크립트로 많은 작업을 해왔고 그것을 좋아합니다. 최근에 POST 요청을 수행하는 약간의 Java 코드를 작성하려고 시도했지만 동기 요청이라는 문제에 직면하여 코드 실행이 요청이 완료되거나 시간이 초과되거나 오류가 표시 될 때까지 대기합니다.

코드가 실행을 계속하고 HTTP 요청이 완료되면 콜백이 호출되는 비동기 요청을 생성하려면 어떻게해야합니까? 스레드를 훑어 봤지만 과잉이라고 생각합니다.


답변:


11

java11은 이제 java의 CompletableFuture를 사용하여 완전히 비동기식 작업을 지원 하는 새로운 HTTP api HttpClient 를 제공합니다 .

또한 동기식 인 send 및 비동기식 sendAsync 와 같은 호출을 사용하여 동기식 버전을 지원합니다 .

비동기 요청의 예 (apidoc에서 가져옴) :

   HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://example.com/"))
        .timeout(Duration.ofMinutes(2))
        .header("Content-Type", "application/json")
        .POST(BodyPublishers.ofFile(Paths.get("file.json")))
        .build();
   client.sendAsync(request, BodyHandlers.ofString())
        .thenApply(HttpResponse::body)
        .thenAccept(System.out::println);

1
java8을 사용하면 어떤 API가 가장 좋습니까?
alen

@alen 모르겠어요. 곧 모두가 java11을 사용할 수 있기를 바랍니다.
Emmanuel Touzery

31

JEE7 환경에있는 경우 클라이언트 API를 사용하여 쉽게 비동기 HTTP 요청을 만들 수있는 적절한 JAXRS 구현이 있어야합니다.

이것은 다음과 같습니다.

public class Main {

    public static Future<Response> getAsyncHttp(final String url) {
        return ClientBuilder.newClient().target(url).request().async().get();
    }

    public static void main(String ...args) throws InterruptedException, ExecutionException {
        Future<Response> response = getAsyncHttp("http://www.nofrag.com");
        while (!response.isDone()) {
            System.out.println("Still waiting...");
            Thread.sleep(10);
        }
        System.out.println(response.get().readEntity(String.class));
    }
}

물론 이것은 단지 선물을 사용하는 것입니다. 더 많은 라이브러리를 사용해도 괜찮다면 RxJava를 살펴보면 코드는 다음과 같습니다.

public static void main(String... args) {
    final String url = "http://www.nofrag.com";
    rx.Observable.from(ClientBuilder.newClient().target(url).request().async().get(String.class), Schedulers
            .newThread())
            .subscribe(
                    next -> System.out.println(next),
                    error -> System.err.println(error),
                    () -> System.out.println("Stream ended.")
            );
    System.out.println("Async proof");
}

그리고 마지막으로, 비동기 호출을 재사용하려면 Hystrix를 살펴볼 수 있습니다. Hystrix는 엄청나게 멋진 다른 것 외에도 다음과 같이 작성할 수 있습니다.

예를 들면 :

public class AsyncGetCommand extends HystrixCommand<String> {

    private final String url;

    public AsyncGetCommand(final String url) {
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("HTTP"))
                .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                        .withExecutionIsolationThreadTimeoutInMilliseconds(5000)));
        this.url = url;
    }

    @Override
    protected String run() throws Exception {
        return ClientBuilder.newClient().target(url).request().get(String.class);
    }

 }

이 명령을 호출하면 다음과 같습니다.

public static void main(String ...args) {
    new AsyncGetCommand("http://www.nofrag.com").observe().subscribe(
            next -> System.out.println(next),
            error -> System.err.println(error),
            () -> System.out.println("Stream ended.")
    );
    System.out.println("Async proof");
}

추신 : 스레드가 오래되었다는 것을 알고 있지만 아무도 찬성 응답에서 Rx / Hystrix 방식을 언급하지 않는다는 것이 잘못되었다고 느꼈습니다.


프록시와 함께 어떻게 사용할 수 있습니까?
Dejell

이 답변, 특히 RxJava 예제에 대해 자세히 설명하면 좋을 것입니다. newThread ()에 대한 메서드 호출이 있습니다.이 코드가 새 스레드를 회전한다는 것을 의미하는 것 같습니다. 나는, 수신의 비동기 기능은 일정한와 막연하게 잘 알고 놀라움 이런 종류의 나 ... 그래서
앤더스 마티니

Scheduler.newThread () 호출은 단순히 Rx에게이 경우 새 스레드에서 실행을 회전하도록 지시합니다.이 경우 비동기 컴퓨팅이 적용됩니다. 물론, 이미 어떤 종류의 비동기 설정이있는 경우에는 대신 쉽게 사용할 수 있습니다 (Scheduler.from (Executor)가 떠 오릅니다).
Psyx

1
@Gank 예, 람다를 사용하기 때문에 1.8 이상 컴파일 할 수 없습니다. 그것은 ... 쉽게 충분히에게 등 소비자를 사용하여 먼 길을 작성해야한다
Psyx

@psyx 옵저버 블 구독을 취소해야하나요?
Nick Gallimore


14

이 SO 스레드의 Apache HTTP 구성 요소 에 대한 링크를 기반으로 HTTP 구성 요소 에 대한 Fluent 파사드 API를 발견했습니다. 여기에있는 예 는 비동기 HTTP 요청의 대기열을 설정하는 방법을 보여줍니다 (그리고 완료 / 실패 / 취소에 대한 알림 받기). 제 경우에는 대기열이 필요하지 않고 한 번에 하나의 비동기 요청 만 필요했습니다.

여기에 내가 끝낸 곳이 있습니다 (또한 HTTP 구성 요소의 URIBuilder 사용, 여기 예 ).

import java.net.URI;
import java.net.URISyntaxException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.apache.http.client.fluent.Async;
import org.apache.http.client.fluent.Content;
import org.apache.http.client.fluent.Request;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.concurrent.FutureCallback;

//...

URIBuilder builder = new URIBuilder();
builder.setScheme("http").setHost("myhost.com").setPath("/folder")
    .setParameter("query0", "val0")
    .setParameter("query1", "val1")
    ...;
URI requestURL = null;
try {
    requestURL = builder.build();
} catch (URISyntaxException use) {}

ExecutorService threadpool = Executors.newFixedThreadPool(2);
Async async = Async.newInstance().use(threadpool);
final Request request = Request.Get(requestURL);

Future<Content> future = async.execute(request, new FutureCallback<Content>() {
    public void failed (final Exception e) {
        System.out.println(e.getMessage() +": "+ request);
    }
    public void completed (final Content content) {
        System.out.println("Request completed: "+ request);
        System.out.println("Response:\n"+ content.asString());
    }

    public void cancelled () {}
});

6

이 질문을 살펴볼 수 있습니다. Java의 비동기 IO?

스레드를 직접 얽히고 싶지 않다면 가장 좋은 방법은 프레임 워크입니다. 이전 게시물에서는 Grizzly, https://grizzly.dev.java.net/ 및 Netty, http://www.jboss.org/netty/를 언급했습니다 .

netty 문서에서 :

Netty 프로젝트는 유지 보수 가능한 고성능 및 높은 확장 성 프로토콜 서버 및 클라이언트의 신속한 개발을위한 비동기 이벤트 중심 네트워크 애플리케이션 프레임 워크 및 도구를 제공하기위한 노력입니다.


2

Apache HttpComponents 에는 이제 비동기 http 클라이언트도 있습니다.

/**
    <dependency>
      <groupId>org.apache.httpcomponents</groupId>
      <artifactId>httpasyncclient</artifactId>
      <version>4.0-beta4</version>
    </dependency>
**/

import java.io.IOException;
import java.nio.CharBuffer;
import java.util.concurrent.Future;

import org.apache.http.HttpResponse;
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.protocol.HttpContext;

public class HttpTest {

  public static void main(final String[] args) throws Exception {

    final CloseableHttpAsyncClient httpclient = HttpAsyncClients
        .createDefault();
    httpclient.start();
    try {
      final Future<Boolean> future = httpclient.execute(
          HttpAsyncMethods.createGet("http://www.google.com/"),
          new MyResponseConsumer(), null);
      final Boolean result = future.get();
      if (result != null && result.booleanValue()) {
        System.out.println("Request successfully executed");
      } else {
        System.out.println("Request failed");
      }
      System.out.println("Shutting down");
    } finally {
      httpclient.close();
    }
    System.out.println("Done");
  }

  static class MyResponseConsumer extends AsyncCharConsumer<Boolean> {

    @Override
    protected void onResponseReceived(final HttpResponse response) {
    }

    @Override
    protected void onCharReceived(final CharBuffer buf, final IOControl ioctrl)
        throws IOException {
      while (buf.hasRemaining()) {
        System.out.print(buf.get());
      }
    }

    @Override
    protected void releaseResources() {
    }

    @Override
    protected Boolean buildResult(final HttpContext context) {
      return Boolean.TRUE;
    }
  }
}

프록시와 함께 어떻게 사용할 수 있습니까?
Dejell

@Dejel 여기에 지정된대로 시스템 속성을 설정했다고 가정합니다. docs.oracle.com/javase/6/docs/technotes/guides/net/proxies.html
Dan Brough

1
future.get ()을 호출하면 스레드가 차단됩니다. 실제로 비동기 상태가되도록 다른 스레드에 배치해야합니다. HttpAsyncClients 라이브러리의 이름이 잘못되었습니다 ...
jjbskir

1

HTTP 프로토콜이 동 기적이며 프로그래밍 언어와 관련이 없음을 분명히해야합니다. 클라이언트가 요청을 보내고 동기 응답을받습니다.

당신은 HTTP를 통해 비동기 행동을하려는 경우,이 구축되어야 이상 (나는 액션에 대해 아무것도 몰라하지만 난이 ActionScript를 너무 무엇이라고 생각) HTTP를. 이러한 기능을 제공 할 수있는 많은 라이브러리가 있습니다 (예 : Jersey SSE ). HTTP 위의 정확한 비표준 통신 방법에 동의해야하기 때문에 클라이언트와 서버 간의 종속성을 어떻게 든 정의합니다.

당신은 클라이언트와 서버를 모두 제어 할 수 또는 당신이 그들 사이의 종속성을 갖고 싶어하지 않을 경우, 사용하고 HTTP를 통해 비동기 (예 : 이벤트 기반) 통신을 구현하는 가장 일반적인 방법 경우 webhooks이 접근은 (확인할 수 있습니다 에 대한 자바에서 구현 예).

내가 도왔 으면 좋겠어!


기술적으로는 사실이지만,이 대답은 오해의 소지가 있습니다. 서버 또는 HTTP 프로토콜이 지원하는 것과 관계없이 클라이언트 구현은 요청을 동일한 스레드, 다른 스레드에서 차단 방식으로 실행하는지 여부에 따라 매우 중요한 성능 영향을 미칠 수 있습니다. 스레드 풀을 사용하거나 응답이 도착할 때 OS가 깨울 때까지 호출 스레드가 휴면하는 NIO (Non-blocking IO)를 사용하는 것이 이상적입니다. OP가 프로토콜보다는 클라이언트 스레딩 모델에 관심이있는 것 같습니다.
geg

0

다음은 apache HttpClient를 사용 하고 별도의 스레드에서 호출 하는 솔루션 입니다. 이 솔루션은 하나의 비동기 호출 만하는 경우에 유용합니다. 여러 번 호출하는 경우 apache HttpAsyncClient를 사용 하고 스레드 풀에 호출을 배치하는 것이 좋습니다 .

import java.lang.Thread;

import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;

public class ApacheHttpClientExample {
    public static void main(final String[] args) throws Exception {
        try (final CloseableHttpClient httpclient = HttpClients.createDefault()) {
            final HttpGet httpget = new HttpGet("http://httpbin.org/get");
            new Thread(() -> {
                 final String responseBody = httpclient.execute(httpget);
            }).start();
        }
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.