我有一个供客户使用的库,他们正在传递DataRequest
对象,其中包含userid
,timeout
和其他一些字段。现在我使用这个DataRequest
对象创建一个URL,然后使用RestTemplate
进行HTTP调用,我的服务返回一个JSON响应,我用它来创建一个DataResponse
对象并返回这个DataResponse
对象回复给他们。
以下是客户通过将DataClient
对象传递给它时使用的DataRequest
类。我使用DataRequest
中的客户传递的超时值,如果在getSyncData
方法中花费太多时间,则超时请求。
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 call as async + waiting
,因为我想用线程数限制它们,否则它们可以在没有任何控制的情况下轰炸我们的服务。
问题陈述: -
我将在我的socket timeout
类中添加另一个名为DataRequest
的超时变量,我想在我的(key.getSocketTimeout())
方法中使用该变量值clientHttpRequestFactory()
而不是使用硬编码400值。这样做的最佳和最有效的方法是什么?
现在我正在使用Inversion of Control
并在构造函数中传递RestTemplate
以在我的所有Task对象之间共享RestTemplate
。我现在很困惑如何在key.getSocketTimeout()
方法中使用clientHttpRequestFactory()
值。我认为这主要是关于如何有效使用RestTemplate
的设计问题,以便我可以在key.getSocketTimeout()
方法中使用clientHttpRequestFactory()
值。
我已经简化了代码,以便想法清楚我想要做什么,而且我在Java 7上。使用ThreadLocal
是我在这里的唯一选择还是有更好的优化方式?
答案 0 :(得分:4)
作为Peter explains,使用ThreadLocal不是一个好主意。 但我也找不到一种方法来将价值传递给方法调用链#34;。
如果您使用普通&#34; Apache HttpClient&#34;,您可以创建一个HttpGet / Put / etc。并简单地打电话
httpRequest.setConfig(myRequestConfig)
。换句话说:为每个请求设置请求配置
(如果请求中未设置任何内容,则使用执行请求的HttpClient
的请求配置。)
相比之下,RestTemplate
调用createRequest(URI, HttpMethod)
(在HttpAccessor
中定义)
它使用ClientHttpRequestFactory
。换句话说:没有选项可以为每个请求设置请求配置。
我不确定为什么Spring退出了这个选项,这似乎是一个合理的功能要求(或者我可能仍然缺少某些东西)。
关于&#34的一些注意事项,他们可以在没有任何控制的情况下轰炸我们的服务&#34;:
PoolingHttpClientConnectionManager
的原因之一:
通过设置适当的最大值,可以永远不会超过指定的最大连接数(因此请求运行)同时使用。这里的假设是您为每个请求重用相同的RestTemplate
实例(以及连接管理器)。workQueue
和handler
)。答案 1 :(得分:1)
ThreadLocal是一种传递动态值的方法,通常您可以通过方法属性传递,但是您使用的是不能/不想更改的API。
您可以在线程堆栈的某个级别设置ThreadLocal(可能是包含多个值的数据结构),您可以在堆栈的更上一层使用它。
这是最好的方法吗?不,你应该把值传递给方法调用链,但有时这是不实际的。
您能否举例说明我的代码在ThreadLocal中的外观如何
您可以从
开始static final ThreadLocal<Long> SOCKET_TIMEOUT = new ThreadLocal<>();
要进行设置,您可以
SOCKET_TIMEOUT .set(key.getSocketTimeout());
并获得你可以做的价值
long socketTimeout = SOCKET_TIMEOUT.get();