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

2023-12-01

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

  • 执行 Synchronous() - 等待直到得到结果,然后返回结果。
  • execute Asynchronous() - 立即返回一个 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实现中删除final变量,那么我会得到编译错误:Cannot refer to a non-final variable keys inside an inner class defined in a different method.
  • 这是在我的中使用 ListenableFutureCallback 的正确方法吗?executeAsync方法?或者有没有更好的方法来使用它?

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


我假设你正在使用 Spring 4 (异步休息模板)。在这种情况下,你得到的 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 和 CancellationException 之间有什么区别 超时异常?

在 Future#get 调用中您指定了超时。如果在keys.getTimeout()毫秒后结果仍然不可用,则会抛出TimeoutException。

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

在这种情况下没有。当以下情况时会抛出InterruptedExceptionclient's线程被中断。您不拥有该线程,因此您应该传播 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实现中必须是final的,因为您从匿名类(onFailure回调)引用它;

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

没看出有什么问题。

一些建议:

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

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

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

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

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

  4. 考虑返回更灵活的 ListenableFuture 而不是普通的 Future (使用可设置可监听未来而不是 SettableFuture)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

随机推荐

  • 我们如何等待 HTTP 请求完成?

    使用 SO 上的几个答案 我们已经成功编写并执行了一个基本的 HTTP 请求 import Foundation let url URL URL string http jsonplaceholder typicode com posts
  • 连接/从网络驱动器复制

    不完全确定如何解决这个问题 我做了一些研究 但还是不够 尝试连接到工作中的网络驱动器并复制出最新的文件夹 更新到项目 对我来说 目录以 开头 但是当我将其添加到字符串变量时 它不会连接 并且在我时不会显示尝试检查一下 这有一个过程吗 这就是
  • 使用 java 8 中的流反转映射[重复]

    这个问题在这里已经有答案了 我有一个Map
  • 为什么 String.Empty 是无效的默认参数?

    如果我输入以下内容 public Response GetArticles string Filter String Empty Body Visual Studio 给我这个错误 Filter 的默认参数值必须是编译时常量 如果我改变St
  • 读取Excel文件工作表名称

    我有一个导出过程 可将数据从 Access 表传输到 Excel 文件 有几次我遇到了问题 该过程没有在 Excel 中生成一张或多张工作表 1 张工作表 1 个表格 因此 当传输完成后 我希望 Access 检查所有工作表是否都位于 Ex
  • 如何解析具有浏览器兼容性的可编辑 DIV 文本

    我将 div 设为可编辑 当我尝试解析 div 的文本时 我需要执行以下正则表达式 innerDOM div I had downloaded all the material from the Intern br You will fin
  • AngularJS ng-options 选定值的问题

    在 AngularJS 中使用 ng options 时 我遇到了一个奇怪的问题 我的场景非常简单 与 ng model 绑定一个值作为所选选项 从后端加载 选择 的值 将加载的值绑定到 select 我从后端加载的对象是一个键 值 例如
  • Access SQL - 插入多行不起作用

    INSERT INTO EMP 1 EMP NUM EMP LNAME EMP FNAME EMP INITIAL EMP HIREDATE JOB CODE VALUES 101 News John G 08 Nov 00 502 102
  • 如何在 fancybox V2 中为 iframe 设置不同的高度/宽度?

    我一直在使用 Fancybox 1 3 4 就在那里 我在链接本身中有 iframe 的尺寸 这是我如何拥有它的一个例子 test html width 675 height 470 当我单击链接 fancybox 时 将打开具有这些尺寸的
  • 在 Fortran 90 中打开多个文件

    我想打开 10 000 个文件 文件名从abc25000 until abc35000并将一些信息复制到每个文件中 我写的代码如下 PROGRAM puppy IMPLICIT NONE integer i CHARACTER len 3
  • 在 XNA 中沿着曲线路径制作 Sprite 动画

    我想在 XNA 游戏中实现弹道轨迹 并试图找出使弹丸遵循重力曲线的最佳方法 我能想到的最好的办法是首先计算曲线并存储在 Curve 类中 然后让精灵沿着那条曲线移动 但我无法真正弄清楚如何沿着该曲线实际移动精灵 我该怎么做 或者有更好的方法
  • 如何在scrollview中滚动RecyclerView

    如何在scrollview中滚动RecyclerView上方的所有内容 我必须在滚动视图中实现 RecyclerView 如下代码所示 但不能滚动 RecyclerView 请给出答案
  • Delphi - 使用 ListView 拖放

    晚上好 我有这个代码可以使用拖放方法用于files TForm1 class TForm public procedure DropFiles var msg TMessage message WM DROPFILES end proced
  • Azure 事件网格 Blob 存储 - 防止重复 Blob 创建事件?

    我在周五的客户测试期间注意到 Blob 存储事件有一点奇怪的行为 现在我想知道是否存在导致双事件 创建 Blob 的已知情况 因此基本上外部应用程序将 blob 写入容器 大多数 blob 像往常一样只触发一个 blob 创建的事件 但由于
  • 如何在 tkinter 中访问不同类的变量?

    我已经搜索了很多 但我仍然不知道如何访问 python 中不同类的变量 在这种情况下我想访问变量self v from PageOne上课到PageTwo class 这是我的代码 import tkinter as tk import s
  • 如何将查询字符串值从 AWS API Gateway 传递到 Lambda C# 函数

    我有一个 C 方法 已成功将其发布为 AWS Lambda 函数 它看起来像这样 public class MyClass public async Task
  • 如何序列化运行时添加“属性”到Json

    我实现了在运行时向具有特殊 SystemComponent PropertyDescriptor 的对象添加 属性 的可能性 由于这些属性只能通过 ComponentModel TypeDescriptor 访问 而不能通过反射访问 因此这
  • 如何检查Access数据库表中是否存在特定列

    我想知道如何检查特定列 例如 日期 是否存在于特定表 例如 myTable 中Access数据库 我读了这个答案它提供了一个查询 该查询会产生另一个查询 IF NOT EXISTS SELECT FROM sys columns WHERE
  • 在R中如何将经度和纬度转换为可在ggplot2或ggmap中使用的格式[关闭]

    Closed 这个问题需要细节或清晰度 目前不接受答案 我得到了原始经度 纬度数据 其格式无法由 R 地图处理 所以我想知道是否有一些 R 函数或算法可以帮助我将这些原始数据转换为可读格式 也许是UTM 这是我的原始数据 纬度 32 14
  • 如何正确处理来自 ListenableFuture 番石榴的异常?

    我有一个库 其中为我们的客户提供了两种方法 同步和异步 他们可以调用他们认为适合其目的的任何方法 执行 Synchronous 等待直到得到结果 然后返回结果 execute Asynchronous 立即返回一个 Future 如果需要