如何使用RestTemplate为每个请求设置RequestConfiguration?
我有一个供客户使用的库,他们正在传递DataRequest
对象,该对象具有userid
, timeout
和其他一些字段。 现在我使用这个DataRequest
对象创建一个URL,然后使用RestTemplate
进行HTTP调用,我的服务返回一个JSON响应,我用它来创建一个DataResponse
对象并将这个DataResponse
对象返回给它们。
下面是我的DataClient
类,客户通过将DataRequest
对象传递给它。 如果在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 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 getAsyncData(DataRequest key) { DataFetcherTask task = new DataFetcherTask(key, restTemplate); Future 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 { 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 response = DataClientFactory.getInstance().getAsyncData(key);
我正在实现sync call as async + waiting
因为我想用线程数限制它们,否则他们可以在没有任何控制的情况下轰炸我们的服务。
问题陈述:-
我将在我的DataRequest
类中添加另一个称为socket timeout
超时变量,我想在我的clientHttpRequestFactory()
方法中使用该变量值(key.getSocketTimeout())
,而不是使用硬编码的400值。 这样做的最佳和最有效的方法是什么?
现在我使用Inversion of Control
并在构造函数中传递RestTemplate
以在我的所有Task对象之间共享RestTemplate
。 我现在很困惑如何在clientHttpRequestFactory()
方法中使用key.getSocketTimeout()
值。 我认为这主要是如何在这里有效使用RestTemplate
设计问题,以便我可以在clientHttpRequestFactory()
方法中使用key.getSocketTimeout()
值。
我已经简化了代码,以便想法清楚我想要做什么,我在Java 7.使用ThreadLocal
是我在这里唯一的选择或有任何更好和优化的方式?
正如彼得解释的那样 ,使用ThreadLocal并不是一个好主意。 但我也找不到“将价值传递给方法调用链”的方法。
如果你使用普通的“Apache HttpClient”,你可以创建一个HttpGet / Put / etc。 并简单地调用httpRequest.setConfig(myRequestConfig)
。 换句话说:为每个请求设置请求配置(如果请求中没有设置任何内容,则使用来自执行请求的HttpClient
的请求配置)。
相反, RestTemplate
调用createRequest(URI, HttpMethod)
(在HttpAccessor
定义),它使用ClientHttpRequestFactory
。 换句话说:没有选项可以为每个请求设置请求配置。
我不确定为什么Spring离开了这个选项,这似乎是一个合理的function要求(或者我可能仍然遗漏了一些东西)。
关于“他们可以无法控制地轰炸我们的服务”的一些注意事项:
- 这是使用
PoolingHttpClientConnectionManager
的原因之一:通过设置适当的最大值,永远不会有超过指定的最大连接数(同时请求运行)同时使用。 这里的假设是您为每个请求重用相同的RestTemplate
实例(以及连接管理器)。 - 要更早地捕获泛洪,请在
workQueue
指定最大等待任务量并设置正确的error handling程序(使用此构造handler
中的workQueue
和handler
)。
ThreadLocal是一种传递动态值的方法,通常您可以通过方法属性传递,但是您使用的是不能/不想更改的API。
您可以在线程堆栈中的某个级别设置ThreadLocal(可能是包含多个值的数据结构),您可以在堆栈中进一步使用它。
这是最好的方法吗? 不,你应该把值传递给方法调用链,但有时候这是不切实际的。
您能举例说明我的代码在ThreadLocal中的样子吗?
你可能会开始
static final ThreadLocal SOCKET_TIMEOUT = new ThreadLocal<>();
要设置它,你可以做到
SOCKET_TIMEOUT .set(key.getSocketTimeout());
并获得你可以做的价值
long socketTimeout = SOCKET_TIMEOUT.get();
- java.net.SocketException:打开的文件太多
- 如何用RequestConfig替换不推荐使用的httpClient.getParams()?
- 配置Apache HttpClient通过代理/负载均衡器访问服务(覆盖主机头)
- HTTPClient示例 – 线程“main”中的exceptionjava.lang.NoSuchFieldError:INSTANCE
- Content-Length分隔邮件正文的过早结束(预期:
- 如何在httpclient 4.3+中更新HttpClient的设置?
- Apache 4.1 HttpClient POST和GET
- Parse在PHP中转义了JSON
- 从HttpClient 3.x迁移到4.x.