如何在Java中以同步方式处理异步回调?

How to handle asynchronous callbacks in a synchronous way in Java?

我有一个与架构相关的问题。这是一个与语言无关的问题,但由于我来自 Java 背景,如果有人以 Java 的方式指导我,对我来说会更容易。

基本上,我正在编写的中间件与基于 SOAP 的第三方服务进行通信。这些调用是异步的 - 在某种程度上,当调用服务时,它 return 带有响应 01 - 处理;意味着第三方已成功收到请求。原来的SOAP请求,每次都要提交一个回调URL,第三方实际发送结果。因此,调用特定服务实际上不会立即 return 结果;结果在中间件的单独 HTTP 端点中接收。

现在在我们的前端,我们不想让用户体验复杂化。我们希望我们的用户调用一个中间件函数(通过菜单 items/buttons),并立即得到结果;把脏活留给中间件。

请注意,从前端调用的中间件函数(假设为 X())和第三方推送结果的中间件端点 URL(假设为 Y)完全独立于彼此。 X() 不知何故必须等待,然后获取 Y 中抓取的结果,然后 return 将结果发送到前端。

如何构建可靠的解决方案来实现上述行为? 图片完美地描述了我的情况。任何建议将不胜感激。

那么这样做到底有什么问题呢?您只需创建一个 API(中间件),它不会 return 响应,直到第三方 return 处理结果。前端向 X() 发送请求,X() 通过向 Y() 发送请求来处理该请求,然后不断轮询 Y() 以查看结果何时准备就绪,然后 X() 从 Y() 获取结果并将其发送回前端。就像一个立面。

关于使用第三方服务存在一些您无法控制的问题,您应该考虑这些问题。首先,您需要实施某种断路器或超时。因为第三方服务可能会挂起并且永远不会处理结果(或者处理它们的时间太长以至于等待没有意义)。此外,您还应该考虑一些有意义的方式来保留网站 运行,即使第三方服务不可用或已更新其 API 或其他原因阻止您使用它。

最后只是最后一个想法。为什么要将已经实现异步的东西做成同步的?它是那样做的,可能是因为它可能需要时间。长时间阻塞前端以等待结果会使用户体验不愉快并且 UI 无响应。通常最好坚持异步请求并向用户展示他们正在处理但让他们同时做其他事情。

这个问题可能更多地与集成模式有关,而不是与多线程有关。但是可以使用异步调用和观察者模式的组合来编排相同 application/JVM 中的请求:

最好使用示例来完成(利用您的 Java 知识)。检查以下试图复制您的场景的简单组件:

第三方服务:暴露了一个returns相关ID的操作,并启动long-运行ning执行

class ExternalService {
    public String send() {
        return UUID.randomUUID().toString();
    }
}

您的面向客户的服务:它接收请求,调用第三方服务,然后在注册后等待响应结果接收者:

class RequestProcessor {
    public Object submitRequest() {
        String correlationId = new ExternalService().send();

        return new ResultReceiver().register(correlationId).join();
    }
}

结果接收方: 对外暴露一个操作给第三方服务,维护内部关联注册表:

class ResultReceiver {

    Map<String, CompletableFuture<Object>> subscribers;

    CompletableFuture<Object> register(String responseId) {
        CompletableFuture<Object> future = new CompletableFuture<Object>();
        this.subscribers.put(responseId, future);

        return future;
    }

    public void externalResponse(String responseId, Object result) {
        this.subscribers.get(responseId).complete(result);
    }
}

期货、承诺、回调在这种情况下很方便。同步由初始请求处理器完成,以强制为客户端阻塞执行。

现在这会引发许多问题,这些问题在这个简单的 class 集合中没有得到解决。其中一些问题可能是:

  • new ExternalService().send()new ResultReceiver().register(correlationId) 之间的竞争条件。这是可以在 ResultReceiver 中解决的问题,如果它不了解某些响应可能非常快(可以说是 2 路等待)
  • 永远不会出现结果:结果可能需要很长时间,或者只是 运行 出错。这些未来的 API 通常会提供超时以强制取消请求。例如:

    new ResultReceiver().register(correlationId)
        .get(10000, TimeUnit.SECONDS);