Java 如何正确处理未来上市番石榴的例外情况?

Java 如何正确处理未来上市番石榴的例外情况?,java,multithreading,asynchronous,guava,future,Java,Multithreading,Asynchronous,Guava,Future,我有一个库,其中我为我们的客户提供了两种方法:同步和异步。他们可以调用任何他们认为适合自己目的的方法 executeSynchronous()-等待得到结果,然后返回结果 executeAsynchronous()-立即返回一个Future,如果需要,可以在完成其他操作后处理该Future 它们将传递包含用户id的DataKey对象。我们将根据用户id确定要调用哪台机器。因此,我们将使用AsyncRestTemplate对url进行http调用,然后根据调用是否成功将响应发送回给他们 以下是

我有一个库,其中我为我们的客户提供了两种方法:同步和异步。他们可以调用任何他们认为适合自己目的的方法

  • executeSynchronous()-等待得到结果,然后返回结果
  • executeAsynchronous()-立即返回一个Future,如果需要,可以在完成其他操作后处理该Future
它们将传递包含用户id的DataKey对象。我们将根据用户id确定要调用哪台机器。因此,我们将使用AsyncRestTemplate对url进行http调用,然后根据调用是否成功将响应发送回给他们

以下是我的界面:

public interface Client {
    // for synchronous
    public DataResponse executeSync(final DataKey key);

    // for asynchronous
    public Future<DataResponse> executeAsync(final DataKey key);
}
公共接口客户端{
//同步
公共数据响应executeSync(最终数据密钥);
//异步的
公共未来执行同步(最终数据密钥);
}
下面是我的实现:

public class DataClient implements IClient {

    // does this have to be final?
    private final AsyncRestTemplate restTemplate = new AsyncRestTemplate();

    @Override
    public DataResponse executeSync(final DataKey keys) {
        Future<DataResponse> responseFuture = executeAsync(keys);
        DataResponse response = null;
        try {
            response = responseFuture.get(keys.getTimeout(), TimeUnit.Milliseconds);
        } catch (CancellationException e) {
            // what to do here?
        }  catch (InterruptedException e) {
            // is this right way to deal with InterruptedException?
            throw new RuntimeException("Interrupted", e);
        } catch (ExecutionException e) {
            // what do you mean by ExecutionException? And how should we deal with this?
            DataLogging.logErrors(e.getCause(), DataErrorEnum.ERROR_CLIENT, keys);
            response = new DataResponse(null, DataErrorEnum.ERROR_CLIENT, DataStatusEnum.ERROR);
        } catch (TimeoutException e) {
            DataLogging.logErrors(e.getCause(), DataErrorEnum.TIMEOUT_ON_CLIENT, keys);
            response = new DataResponse(null, DataErrorEnum.TIMEOUT_ON_CLIENT, DataStatusEnum.ERROR);       
        }

        return response;
    }

    @Override
    public Future<DataResponse> executeAsync(final DataKey keys) {
        final SettableFuture<DataResponse> responseFuture = SettableFuture.create();
        restTemplate.exchange(createURL(keys), HttpMethod.GET, keys.getEntity(), String.class).addCallback(
                new ListenableFutureCallback<ResponseEntity<String>>() {
                    @Override
                    public void onSuccess(ResponseEntity<String> result) {
                        responseFuture.set(new DataResponse(result.getBody(), DataErrorEnum.OK,
                                DataStatusEnum.SUCCESS));
                    }

                    @Override
                    public void onFailure(Throwable ex) {
                        DataLogging.logErrors(ex, DataErrorEnum.ERROR_SERVER, keys);
                        responseFuture.set(new DataResponse(null, DataErrorEnum.ERROR_CLIENT,
                                DataStatusEnum.ERROR));
                    }
                });

        return responseFuture;

    }
}
公共类DataClient实现IClient{
//这必须是最终决定吗?
私有最终AsyncRestTemplate restTemplate=新AsyncRestTemplate();
@凌驾
公共DataResponse executeSync(最终数据密钥){
未来响应未来=执行同步(键);
DataResponse=null;
试一试{
response=responseFuture.get(key.getTimeout(),TimeUnit.ms);
}捕获(取消异常e){
//在这里做什么?
}捕捉(中断异常e){
//这是处理中断异常的正确方法吗?
抛出新的运行时异常(“中断”,e);
}捕获(执行例外){
//你说的死刑例外是什么意思?我们应该如何处理?
logErrors(例如getCause(),DataErrorEnum.ERROR\u客户端,键);
response=新的DataResponse(null,DataErrorEnum.ERROR\u客户端,DataStatusEnum.ERROR);
}捕获(超时异常e){
logErrors(例如,getCause(),客户端上的DataErrorEnum.TIMEOUT\u,键);
response=新的DataResponse(null,客户端上的DataErrorEnum.TIMEOUT,DataStatusEnum.ERROR);
}
返回响应;
}
@凌驾
公共未来执行同步(最终数据密钥){
final SettableFuture responseFuture=SettableFuture.create();
restemplate.exchange(createURL(keys)、HttpMethod.GET、keys.getEntity()、String.class).addCallback(
新建ListenableFutureCallback(){
@凌驾
成功时公共无效(响应结果){
responseFuture.set(新的DataResponse(result.getBody()、DataErrorEnum.OK、,
DataStatusEnum.SUCCESS);
}
@凌驾
失效时的公共无效(可丢弃的ex){
logErrors(例如,DataErrorEnum.ERROR_服务器,密钥);
responseFuture.set(新的DataResponse(null,DataErrorEnum.ERROR\u客户端,
DataStatusEnum.ERROR);
}
});
回报与未来;
}
}
现在我的问题是:

  • 如何正确处理
    executeSync
    的catch块中的异常?CancellationException和TimeoutException之间有什么区别吗?另外,我们通常应该如何处理
    执行异常
  • 在我的接口中,我的数据键必须是最终的吗?如果在executeAsync实现中删除final变量,则会出现编译错误,因为
    无法引用在不同方法中定义的内部类中的非final变量键
  • 这是在我的
    executeAsync
    方法中使用ListenableFutureCallback的正确方法吗?还是有更好的方法来使用它

在我的设计中,对于同步和异步实现,也欢迎任何输入/建议。

我假设您使用的是Spring 4()。在这种情况下,你得到的未来不是番石榴的未来,而是春天的克隆。无论如何,您应该像处理标准将来的异常一样处理异常

回答你的问题: 它不会(在本例中),但这是一个很好的实践,因为在一般情况下,它会减少对象的易变性,从而简化对其行为的推理

如果取消任务(通过Future#cancel或ExecutorService#shutdownNow)将引发CancellationException。这种情况在您的情况下不会发生,因为只有您对execute查询使用的Future和(通过私有AsyncRestTemplate隐式地)EXECUTERService有引用。所以

throw new AssertionError("executeAsync task couldn't be cancelled", e);
CancellationException和 超时异常

将来#接到指定超时的来电。如果在keys.getTimeout()毫秒后结果仍然不可用,则将引发TimeoutException

在这种情况下,当客户端的线程被中断时,不会抛出InterruptedException。您不拥有该线程,因此应该传播InterruptedException(即,declare
executeSync(数据键键)抛出InterruptedException
)。如果由于某种原因无法更改方法的签名,则在引发RuntimeException之前,至少恢复中断标志(
Thread.currentThread().interrupt()

ExecutionException是指作为Callable/Runnable提交给ExecutorService的代码在执行期间引发异常。在您的情况下,永远不会抛出ExecutionException,因为您在onSuccess和onFailure回调中返回SettableFuture并设置值,因此您可以在catch块中抛出AssertionError。没有针对执行例外的一般策略

在我的接口中,我的数据键必须是最终的吗

它在executeAsync实现中必须是final,因为您从匿名类(onFailure回调)引用它

这是r吗
catch (CancellationException e) {
    // what to do here?
}
throw new AssertionError("executeAsync task couldn't be cancelled", e);
catch (InterruptedException e) {
   // is this right way to deal with InterruptedException?
   throw new RuntimeException("Interrupted", e);
}
catch (ExecutionException e) {
   // what do you mean by ExecutionException? And how should we deal with this?
   DataLogging.logErrors(e.getCause(), DataErrorEnum.ERROR_CLIENT, keys);
   response = new DataResponse(null, DataErrorEnum.ERROR_CLIENT, DataStatusEnum.ERROR);
}