【发布时间】:2015-06-26 20:41:15
【问题描述】:
我使用RestTemplate 作为我的HttpClient 来执行 URL,服务器将返回一个 json 字符串作为响应。客户将通过传递包含userId 的DataKey 对象来调用此库。
- 使用给定的
userId,我将找出可以访问哪些机器来获取数据,然后将这些机器存储在LinkedList中,以便我可以按顺序执行它们。 - 之后我会检查第一个主机名是否在阻止列表中。如果它不在阻止列表中,那么我将使用列表中的第一个主机名创建一个 URL 并执行它,如果响应成功则返回响应。但是假设第一个主机名在阻止列表中,那么我将尝试获取列表中的第二个主机名并制作 url 并执行它,所以基本上,首先找到不在列表中的主机名在创建 URL 之前阻止列表。
- 现在,假设我们选择了不在阻止列表中的第一个主机名并执行了 URL,但不知何故服务器已关闭或没有响应,那么我将执行列表中的第二个主机名并继续执行此操作,直到您获得成功响应。 但请确保它们不在阻止列表中,因此我们需要遵循上述要点。
- 如果所有服务器都关闭或在阻止列表中,那么我可以简单地记录并返回服务不可用的错误。
下面是我的 DataClient 类,客户将调用它,他们会将 DataKey 对象传递给 getData 方法。
public class DataClient implements Client {
private RestTemplate restTemplate = new RestTemplate(new HttpComponentsClientHttpRequestFactory());
private ExecutorService service = Executors.newFixedThreadPool(15);
public Future<DataResponse> getData(DataKey key) {
DataExecutorTask task = new DataExecutorTask(key, restTemplate);
Future<DataResponse> future = service.submit(task);
return future;
}
}
下面是我的 DataExecutorTask 类:
public class DataExecutorTask implements Callable<DataResponse> {
private DataKey key;
private RestTemplate restTemplate;
public DataExecutorTask(DataKey key, RestTemplate restTemplate) {
this.restTemplate = restTemplate;
this.key = key;
}
@Override
public DataResponse call() {
DataResponse dataResponse = null;
ResponseEntity<String> response = null;
MappingsHolder mappings = ShardMappings.getMappings(key.getTypeOfFlow());
// given a userId, find all the hostnames
// it can also have four hostname or one hostname or six hostname as well in the list
List<String> hostnames = mappings.getListOfHostnames(key.getUserId());
for (String hostname : hostnames) {
// If host name is null or host name is in local block list, skip sending request to this host
if (ClientUtils.isEmpty(hostname) || ShardMappings.isBlockHost(hostname)) {
continue;
}
try {
String url = generateURL(hostname);
response = restTemplate.exchange(url, HttpMethod.GET, key.getEntity(), String.class);
if (response.getStatusCode() == HttpStatus.NO_CONTENT) {
dataResponse = new DataResponse(response.getBody(), DataErrorEnum.NO_CONTENT,
DataStatusEnum.SUCCESS);
} else {
dataResponse = new DataResponse(response.getBody(), DataErrorEnum.OK,
DataStatusEnum.SUCCESS);
}
break;
// below codes are duplicated looks like
} catch (HttpClientErrorException ex) {
HttpStatusCodeException httpException = (HttpStatusCodeException) ex;
DataErrorEnum error = DataErrorEnum.getErrorEnumByException(httpException);
String errorMessage = httpException.getResponseBodyAsString();
dataResponse = new DataResponse(errorMessage, error, DataStatusEnum.ERROR);
return dataResponse;
} catch (HttpServerErrorException ex) {
HttpStatusCodeException httpException = (HttpStatusCodeException) ex;
DataErrorEnum error = DataErrorEnum.getErrorEnumByException(httpException);
String errorMessage = httpException.getResponseBodyAsString();
dataResponse = new DataResponse(errorMessage, error, DataStatusEnum.ERROR);
return dataResponse;
} catch (RestClientException ex) {
// if it comes here, then it means some of the servers are down so adding it into block list
ShardMappings.blockHost(hostname);
}
}
if (ClientUtils.isEmpty(hostnames)) {
dataResponse = new DataResponse(null, DataErrorEnum.PERT_ERROR, DataStatusEnum.ERROR);
} else if (response == null) { // either all the servers are down or all the servers were in block list
dataResponse = new DataResponse(null, DataErrorEnum.SERVICE_UNAVAILABLE, DataStatusEnum.ERROR);
}
return dataResponse;
}
}
我的阻止列表每 1 分钟从另一个后台线程更新一次。如果任何服务器关闭并且没有响应,那么我需要使用它来阻止该服务器 -
ShardMappings.blockHost(hostname);
为了检查任何服务器是否在阻止列表中,我使用它 -
ShardMappings.isBlockHost(hostname);
如果服务器已关闭或在阻止列表中,我将返回 SERVICE_UNAVAILABLE,基于 response == null 检查,不确定这是否是正确的方法。
我想我根本没有遵循单一职责原则。 谁能在这里举个例子,什么是使用 SRP 原则的最佳方式。
经过深思熟虑,我能够提取如下所示的主机类,但不确定在我的上面使用它的最佳方法是什么 DataExecutorTask 类。
public class Hosts {
private final LinkedList<String> hostsnames = new LinkedList<String>();
public Hosts(final List<String> hosts) {
checkNotNull(hosts, "hosts cannot be null");
this.hostsnames.addAll(hosts);
}
public Optional<String> getNextAvailableHostname() {
while (!hostsnames.isEmpty()) {
String firstHostname = hostsnames.removeFirst();
if (!ClientUtils.isEmpty(firstHostname) && !ShardMappings.isBlockHost(firstHostname)) {
return Optional.of(firstHostname);
}
}
return Optional.absent();
}
public boolean isEmpty() {
return hostsnames.isEmpty();
}
}
【问题讨论】:
-
题外话:在Spring中使用模板有什么好处?
-
@PantaRhei RestTemplate 是 HttpClient 的便捷包装器。它只是简化和抽象了一些最常见的用例和代码。
标签: java spring oop resttemplate