问题描述
我有一个客户正在使用的库,他们正在传递 DataRequest
对象,其中包含 userid
、timeout
和其他一些字段它.现在我使用这个 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 callhttpRequest.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 sameRestTemplate
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
andhandler
in this constructor).
这篇关于如何使用 RestTemplate 为每个请求设置 RequestConfiguration?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!