我有一个库,其中客户正在传递一个DataRequest
对象,该对象包含包含用户ID和其他字段的信息。我们使用DataRequest
对象对两个不同的REST服务进行HTTP调用,然后创建一个DataResponse
对象并将其返回给客户。我在我的库中有一个全局级别超时,它应用于两个HTTP调用,如果调用得到timedout,那么我们只需返回带有超时错误消息给客户同时制作DataResponse
对象。
给定一个DataRequest
对象,我将对一个服务进行HTTP调用,这将给我一些东西,然后根据我将生成List,然后对于每个DataRequest
对象,我将调用{{我在performDataRequest
方法中使用相同的全局超时并行方法,然后生成getSyncData
对象并返回响应。
以下是我的List<DataResponse>
课程,客户将通过传递DataClient
对象来调用
DataRequest
以下是完成所有工作的public class DataClient implements Client {
private RestTemplate restTemplate = new RestTemplate();
private ExecutorService service = Executors.newFixedThreadPool(15);
@Override
public List<DataResponse> getSyncData(DataRequest key) {
List<DataResponse> response = new ArrayList<DataResponse>();
Future<List<DataResponse>> responseFuture = null;
try {
responseFuture = getAsyncData(key);
response = responseFuture.get(key.getTimeout(), key.getTimeoutUnit());
} catch (TimeoutException ex) {
response.add(new DataResponse(DataErrorEnum.CLIENT_TIMEOUT, DataStatusEnum.ERROR));
responseFuture.cancel(true); // terminating the tasks that have got timed out
// logging exception here
}
return response;
}
@Override
public Future<List<DataResponse>> getAsyncData(DataRequest key) {
DataFetcherTask task = new DataFetcherTask(key, restTemplate);
Future<List<DataResponse>> future = service.submit(task);
return future;
}
}
课程:
DataFetcherTask
Probem声明: -
public class DataFetcherTask implements Callable<List<DataResponse>> {
private DataRequest key;
private RestTemplate restTemplate;
private ExecutorService executorService = Executors.newFixedThreadPool(10);
public DataFetcherTask(DataRequest key, RestTemplate restTemplate) {
this.key = key;
this.restTemplate = restTemplate;
}
@Override
public List<DataResponse> call() throws Exception {
List<DataRequest> keys = performKeyRequest();
List<Future<DataResponse>> responseFutureList = new ArrayList<Future<DataResponse>>();
for (final DataRequest key : keys) {
responseFutureList.add(executorService.submit(new Callable<DataResponse>() {
@Override
public DataResponse call() throws Exception {
return performDataRequest(key);
}
}));
}
List<DataResponse> responseList = new ArrayList<DataResponse>();
for (Future<DataResponse> future : responseFutureList) {
responseList.add(future.get());
}
return responseList;
}
private List<DataRequest> performKeyRequest() {
List<DataRequest> keys = new ArrayList<>();
// use key object which is passed in contructor to make HTTP call to another service
// and then make List of DataRequest object and return keys.
// max size of keys list will be three.
return keys;
}
private DataResponse performDataRequest(DataRequest key) {
Mappings mappings = ShardMapping.getMappings(key.getType());
List<String> hostnames = mappings.getAllHostnames(key);
for (String hostname : hostnames) {
if (DataUtils.isEmpty(hostname) || ShardMapping.isBlockHost(hostname)) {
continue;
}
try {
String url = generateUrl(hostname);
URI uri = URI.create(url);
ResponseEntity<String> response = restTemplate.exchange(uri, HttpMethod.GET, key.getEntity(), String.class);
ShardMapping.unblockHost(hostname);
if (response.getStatusCode() == HttpStatus.NO_CONTENT) {
return new DataResponse(response.getBody(), DataErrorEnum.NO_CONTENT,
DataStatusEnum.SUCCESS);
} else {
return new DataResponse(response.getBody(), DataErrorEnum.OK, DataStatusEnum.SUCCESS);
}
} catch (HttpClientErrorException | HttpServerErrorException ex) {
HttpStatusCodeException httpException = ex;
DataErrorEnum error = DataErrorEnum.getErrorEnumByException(httpException);
String errorMessage = httpException.getResponseBodyAsString();
return new DataResponse(errorMessage, error, DataStatusEnum.ERROR);
// logging exception here
} catch (RestClientException ex) {
ShardMapping.blockHost(hostname);
// logging exception here
}
}
return new DataResponse(DataErrorEnum.SERVICE_UNAVAILABLE, DataStatusEnum.ERROR);
}
}
方法? performDataRequest
方法在另一个call
内完成这项工作感觉很奇怪?为此,我有两个执行器,一个在call
类内部,有15个线程,另一个在DataClient
类中有10个线程。不确定这是否是正确的方法?还有更好的办法吗?答案 0 :(得分:1)
我的代码线程是否与我调用performDataRequest的方式一样安全 调用方法并行的方法?
主要是,但并非完全。当另一个线程正在调用ShardMapping
时,一个线程是否可以修改ShardMapping.getMapping()
?例如,ShardMapping.unblockHost()
修改ShardMapping
吗?如果是这样,如果两个线程同时尝试呼叫ShardMapping.unblockHost()
,您就会被搞砸。这有意义吗?
修复方法是让PerformDataRequest()
仅执行HTTP请求,而不是执行ShardMapping
逻辑。像这样:
private DataResponse performDataRequest(URI uri, DataRequest key) {
try {
ResponseEntity<String> response = restTemplate.exchange(uri, HttpMethod.GET, key.getEntity(), String.class);
if (response.getStatusCode() == HttpStatus.NO_CONTENT) {
return new DataResponse(response.getBody(), DataErrorEnum.NO_CONTENT,
DataStatusEnum.SUCCESS);
} else {
return new DataResponse(response.getBody(), DataErrorEnum.OK, DataStatusEnum.SUCCESS);
}
} catch (HttpClientErrorException | HttpServerErrorException ex) {
HttpStatusCodeException httpException = ex;
DataErrorEnum error = DataErrorEnum.getErrorEnumByException(httpException);
String errorMessage = httpException.getResponseBodyAsString();
return new DataResponse(errorMessage, error, DataStatusEnum.ERROR);
// logging exception here
} catch (RestClientException ex) {
return null;
// logging exception here
}
}
然后在ShardMapping
循环中将for (final DataRequest key : keys) {
代码移到未来之外。
其次,在另一个内部使用call方法感觉很奇怪 打电话做这个工作?为此我也有两个执行者, 一个内部DataClient类,有15个线程,另一个在 DataFetcherTask类有10个线程。不确定这是不是 正确的方法吗?还有更好的办法吗?
这有点傻,而且不是最好的方法。现在,您的设置如下所示:
+----------------- performDataRequest()
|
max 3 sec |
getAsyncData --- DataFetcherTask ----------- performDataRequest()
|
|
+----------------- performDataRequest()
相反,为什么不在performDataRequest()
的未来放置3秒超时,然后只是正常拨打DataFetcherTask.call()
?
max 3 sec
+----------------- performDataRequest()
|
| max 3 sec
DataFetcherTask ----------- performDataRequest()
|
| max 3 sec
+----------------- performDataRequest()