当前位置: 首页 > 面试题库 >

如何正确处理来自ListenableFuture番石榴的异常?

王渊
2023-03-14
问题内容

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

  • executeSynchronous()-等待直到得到结果,然后返回结果。
  • executeAsynchronous()-立即返回一个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);
}

下面是我的实现:

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;

    }
}

现在我的问题是:

  • 如何正确处理的catch块中的异常executeSync?CancellationException和TimeoutException之间有什么区别吗?另外,我们应该怎么做ExecutionException
  • 我的DataKey是否必须在界面中是最终的?如果我在我的executeAsync实现中删除最终变量,则编译错误为Cannot refer to a non-final variable keys inside an inner class defined in a different method
  • 这是在我的executeAsync方法中使用ListenableFutureCallback的正确方法吗?还是有更好的方法来使用它?

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


问题答案:

我假设您正在使用Spring
4(AsyncRestTemplate)。在这种情况下,您获得的ListenableFuture实际上不是Guava的ListenableFuture,而是在Spring中的克隆。无论如何,您应该像处理标准Future中的异常一样处理异常。

您的问题的答案:

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

它不是(在这种情况下),但这是一个好习惯,因为通常它使对象变得不那么易变,从而简化了有关其行为的推理。

catch (CancellationException e) {
    // what to do here?
}

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

throw new AssertionError("executeAsync task couldn't be cancelled", e);

CancellationException和TimeoutException之间有什么区别吗?

在Future#get呼叫中,您已指定超时。如果在keys.getTimeout()毫秒后仍然无法获得结果,则将抛出TimeoutException。

catch (InterruptedException e) {
   // is this right way to deal with InterruptedException?
   throw new RuntimeException("Interrupted", e);
}

在这种情况下,没有。当 客户端
线程中断时,将引发InterruptedException。您不拥有该线程,因此应该传播InterruptedException(即,声明executeSync(DataKey keys) throws InterruptedException)。如果由于某种原因您不能更改方法的签名,则至少Thread.currentThread().interrupt()在引发RuntimeException之前还原中断的标志()。

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);
}

ExecutionException表示在执行过程中以Callable /
Runnable形式提交给ExecutorService的代码引发了异常。在您的情况下,永远不会引发ExecutionException,因为您返回的settableFuture在onSuccess和onFailure回调中均设置了值,因此可以在catch块中引发AssertionError。没有响应ExecutionException的通用策略。

我的DataKey是否必须在界面中是最终的?

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

这是在我的executeAsync方法中使用ListenableFutureCallback的正确方法吗?还是有更好的方法来使用它?

没有发现任何问题。

一些建议:

  1. 考虑使异步客户端的线程池可配置。

默认情况下,AsyncRestTemplate使用SimpleAsyncTaskExecutor,它为每个请求创建新线程。这可能并不适合所有客户。请注意,如果您遵循此建议,对CancellationException的响应必须有所不同,因为客户端现在可以引用ExecutorService:抛出RuntimeException应该很好。

  1. 描述默认使用的(java)doc线程池!

  2. 我会拆分同步和异步版本。

  3. 我认为使用同步RestTemplate并通过同步版本实现异步版本将简化实现。

  4. 考虑返回更灵活的ListenableFuture而不是简单的Future(使用SettableListenableFuture而不是SettableFuture)。



 类似资料:
  • 问题内容: 刚刚发现了Guava库项目。 这些与GWT兼容吗? 问题答案: 从该页面的介绍性PDF中, 您可以在…上使用这些库。 在JDK 6上 在Android上, 我们认为。需要志愿者来帮助我们进行测试。 在Google App Engine上, 我们认为。需要志愿者来帮助我们进行测试。 在GWT上-参差不齐! 由于GWT的JDK库支持 可能参差不齐,也许是2/3,所以到目前为止,这些库中的东

  • 问题内容: 我有一个带有参数的方法,该参数可以为NULL。我想将输入的本地副本作为结束。现在,我的代码如下所示: 有没有更清洁的方法可以做到这一点?如果是一个简单的参数,我可以做类似的事情,但是我不确定是否有类似处理集合的事情。 问题答案: 我不明白为什么您不能使用: 您可以使用静态导入保存某些类型的输入,如果您要这样做的话:

  • 问题内容: 在过去的几周内,我一直在尝试使用番石榴的MapMaker查找理想的缓存实现。 根据我所学的知识,我的下一个尝试将弃用软值,以使用maximumSize和expireAfterAccess: 哪里 但是,我仍然要解决的一个问题是,一旦它们用完了,即使对象很容易到达,此实现也将逐出对象。这可能会导致环境中漂浮着具有相同UID的多个对象,而这是我所不希望的(我相信我试图实现的目标称为规范化)

  • 问题内容: 我想知道哪个更有效,为什么? 1) 要么 2) 问题答案: 我看不出您为什么要在此处使用builder的任何原因: 比在这种情况下制作一个更具可读性, 不会推断通用类型,并且在用作单行代码时必须自己指定类型, (来自docs) 在与另一个不可变集合调用时 做得很好( 尝试避免在安全的情况下实际复制数据 ), (从源) 调用以前创建的,同时避免为零元素和一元素集合创建任何列表(分别返回空

  • 我想为番石榴设个坑(http://pitest.org/)进行突变熟练度测试,但我在尝试运行时出现以下错误。这是我得到的错误: 基本上,要么PIT没有检测到测试,要么测试在PIT中运行不正常,所有测试都失败了。我读了这篇文章:JUnit测试通过,但PIT说套件不是绿色的,我认为“您的代码库是否包含可变静态状态?(例如在单例中)”是问题所在,但我不知道如何修复它。如果测试套件有一些隐藏的顺序依赖关系

  • 我的 Spark 版本是 2.2.0,它在本地工作,但在具有相同版本的 EMR 上,它给出了以下异常。