如何正确应对来自ListenableFuture番石榴来异常? [英] How to properly deal with exceptions coming from ListenableFuture guava?

查看:1077
本文介绍了如何正确应对来自ListenableFuture番石榴来异常?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在我所提供的两种方法,同步和异步为我们的客户库。他们可以拨打他们觉得哪种方法最适合他们的目的。


  • executeSynchronous() - 等待,直到我有一个结果,返回结果

  • executeAsynchronous() - 返回一个未来如果需要其他的事情完成后立即可以加工,

他们将通过其拥有的用户ID在它DataKey对象。我们将找出对用户ID调用哪台机器的基础。因此,我们将使用AsyncRestTemplate进行HTTP调用的URL,然后发送响应返回给他们基础上,无论是成功还是失败。

下面是我的界面:

 公共接口的客户端{
    //同步
    公共DataResponse executeSync(最终DataKey键);    //异步
    公共未来< D​​ataResponse> executeAsync(最终DataKey键);
}

和下面是我的实现:

 公共类DataClient实现IClient {    //这是否必须是最终的?
    私人最终AsyncRestTemplate restTemplate =新AsyncRestTemplate();    @覆盖
    公共DataResponse executeSync(最终DataKey键){
        未来< D​​ataResponse> responseFuture = executeAsync(键);
        DataResponse响应=无效;
        尝试{
            响应= responseFuture.get(keys.getTimeout(),TimeUnit.Milliseconds);
        }赶上(CancellationException E){
            //在这里做什么?
        }赶上(InterruptedException的E){
            //是对付这种InterruptedException的正确方法?
            抛出新的RuntimeException(中断,E);
        }赶上(为ExecutionException E){
            //你是什么意思为ExecutionException?我们又该如何面对呢?
            DataLogging.logErrors(e.getCause(),DataErrorEnum.ERROR_CLIENT,密钥);
            响应=新DataResponse(NULL,DataErrorEnum.ERROR_CLIENT,DataStatusEnum.ERROR);
        }赶上(TimeoutException异常五){
            DataLogging.logErrors(e.getCause(),DataErrorEnum.TIMEOUT_ON_CLIENT,密钥);
            响应=新DataResponse(NULL,DataErrorEnum.TIMEOUT_ON_CLIENT,DataStatusEnum.ERROR);
        }        返回响应;
    }    @覆盖
    公共未来< D​​ataResponse> executeAsync(最终DataKey键){
        最后SettableFuture< D​​ataResponse> responseFuture = SettableFuture.create();
        restTemplate.exchange(createURL(键),HttpMethod.GET,keys.getEntity(),为String.class).addCallback(
                新ListenableFutureCallback< ResponseEntity<串GT;>(){
                    @覆盖
                    公共无效的onSuccess(ResponseEntity<串GT;的结果){
                        responseFuture.set(新DataResponse(result.getBody(),DataErrorEnum.OK,
                                DataStatusEnum.SUCCESS));
                    }                    @覆盖
                    公共无效onFailure处(Throwable的前){
                        DataLogging.logErrors(例如,DataErrorEnum.ERROR_SERVER,键);
                        responseFuture.set(新DataResponse(NULL,DataErrorEnum.ERROR_CLIENT,
                                DataStatusEnum.ERROR));
                    }
                });        返回responseFuture;    }
}

现在我的问题是:


  • 如何妥善处理在 executeSync 的catch块异常?有CancellationException和TimeoutException异常有什么区别?也就是我们一般应与为ExecutionException 吗?

  • 难道我的DataKey在我的界面是最终的?如果我在executeAsync执行删除最后一个变量,然后我得到的编译错误为无法引用非最终变量键在不同的方法中定义的内部类中。

  • 这是我的 executeAsync 方法使用ListenableFutureCallback的正确方法?或者有使用的?
  • 什么更好的方法

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


解决方案

我假设你使用Spring 4(<一个href=\"http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/client/AsyncRestTemplate.html\"相对=nofollow> AsyncRestTemplate )。在这种情况下ListenableFuture,你得到的是不是真的番石榴的ListenableFuture,但它的clone春天。反正你应该处理异常相同的方式,你处理与标准未来的例外。

回答您的问题:


  //这是否必须是最终的?私人最终AsyncRestTemplate
restTemplate =新AsyncRestTemplate();


它没有(在这种情况下),但是这是一个很好的做法,因为一般来说它使对象较少可变简化推理它的行为。


 赶上(CancellationException E){
    //在这里做什么?
}


如果任务被取消CancellationException将被抛出(通过未来#取消或ExecutorService的#shutdownNow时)。因为只有你有引用和未来(通过隐含的私人AsyncRestTemplate)通过执行查询使用的ExecutorService它不能在你的情况发生。因此,

 抛出新的AssertionError(executeAsync任务无法被取消,E);


  

有CancellationException之间有什么区别
  TimeoutException异常?


在未来得到#调用你指定的时间。 TimeoutException异常会被抛出,如果结果仍然不可keys.getTimeout()毫秒后。


 赶上(InterruptedException的E){
   //是对付这种InterruptedException的正确方法?
   抛出新的RuntimeException(中断,E);
}


在这种情况下,没有。当的客户的线程被中断InterruptedException异常将被抛出。你不拥有该线程所以你应该传播InterruptedException的(即申报 executeSync(DataKey键)抛出InterruptedException的)。如果由于某种原因,你不能改变方法的签名,然后至少恢复中断标志位( Thread.currentThread()。中断())抛出RuntimeException的前


 赶上(为ExecutionException E){
   //你是什么意思为ExecutionException?我们又该如何面对呢?
   DataLogging.logErrors(e.getCause(),DataErrorEnum.ERROR_CLIENT,密钥);
   响应=新DataResponse(NULL,DataErrorEnum.ERROR_CLIENT,DataStatusEnum.ERROR);
}


为ExecutionException意味着code提交给ExecutorService的作为赎回/的Runnable执行期间抛出异常。根据你的情况为ExecutionException将永远不会被抛出,因为你在这两个的onSuccess和onFailure处设置回调返回值SettableFuture,这样你就可以在catch块中抛出的AssertionError。有回应ExecutionException当没有总体战略。


  

我的DataKey是否有在我的界面是最终的?


因为你从匿名类(onFailure处回调)引用它必须是在最终实施executeAsync;


  

这是在我的executeAsync方法使用ListenableFutureCallback的正确方法?还是有使用任何更好的办法?


看不出什么毛病。

一些建议:


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

在默认情况下使用AsyncRestTemplate <一个href=\"http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/core/task/SimpleAsyncTaskExecutor.html\"相对=nofollow> SimpleAsyncTaskExecutor 它为每个请求创建一个新的线程。这可能并不适合所有客户。请注意,如果你按照这个建议反应CancellationException必须为不同的客户现在可以参考的ExecutorService:抛RuntimeException的要细

<醇开始=2>
  • 在描述默认使用(JAVA)DOC线程池!


  • 我会分裂同步和异步版本。


  • 我觉得用同步RestTemplate并通过同步版本手段实现异步版本将简化实施。


  • 考虑回到更加灵活ListenableFuture而不是简单的未来(使用<一个href=\"http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/util/concurrent/SettableListenableFuture.html\"相对=nofollow> SettableListenableFuture 而不是SettableFuture)。


  • I have a library in which I have provided two methods, sync and async for our customer. They can call whichever method they feel is right for their purpose.

    • executeSynchronous() - waits until I have a result, returns the result.
    • executeAsynchronous() - returns a Future immediately which can be processed after other things are done, if needed.

    They will pass DataKey object which has the user id in it. And we will figure out which machine to call basis on the user id. So we will make http call to the url using AsyncRestTemplate and then send the response back to them basis on whether it is successful or not.

    Below is my interface:

    public interface Client {
        // for synchronous
        public DataResponse executeSync(final DataKey key);
    
        // for asynchronous
        public Future<DataResponse> executeAsync(final DataKey key);
    }
    

    And below is my implementation:

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

    Now my question is:

    • How do I properly deal with exceptions in the catch block of executeSync? Is there any difference between CancellationException and TimeoutException? Also what we should do with ExecutionException in general?
    • Does my DataKey have to be final in my interface? If I remove final variable in my executeAsync implementation, then I get compilation error as Cannot refer to a non-final variable keys inside an inner class defined in a different method.
    • Is this the right way to use ListenableFutureCallback in my executeAsync method? Or is there any better way to use that?

    Any inputs/suggestions are also welcome on my design for having sync and async implementations.

    解决方案

    I assume you're using Spring 4 (AsyncRestTemplate). In this case ListenableFuture that you get is not really Guava's ListenableFuture, but it's clone in Spring. Anyway you should deal with exceptions the same way as you deal with exceptions from the standard Future.

    Answers to your questions:

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

    It doesn't (in this case), but this is a good practice as in general it makes object less mutable simplifying reasoning about it's behaviour.

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

    CancellationException will be thrown if task is cancelled (either via Future#cancel or ExecutorService#shutdownNow). It cannot happen in your case as only you have references to Future and (implicitly via private AsyncRestTemplate) ExecutorService used by execute queries. So

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

    Is there any difference between CancellationException and TimeoutException?

    In Future#get call you've specified timeout. TimeoutException will be thrown if result is still not available after keys.getTimeout() milliseconds.

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

    In this case no. InterruptedException will be thrown when client's thread is interrupted. You don't own that thread so you should propagate InterruptedException (i.e. declare executeSync(DataKey keys) throws InterruptedException). If for some reason you cannot change signature of the method then at least restore interrupted flag (Thread.currentThread().interrupt()) before throwing 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 means that code submitted to ExecutorService as Callable/Runnable threw exception during execution. In your case ExecutionException will never be thrown because you return SettableFuture with value set in both onSuccess and onFailure callbacks, so you can throw AssertionError in the catch block. There is no general strategy of response to ExecutionException.

    Does my DataKey have to be final in my interface?

    It must be final in executeAsync implementation because you reference it from anonymous class (onFailure callback);

    Is this the right way to use ListenableFutureCallback in my executeAsync method? Or is there any better way to use that?

    Don't see anything wrong with it.

    Some advices:

    1. Consider making thread pool for async client configurable.

    By default AsyncRestTemplate uses SimpleAsyncTaskExecutor which creates new thread for each request. This may be not suitable for all your clients. Note that if you follow this advice response to CancellationException must be different as client now can have reference to ExecutorService: throwing RuntimeException should be fine.

    1. Describe in (java)doc thread pool used by default!

    2. I would split sync and async versions.

    3. I think that using sync RestTemplate and implementing async version by means of sync version would simplify implementation.

    4. Consider returning more flexible ListenableFuture instead of plain Future (using SettableListenableFuture instead of SettableFuture).

    这篇关于如何正确应对来自ListenableFuture番石榴来异常?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆