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

一旦线程中断,如何中断RestTemplate调用?

洪通
2023-03-14
问题内容

我需要制作一个具有同步和异步功能的库。

  • executeSynchronous() -等到得到结果,然后返回结果。
  • executeAsynchronous() -立即返回Future,如果需要,可以在完成其他操作后进行处理。

我图书馆的核心逻辑

客户将使用我们的库,他们将通过传递DataKey构建器对象来调用它。然后,我们将使用该DataKey对象构造一个URL,并通过执行该对象来对该URL进行HTTP客户端调用,并在将响应作为JSON字符串返回给我们之后,通过创建DataResponse对象将该JSON字符串发送回给我们的客户。有些客户会打电话executeSynchronous(),有些可能会打电话,executeAsynchronous()所以这就是为什么我需要在库中分别提供两个方法

接口:

public interface Client {

    // for synchronous
    public DataResponse executeSynchronous(DataKey key);

    // for asynchronous
    public Future<DataResponse> executeAsynchronous(DataKey key);
}

然后我有了DataClient实现以上Client接口的代码:

public class DataClient implements Client {

    private RestTemplate restTemplate = new RestTemplate();
    private ExecutorService executor = Executors.newFixedThreadPool(10);

    // for synchronous call
    @Override
    public DataResponse executeSynchronous(DataKey key) {
        DataResponse dataResponse = null;
        Future<DataResponse> future = null;

        try {
            future = executeAsynchronous(key);
            dataResponse = future.get(key.getTimeout(), TimeUnit.MILLISECONDS);
        } catch (TimeoutException ex) {
            PotoLogging.logErrors(ex, DataErrorEnum.TIMEOUT_ON_CLIENT, key);
            dataResponse = new DataResponse(null, DataErrorEnum.TIMEOUT_ON_CLIENT, DataStatusEnum.ERROR);
            // does this looks right?
            future.cancel(true); // terminating tasks that have timed out
        } catch (Exception ex) {
            PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, key);
            dataResponse = new DataResponse(null, DataErrorEnum.CLIENT_ERROR, DataStatusEnum.ERROR);
        }

        return dataResponse;
    }

    //for asynchronous call
    @Override
    public Future<DataResponse> executeAsynchronous(DataKey key) {
        Future<DataResponse> future = null;

        try {
            Task task = new Task(key, restTemplate);
            future = executor.submit(task); 
        } catch (Exception ex) {
            PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, key);
        }

        return future;
    }
}

简单类将执行实际任务:

public class Task implements Callable<DataResponse> {

    private DataKey key;
    private RestTemplate restTemplate;

    public Task(DataKey key, RestTemplate restTemplate) {
        this.key = key;
        this.restTemplate = restTemplate;
    }

    @Override
    public DataResponse call() {
        DataResponse dataResponse = null;
        String response = null;

        try {
            String url = createURL();
            response = restTemplate.getForObject(url, String.class);

            // it is a successful response
            dataResponse = new DataResponse(response, DataErrorEnum.NONE, DataStatusEnum.SUCCESS);
        } catch (RestClientException ex) {
            PotoLogging.logErrors(ex, DataErrorEnum.SERVER_DOWN, key);
            dataResponse = new DataResponse(null, DataErrorEnum.SERVER_DOWN, DataStatusEnum.ERROR);
        } catch (Exception ex) {
            PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, key);
            dataResponse = new DataResponse(null, DataErrorEnum.CLIENT_ERROR, DataStatusEnum.ERROR);
        }

        return dataResponse;
    }

    // create a URL by using key object
    private String createURL() {
        String url = somecode;
        return url;
    }
}

当我开始研究此解决方案时,我并没有终止超时的任务。我正在向客户端报告超时,但是任务继续在线程池中运行(很长时间可能占用了我有限的10个线程之一)。因此,我在网上进行了一些研究,发现可以通过在将来使用cancel来取消超时的任务,如下所示-

future.cancel(true);

但是,如果我按照上述解决方案所示进行操作,那么是否需要像RestTemplate线程中断一样立即关闭任何其他资源?如果是,那我该怎么办?另外,我们可以打扰RestTemplate电话吗?因为我试图在任务超时后立即调用cancel,但是我猜我的线程不会被中断。

我们是否应该总是终止已经超时的任务?如果我们不这样做,那我会有什么影响?会影响我的表现吗?

在我当前的设置中,有没有更好的解决方案来处理这种情况?


问题答案:

有时不可能中断线程,尤其是当线程在Socket上执行阻塞操作时。

因此,与其在超时时取消任务,不如在http连接上设置超时。

不幸的是,每个连接工厂和RestTemplate都设置了时间限制,因此每个请求必须使用它自己的RestTemplate。

您可以为每个任务创建新的RestTemplate,或者使用ThreadLocal或资源池重用以前创建的模板。

例如,使用Thread local的任务可能如下所示:

    public class Task implements Callable<DataResponse> {

    private DataKey key;

    private ThreadLocal<RestTemplate> restTemplateThreadLocal =
            ThreadLocal.withInitial(()->new RestTemplate(new SimpleClientHttpRequestFactory()));

    public Task(DataKey key) {
        this.key = key;
    }

    private SimpleClientHttpRequestFactory getConnectionFactory(){
        return (SimpleClientHttpRequestFactory)restTemplateThreadLocal.get().getRequestFactory();
    }

    @Override
    public DataResponse call() {
        DataResponse dataResponse = null;
        String response = null;

        try {
            String url = createURL();
            //it is up to you, how to set connection and read timeouts from provided key.getTimeout
            getConnectionFactory().setConnectTimeout(1000);
            getConnectionFactory().setReadTimeout(key.getTimeout());
            response = restTemplateThreadLocal.get().getForObject(url, String.class);

            // it is a successful response
            dataResponse = new DataResponse(response, DataErrorEnum.NONE, DataStatusEnum.SUCCESS);
        } catch (RestClientException ex) {
            PotoLogging.logErrors(ex, DataErrorEnum.SERVER_DOWN, key);
            dataResponse = new DataResponse(null, DataErrorEnum.SERVER_DOWN, DataStatusEnum.ERROR);
        } catch (Exception ex) {
            PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, key);
            dataResponse = new DataResponse(null, DataErrorEnum.CLIENT_ERROR, DataStatusEnum.ERROR);
        }

        return dataResponse;
    }

    // create a URL by using key object
    private String createURL() {
        String url = somecode;
        return url;
    }
   }

顺便说一句。Spring还提供了AsyncRestTemplate,可以使您的代码更简单。如果与Netty4ClientHttpRequestFactory一起使用,则可以获取基于NIO的客户端连接。在这种情况下,即使建立Http连接,您也应该能够中断您的任务。

以下是简短示例。它使用NIO,因此您不必担心超时后是否真的取消了请求。

        URI url = new URI("http://www.chicagotribune.com/news/ct-college-of-dupage-investigation-met-20150330-story.html");
        Netty4ClientHttpRequestFactory asyncRequestFactory = new Netty4ClientHttpRequestFactory();
        AsyncRestTemplate asyncRestTemplate = new AsyncRestTemplate(asyncRequestFactory);
        ListenableFuture<ResponseEntity<String>> entity = asyncRestTemplate.getForEntity(url, String.class);
        System.out.println("entity.get() = " + entity.get());
        asyncRequestFactory.destroy();


 类似资料:
  • 我需要使一个库,我将有同步和异步的特点。 -等待得到结果,返回结果。 -立即返回一个将来值,如果需要,该将来值可以在其他事情完成后处理。 my Library的核心逻辑 将执行实际任务的简单类: 当我开始处理此解决方案时,我并没有终止超时的任务。我向客户机报告超时,但任务继续在线程池中运行(可能会长时间占用我有限的10个线程中的一个)。所以我在网上做了一些研究,我发现我可以通过使用取消未来来取消超

  • 问题内容: 使用return by时,如何中断它? 问题答案: 为此,您需要将任务分配给,而不是调用。当您执行此操作时,将返回一个可用于操纵计划任务的a。特别是,您可以调用关联程序来中断当前正在执行的任务(或者,如果该任务尚未开始运行,则完全跳过执行)。 顺便说一句,由返回的对象实际上是个。

  • 如果我在Java8中有一个并行流,并且我以anyMatch终止,并且我的集合有一个与谓词匹配的元素,那么我将试图弄清楚当一个线程处理这个元素时会发生什么。 我知道anyMatch是短路的,这样我就不会期望一旦匹配元素被处理,就会有更多的元素被处理。我的困惑是其他线程会发生什么,这些线程大概处于处理元素的中间。我可以想到3种可能的场景:a)它们是否被中断?b)它们是否继续处理它们正在处理的元素,然后

  • 很多情况下,使用信号来终止一个长时间运行的线程是合理的。这种线程的存在,可能是因为工作线程所在的线程池被销毁,或是用户显式的取消了这个任务,亦或其他各种原因。不管是什么原因,原理都一样:需要使用信号来让未结束线程停止运行。这里需要一种合适的方式让线程主动的停下来,而非让线程戛然而止。 你可能会给每种情况制定一个独立的机制,这样做的意义不大。不仅因为用统一的机制会更容易在之后的场景中实现,而且写出来

  • 我有两个实现Runnable接口的对象,并在单独的线程中执行它们。本质上,在Runnable对象的方法中,我执行一些网络活动,包括调用在等待输入(来自网络)时阻塞的方法。请注意,我没有任何有意的暂停,即调用。任何暂停都是由对可能阻塞的方法的调用引起的。 这些可运行对象在GUI的控制下,因此GUI界面和我希望提供给用户的一个功能是结束执行我的可运行对象的线程的能力,但是我无法理解如何做到这一点。 下

  • 问题内容: 我正在使用此处接受的答案中的方法来构造一个gameloop线程。 AndroidService类中的哪里停止/销毁线程? 此刻,我的线程基本上获取了时间,进行了一个本机功能调用,以更新游戏逻辑,然后在调整的经过时间之前进入睡眠状态。 令我好奇的是,由于我对Threads仍然不太满意,因此用interrupt()杀死Thread有多快?如果它在本机函数中运行的代码的中间,它将停止在其中,