Как следовать принципу единой ответственности в моем исполнителе HttpClient?

я использую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;
    }
}

Мой черный список постоянно обновляется из другого фонового потока каждую минуту. Если какой-либо сервер не работает и не отвечает, то мне нужно заблокировать этот сервер, используя это -

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();
    }
}

Ответы на вопрос(1)

Ваш ответ на вопрос