如何使用 RestTemplate 为每个请求设置 RequestConfiguration? [英] How to set RequestConfiguration per request using RestTemplate?

查看:42
本文介绍了如何使用 RestTemplate 为每个请求设置 RequestConfiguration?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个客户正在使用的库,他们正在传递 DataRequest 对象,其中包含 useridtimeout 和其他一些字段它.现在我使用这个 DataRequest 对象来创建一个 URL,然后我使用 RestTemplate 进行 HTTP 调用,我的服务返回一个 JSON 响应,我用它来创建一个 DataResponse 对象并将这个 DataResponse 对象返回给他们.

I have a library which is being used by customer and they are passing DataRequest object which has userid, timeout and some other fields in it. Now I use this DataRequest object to make a URL and then I make an HTTP call using RestTemplate and my service returns back a JSON response which I use it to make a DataResponse object and return this DataResponse object back to them.

下面是我的 DataClient 类,客户通过将 DataRequest 对象传递给它来使用它.如果 getSyncData 方法花费太多时间,我正在使用客户在 DataRequest 中传递的超时值来超时请求.

Below is my DataClient class used by customer by passing DataRequest object to it. I am using timeout value passed by customer in DataRequest to timeout the request if it is taking too much time in getSyncData method.

public class DataClient implements Client {

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

    // this constructor will be called only once through my factory
    // so initializing here
    public DataClient() {
        try {
          restTemplate.setRequestFactory(clientHttpRequestFactory());
        } catch (Exception ex) {
          // log exception
        }
    }           

    @Override
    public DataResponse getSyncData(DataRequest key) {
        DataResponse response = null;
        Future<DataResponse> responseFuture = null;

        try {
            responseFuture = getAsyncData(key);
            response = responseFuture.get(key.getTimeout(), key.getTimeoutUnit());
        } catch (TimeoutException ex) {
            response = new DataResponse(DataErrorEnum.CLIENT_TIMEOUT, DataStatusEnum.ERROR);
            responseFuture.cancel(true);
            // logging exception here               
        }

        return response;
    }   

    @Override
    public Future<DataResponse> getAsyncData(DataRequest key) {
        DataFetcherTask task = new DataFetcherTask(key, restTemplate);
        Future<DataResponse> future = service.submit(task);

        return future;
    }

    // how to set socket timeout value by using `key.getSocketTimeout()` instead of using hard coded 400
    private ClientHttpRequestFactory clientHttpRequestFactory() {
        HttpComponentsClientHttpRequestFactory requestFactory =
            new HttpComponentsClientHttpRequestFactory();
        RequestConfig requestConfig =
            RequestConfig.custom().setConnectionRequestTimeout(400).setConnectTimeout(400)
                .setSocketTimeout(400).setStaleConnectionCheckEnabled(false).build();
        SocketConfig socketConfig =
            SocketConfig.custom().setSoKeepAlive(true).setTcpNoDelay(true).build();

        PoolingHttpClientConnectionManager poolingHttpClientConnectionManager =
            new PoolingHttpClientConnectionManager();
        poolingHttpClientConnectionManager.setMaxTotal(300);
        poolingHttpClientConnectionManager.setDefaultMaxPerRoute(200);

        CloseableHttpClient httpClientBuilder =
            HttpClientBuilder.create().setConnectionManager(poolingHttpClientConnectionManager)
                .setDefaultRequestConfig(requestConfig).setDefaultSocketConfig(socketConfig).build();

        requestFactory.setHttpClient(httpClientBuilder);
        return requestFactory;
    }       
}

DataFetcherTask 类:

public class DataFetcherTask implements Callable<DataResponse> {

    private final DataRequest key;
    private final RestTemplate restTemplate;

    public DataFetcherTask(DataRequest key, RestTemplate restTemplate) {
        this.key = key;
        this.restTemplate = restTemplate;
    }

    @Override
    public DataResponse call() throws Exception {
        // In a nutshell below is what I am doing here. 
        // 1. Make an url using DataRequest key.
        // 2. And then execute the url RestTemplate.
        // 3. Make a DataResponse object and return it.
    }
}

我们公司内的客户将通过在他们的代码库中使用我的工厂来使用我的库,如下所示 -

Customer within our company will use my library like this as shown below by using my factory in their code base -

// if they are calling `getSyncData()` method
DataResponse response = DataClientFactory.getInstance().getSyncData(key);

// and if they want to call `getAsyncData()` method
Future<DataResponse> response = DataClientFactory.getInstance().getAsyncData(key);

我正在将 sync 调用实现为异步 + 等待,因为我想用线程数来限制它们,否则它们会在没有任何控制的情况下轰炸我们的服务.

I am implementing sync call as async + waiting since I want to throttle them with the number of threads otherwise they can bombard our service without any control.

问题陈述:-

我将在我的 DataRequest 类中添加另一个名为 socket timeout 的超时变量,并且我想使用该变量值 (key.getSocketTimeout()) 在我的 clientHttpRequestFactory() 方法中,而不是使用硬编码的 400 值.最好和最有效的方法是什么?

I am going to add another timeout variable called socket timeout in my DataRequest class and I want to use that variable value (key.getSocketTimeout()) in my clientHttpRequestFactory() method instead of using hard coded 400 value. What is the best and efficient way to do that?

现在我正在使用 Inversion of Control 并在构造函数中传递 RestTemplate 以在我的所有 Task 对象之间共享 RestTemplate.我现在很困惑如何在我的 clientHttpRequestFactory() 方法中使用 key.getSocketTimeout() 值.我认为这主要是关于如何在这里有效地使用 RestTemplate 的设计问题,以便我可以在我的 clientHttpRequestFactory() 中使用 key.getSocketTimeout() 值> 方法.

Right now I am using Inversion of Control and passing RestTemplate in a constructor to share the RestTemplate between all my Task objects. I am confuse now how to use key.getSocketTimeout() value in my clientHttpRequestFactory() method. I think this is mostly design question of how to use RestTemplate efficiently here so that I can use key.getSocketTimeout() value in my clientHttpRequestFactory() method.

我已经简化了代码,以便让我的想法变得清晰,我正在尝试做什么,而且我使用的是 Java 7.使用 ThreadLocal 是我在这里的唯一选择,或者有任何更好和优化的方法?

I have simplified the code so that idea gets clear what I am trying to do and I am on Java 7. Using ThreadLocal is the only option I have here or there is any better and optimized way?

推荐答案

正如 Peter 解释的,使用 ThreadLocal 并不是这里的好主意.但我也找不到将值传递到方法调用链上"的方法.

As Peter explains, using ThreadLocal is not a good idea here. But I also could not find a way to "pass the value up the chain of method calls".

如果你使用普通的Apache HttpClient",你可以创建一个 HttpGet/Put/etc.并简单地调用httpRequest.setConfig(myRequestConfig).换句话说:为每个请求设置一个请求配置(如果请求中未设置任何内容,则使用来自执行请求的 HttpClient 的请求配置).

If you use plain "Apache HttpClient", you can create an HttpGet/Put/etc. and simply call httpRequest.setConfig(myRequestConfig). In other words: set a request configuration per request (if nothing is set in the request, the request configuration from the HttpClient which executes the request is used).

相比之下,RestTemplate调用 createRequest(URI, HttpMethod)(定义在 HttpAccessor)它使用 ClientHttpRequestFactory.换句话说:没有选项可以为每个请求设置请求配置.
我不知道为什么 Spring 没有考虑这个选项,这似乎是一个合理的功能需求(或者我可能仍然缺少一些东西).

In contrast, the RestTemplate calls createRequest(URI, HttpMethod) (defined in HttpAccessor) which uses the ClientHttpRequestFactory. In other words: there is no option to set a request configuration per request.
I'm not sure why Spring left this option out, it seems a reasonable functional requirement (or maybe I'm still missing something).

关于他们可以在没有任何控制的情况下轰炸我们的服务"的一些说明:

Some notes about the "they can bombard our service without any control":

  • 这是使用 PoolingHttpClientConnectionManager 的原因之一:通过设置适当的最大值,同时使用的连接数(因此请求运行)永远不会超过指定的最大连接数.这里的假设是您对每个请求重复使用相同的 RestTemplate 实例(以及连接管理器).
  • 要更早地捕获洪水,请指定线程池中等待任务的最大数量并设置适当的错误处理程序(使用 这个构造函数).
  • This is one of the reasons to use the PoolingHttpClientConnectionManager: by setting the appropriate maximum values, there can never be more than the specified maximum connections in use (and thus requests running) at the same time. The assumption here is that you re-use the same RestTemplate instance (and thus connection manager) for each request.
  • To catch a flood earlier, specify a maximum amount of waiting tasks in the threadpool and set a proper error-handler (use the workQueue and handler in this constructor).

这篇关于如何使用 RestTemplate 为每个请求设置 RequestConfiguration?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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