java - 如何使用 RestTemplate 为每个请求设置 RequestConfiguration?

标签 java spring multithreading apache-httpclient-4.x resttemplate

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

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

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

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

// 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 调用实现为异步 + 等待,因为我想用线程数限制它们,否则它们可以在没有任何控制的情况下轰炸我们的服务。

问题陈述:-

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

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

我已经简化了代码,以便清楚我想要做什么并且我在 Java 7 上。使用 ThreadLocal 是我在这里唯一的选择,或者有任何更好和优化的方法?

最佳答案

作为 Peter explains ,在这里使用 ThreadLocal 不是一个好主意。 但我也找不到“将值传递到方法调用链”的方法。

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

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

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

  • 这是使用PoolingHttpClientConnectionManager的原因之一: 通过设置适当的最大值,同时使用的连接数永远不会超过指定的最大连接数(因此请求正在运行)。这里的假设是您为每个请求重复使用相同的 RestTemplate 实例(以及连接管理器)。
  • 要更早地捕获洪水,请指定线程池中等待任务的最大数量并设置适当的错误处理程序 (使用 this constructor 中的 workQueuehandler )。

关于java - 如何使用 RestTemplate 为每个请求设置 RequestConfiguration?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37125694/

相关文章:

c# - 从其他线程调用主线程中的方法

Java ModelMapper 将日期映射到字符串格式字段

java - Spring Boot - 不明确的映射

java - JspWriter 写入与打印

java - minOccurs设置为0的XSD日期类型现在的值为“<xml-fragment…”(可设置为false)

c# - Mutex() 和 Mutex(true ,"myMutex") 线程对象有什么区别?

java - 如何在服务器端使用 Java 验证 Windows Phone 应用内购买?

java - 如何将身份验证从 LDAP 更改为 JDBC 以保护我的应用程序

java - ManyToMany 关系上的 PostgreSQL 约束违规错误,但应存在外键

python - 是否可以在完成后获得 python 池结果?