【问题标题】:How to use RestTemplate efficiently with RequestFactory?如何通过 RequestFactory 有效地使用 RestTemplate?
【发布时间】:2015-03-27 07:37:35
【问题描述】:

我正在开发一个项目,在该项目中我需要对运行 Restful 服务的服务器进行 HTTP URL 调用,该服务将响应作为 JSON 字符串返回。我在这里使用RestTemplateHttpComponentsClientHttpRequestFactory 来执行一个url。

我使用HttpComponentsClientHttpRequestFactory 在我的RestTemplate 上设置了http 请求超时(读取和连接超时)。

下面是我的界面:

public interface Client {

    // for synchronous
    public String getSyncData(String key, long timeout);

    // for asynchronous
    public String getAsyncData(String key, long timeout);
}

下面是我的客户端接口实现-

public class DataClient implements Client {

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

    // for synchronous call
    @Override
    public String getSyncData(String key, long timeout) {
        String response = null;

        try {
            Task task = new Task(key, restTemplate, timeout);

            // direct call, implementing sync call as async + waiting is bad idea. 
            // It is meaningless and consumes one thread from the thread pool per a call.               
            response = task.call();
        } catch (Exception ex) {
            PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, key);
        }

        return response;
    }

    // for asynchronous call
    @Override
    public Future<String> getAsyncData(String key, long timeout) {
        Future<String> future = null;

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

        return future;
    }
}

下面是我的简单任务类

class Task implements Callable<String> {

    private RestTemplate restTemplate;
    private String key;
    private long timeout; // in milliseconds

    public Task(String key, RestTemplate restTemplate, long timeout) {
        this.key = key;
        this.restTemplate = restTemplate;
        this.timeout = timeout;
    }

    public String call() throws Exception {

        String url = "some_url_created_by_using_key";

        // does this looks right the way I am setting request factory?
        // or is there any other effficient way to do this?
        restTemplate.setRequestFactory(clientHttpRequestFactory());
        String response = restTemplate.exchange(url, HttpMethod.GET, null, String.class);

        return response;
    }

    private static ClientHttpRequestFactory clientHttpRequestFactory() {
        // is it ok to create a new instance of HttpComponentsClientHttpRequestFactory everytime?
        HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
        factory.setReadTimeout(timeout); // setting timeout as read timeout
        factory.setConnectTimeout(timeout); // setting timeout as connect timeout
        return factory;
    }   
}

现在我的问题是 - 我每次在 Task 类的调用方法中使用 RestTemplatesetRequestFactory 的方式是否有效?由于RestTemplate 创建起来非常繁重,所以不确定我是否做对了。

每次都可以创建HttpComponentsClientHttpRequestFactory 的新实例吗?会贵吗?

如果我们需要设置读取和连接超时,使用RestTemplate 的正确和有效方法是什么。

这个库会这样使用——

String response = DataClientFactory.getInstance().getSyncData(keyData, 100);

【问题讨论】:

  • 仅供参考 Spring 4 有一个 AsyncRestTemplate。
  • @AdamGent 我明白了,它对我的​​场景有用吗?
  • AsyncRestTemplate 可能会完全消除对 Task 的需求,因为它返回 Future。您可以选择在普通的 RestTemplate 或 Async 上运行请求,而不是在直接执行任务或将其提交给执行程序之间进行选择。

标签: java multithreading performance resttemplate


【解决方案1】:

据我所知,您重复使用相同的RestTemplate 对象,但每个Task 都在执行此行:restTemplate.setRequestFactory(clientHttpRequestFactory());。这似乎可以有竞争条件,例如一个Task 可以设置另一个Task 会意外使用的RequestFactory

否则,您似乎正确地使用了RestTemplate

您的超时多久更改一次?如果您主要使用一两个超时,您可以使用带有预加载超时的RequestFactory 构造函数创建一两个RestTemplates。如果您是效率的坚持者,请创建一个 HashMap&lt;Integer, RestTemplate&gt;,它会在每次请求新的超时时缓存具有特定超时的 RestTemplate

否则,查看RestTemplate's constructorHttpComponentsClientHttpRequestFactory's constructor 的代码,它们看起来并不会特别繁重,因此重复调用它们可能不会成为太大的瓶颈。

【讨论】:

  • 在仔细查看代码后,我可以看到您刚才解释的竞争条件。我怎样才能避免这种竞争条件?
  • 每个客户都会在他们的代码库中使用这个库,所以他们只会设置一个超时时间,他们不会继续为每次调用更改超时值。设置后,它将用于他们将对该库进行的所有调用。
  • 修复竞争条件的简单方法是在创建任务之前创建一个新的 RestTemplate。然后,您根本不需要将超时传递给 Task。如果一个库使用只有一个超时设置,我建议添加 DataClient.setTimeout(int timeout)。然后您可以使用一个 RestTemplate,通过该函数设置一次超时,然后继续生活。
  • 你能提供一个例子我该怎么做吗?这将有助于我更好地理解。
  • 哪个例子?为避免竞争条件,请将“clientHttpRequestFactory”从 Task 移至 DataClient。然后任务创建行将看起来像“Task task = new Task(key, new RestTemplate(clientHttpRequestFactory(timeout));”