简体   繁体   English

如何将异步HTTP请求抽象为同步

[英]How to abstract asynchronous HTTP requests as synchronous

I have a third party REST API service that behaves 'asynchronously'; 我有一个第三方REST API服务,行为'异步'; as in requests respond with intermediate responses that are supplemented by callbacks with a correlator to the intermediate response. 在请求中响应中间响应,这些响应由具有中间响应的相关器的回调补充。

The callbacks are returned almost immediately via a 'callback URL'. 回调几乎立即通过“回调URL”返回。

I am trying to devise a solution, sort of an adapter to call this resource as though it was 'synchronous' because it is cumbersome to deal with the callbacks, especially when I need to batch successive requests serially to other similar APIs by the same thirdparty. 我试图设计一个解决方案,一种适配器来调用这个资源,好像它是'同步',因为处理回调是很麻烦的,特别是当我需要通过相同的第三方将连续请求串行批处理到其他类似的API时。 Basically I want to abstract the green part so that the caller only gets full callback, an error or timeout exception. 基本上我想抽象绿色部分,以便调用者只获得完整的回调,错误或超时异常。 在此输入图像描述

My research points at using RxJava but I can't figure out how this problem can be solved by principles of reactive programming(my understanding is limited). 我的研究指向使用RxJava,但我无法弄清楚这个问题如何通过反应式编程的原理来解决(我的理解是有限的)。

Design considerations: 设计注意事项:

  1. Persisting correlator to a datastore for later lookup upon callback is not desirable because it is expensive 将相关器保持到数据存储区以便稍后在回调时查找是不可取的,因为它很昂贵
  2. Wait strategy for the callback is OK because the response times for the callback is less than 1 sec 回调的等待策略是正常的,因为回调的响应时间小于1秒

How can I employ a CompletableFuture , or Observable-Observer pattern to wait for the callback and return to the caller? 如何使用CompletableFutureObservable-Observer模式等待回调并返回调用者?

Consider using a CountDownLatch for the main thread to wait until a worker thread has dealt with the third party API. 考虑使用CountDownLatch作为主线程,等待工作线程处理第三方API。 The worker gets the callbacks so it knows when the request has made progress, is complete, timed out, had an error, etc. 工作人员获取回调,以便它知道请求何时进展,完成,超时,出错等。

Here's a rough simulation: 这是一个粗略的模拟:

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadLocalRandom;

public class TreatAsSync {

    public static void main(String[] args) {
        TreatAsSync app = new TreatAsSync();
        app.call();
    }

    private void call() {
        RestClient restClient = new RestClient();
        Request request = new Request();
        Response response = restClient.call(request);
        System.out.println("Response was: " + response);
    }

    private class Request {

    }

    private class Response {
        private final boolean error;
        private final boolean timedOut;
        private final String result;

        public Response(boolean error, boolean timedOut, String result) {
            this.error = error;
            this.timedOut = timedOut;
            this.result = result;
        }

        public String toString() {
            return "error:" + error + ", timedOut: " + timedOut + ", result: " + result;
        }
    }

    private class ResponseWrapper {
        private Response response;

        public Response getResponse() {
            return response;
        }

        public void setRespose(Response response) {
            this.response = response;
        }
    }

    private class RestClient {

        public Response call(Request request) {
            ResponseWrapper wrapper = new ResponseWrapper();
            CountDownLatch latch = new CountDownLatch(1);

            ThirdPartyRunner runner = new ThirdPartyRunner(request, wrapper, latch);
            new Thread(runner).start();

            try {
                latch.await();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            return wrapper.getResponse();
        }
    }

    private interface ThirdPartyCallBack {
        public void onProgress(Response response);
        public void onComplete(Response response);
        public void onTimeOut(Response response);
        public void onError(Response response);
    }

    private class ThirdPartyRunner implements ThirdPartyCallBack, Runnable {
        private final Request request;
        private final ResponseWrapper wrapper;
        private final CountDownLatch latch;

        public ThirdPartyRunner(Request request, ResponseWrapper wrapper, CountDownLatch latch) {
            this.request = request;
            this.wrapper = wrapper;
            this.latch = latch;
        }

        @Override
        public void onProgress(Response response) {
            System.out.println("some progress was made...");
        }

        @Override
        public void onComplete(Response response) {
            System.out.println("request completed");
            finished(response);
        }

        @Override
        public void onTimeOut(Response response) {
            System.out.println("request timed out");
            finished(response);
        }

        @Override
        public void onError(Response response) {
            System.out.println("request had an error");
            finished(response);
        }

        private void finished(Response response) {
            wrapper.setRespose(response);
            latch.countDown();
        }

        @Override
        public void run() {
            try {
                callThirdParty();
            } catch (Exception e) {
                finished(new Response(true, false, e.getMessage()));
            }
        }

        private void callThirdParty() {
            // simulate api.call(request, this);
            for (int i = 0; i < ThreadLocalRandom.current().nextInt(10) + 1; i++) {
                onProgress(new Response(false, false, "in progress"));
            }

            switch (ThreadLocalRandom.current().nextInt(3)) {
            case 0:
                onComplete(new Response(false, false, "done"));
                break;

            case 1:
                onTimeOut(new Response(false, true, "hello?"));
                break;

            case 2:
                onError(new Response(true, false, "uh oh!"));
                break;
            }
        }
    }

}

If you have this: 如果你有这个:

public ImmediateResponse asynchCall(Callback callback) throws ImmediateException {...}

Where Callback looks like this: Callback看起来像这样:

interface Callback {
    void onSuccess(EventualResponse eventualResponse);
    void onFailure(Exception e);
}

Then what you want is something like this: 然后你想要的是这样的:

public static EventualResponse synchCall(long timeout, TimeUnit timeUnit)
        throws InterruptedException, TimeoutException, EventualException
{
    CompletableFuture<EventualResponse> responseFuture = new CompletableFuture<>();
    Callback callback = new Callback() {
        public void onSuccess(EventualResponse response) { 
            responseFuture.complete(response); 
        }
        public void onFailure(Exception e) { 
            responseFuture.completeExceptionally(e); 
        }
    };
    try {
        /*ImmediateResponse immediateResponse = */asynchCall(callback); 
        // use immediateResponse if you need it
        return responseFuture.get(timeout, timeUnit);
    } catch (ImmediateException e) {
        throw new EventualException(e);
    } catch (ExecutionException ee) {
        throw new EventualException(ee.getCause());
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM