简体   繁体   English

如何处理Java 8 CompletableFuture中的错误?

[英]How to handle errors in Java 8 CompletableFuture?

I am trying to implement a simple RPC-like (or request-response) system over WebSockets in Java (there will be JS on the front-end but I am working on the back-end for now). 我试图在Java上用WebSockets实现一个简单的类RPC(或请求 - 响应)系统(前端会有JS,但我现在正在后端工作)。

I am trying to apply the Java CompletableFuture pattern to handle sending messages asynchronously. 我正在尝试应用Java CompletableFuture模式来异步处理发送消息。 But I am currnently stuck on error handling. 但我一直坚持错误处理。

I have a class (let's call it the rpc class) that is responsible to send the message over a WebSocket session (using Spring WebSocket support classes here), then to wait for "reply" type messages, and matches them with the pending request and returning the content to the caller. 我有一个类(让我们称之为rpc类)负责通过WebSocket会话发送消息(在这里使用Spring WebSocket支持类),然后等待“回复”类型的消息,并将它们与待处理的请求进行匹配。将内容返回给调用者。

The flow is : 流程是:

  • Client code calls the method on the rpc class, specifying the name of the procedure to call on the remote process, the session to which to send the message, and a map of arguments to send along. 客户端代码调用rpc类上的方法,指定要在远程进程上调用的过程的名称,要向其发送消息的会话以及要发送的参数映射。
  • The rpc class uses another lower level class to sends the message asynchronously using an Executor (thread pool), and receives a CompletableFuture<Void> for the "send the message" operation rpc类使用另一个较低级别的类使用Executor (线程池)异步发送消息,并接收CompletableFuture<Void>以执行“发送消息”操作
  • It stores the pending request in a map, builds a CompletableFuture<Map<String, Object>> and associates it with the pending request, and stores them in a map. 它将挂起的请求存储在映射中,构建CompletableFuture<Map<String, Object>>并将其与挂起的请求相关联,并将它们存储在映射中。 It returns the completable future. 它返回了可完成的未来。
  • When a "reply" type message is received, a method is called on the same class, this method tries to match the response with one of the pending requests (they have an ID for this) and then completes the CompletableFuture with the content received in the response. 当收到“回复”类型消息时,在同一个类上调用一个方法,此方法尝试将响应与其中一个待处理请求(它们具有此ID)匹配,然后使用收到的内容完成CompletableFuture 。响应。

So there are 3 threads involved : the caller thread, the thread that sends the message, and the thread that receives the message and completes the future. 因此涉及3个线程:调用者线程,发送消息的线程,以及接收消息并完成未来的线程。

Now, how should I handle an error in the sending of the message (eg IO error) in order to make the returned completableFuture also fail (or maybe implement a retry strategy, and a time out...) ? 现在,我应该如何处理发送消息中的错误(例如IO错误)以使返回的completableFuture也失败(或者可能实现重试策略,以及超时......)?

Here is the code of the rpc class method that sends the message : 以下是发送消息的rpc类方法的代码:

/**
 * Invoke a remote procedure over WS on the specified session, with the given arguments.
 * @param session The target session on which to send the RPC message
 * @param target The name of the procedure to call
 * @param arguments The arguments to be sent in the message
 * @return
 */
public CompletableFuture<Map<String,Object>> invoke(WebSocketSession session, String target, Map<String, Object> arguments){
    Invocation invocationMessage = new Invocation(target, arguments);
    invocationMessage.setId(getNextId());

    // completeable future for the result. It does nothing, will be completed when reply is received which happen in a different thread, see completeInvocation
    CompletableFuture<Map<String, Object>> invocationFuture = new CompletableFuture<>();

    CompletableFuture<Void> senderFuture = sender.sendMessage(session, invocationMessage);

    // handle problem in the sending of the message
    senderFuture.exceptionally(e -> {
        // is this correct ??
        invocationFuture.completeExceptionally(e);
        return null;
    });

    // store the pending invocation in the registry
    registry.addPendingInvocation(new PendingInvocation(invocationMessage, session, invocationFuture));

    // return the future so the caller can have access to the result once it is ready
    return invocationFuture;
}

The simplest way to do it would be to simply chain the futures using thencompose() : 最简单的方法是使用thencompose()简单地链接期货:

// completeable future for the result. It does nothing, will be completed when reply is received which happen in a different thread, see completeInvocation
CompletableFuture<Map<String, Object>> invocationFuture = new CompletableFuture<>();

CompletableFuture<Void> senderFuture = sender.sendMessage(session, invocationMessage);

// store the pending invocation in the registry
registry.addPendingInvocation(new PendingInvocation(invocationMessage, session, invocationFuture));

// return the future so the caller can have access to the result once it is ready
return senderFuture.thenCompose(__ -> invocationFuture);

In case of exceptional completion of senderFuture , the returned future will be completed exceptionally as well, with a CompletionException holding the exception as its cause (cf CompletionStage api ). senderFuture异常完成的情况下,返回的future也将异常CompletionExceptionCompletionException将异常作为其原因(cf CompletionStage api )。

Note that there are other concerns you might want to tackle as well: 请注意,您可能还需要解决其他问题:

  • Shouldn't you also cancel the pending invocation in case of exception? 如果发生异常,您是否还应该取消挂起的调用?
  • What happens if the response arrives before addPendingInvocation is called? 如果在调用addPendingInvocation之前响应到达会发生什么? Shouldn't you call it before calling sendMessage to avoid issues? 你不应该在调用sendMessage之前调用它来避免问题吗?
  • Since you don't do anything with invocationFuture , wouldn't it be better to create it inside addPenidngInvocation ? 既然你没有对invocationFuture做任何事情,那么在addPenidngInvocation创建它会不会更好?

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

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