Я использую RestTemplate
как мой HttpClient
для выполнения URL-адреса, и сервер вернет обратно строку json в качестве ответа. Клиент будет вызывать эту библиотеку, передав DataKey
объект, в котором есть userId
.
- Используя заданный
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
check, не уверены, правильный ли подход или нет.
Я не придерживаюсь принципа единой ответственности здесь, я думаю, вообще. Может ли кто-нибудь представить пример, как лучше всего использовать принцип 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();
}
}