如何在我的HttpClient执行器中遵循单一责任原则?

2022-09-04 04:23:08

我使用RestTemplate作为我的执行URL,服务器将返回一个json字符串作为响应。客户将通过传递其中包含的对象来调用此库。HttpClientDataKeyuserId

  • 使用给定的,我将找出我可以点击哪些机器来获取数据,然后将这些机器存储在一个中,以便我可以按顺序执行它们。userIdLinkedList
  • 之后,我将检查第一个主机名是否在阻止列表中。如果它不在阻止列表中,那么我将创建一个具有列表中第一个主机名的URL并执行它,如果响应成功,则返回响应。但是,假设第一个主机名在阻止列表中,那么我将尝试在列表中获取第二个主机名并创建URL并执行它,因此基本上,在创建URL之前,首先要找到不在阻止列表中的主机名
  • 现在,假设我们选择了第一个不在阻止列表中的主机名并执行了URL,并且服务器以某种方式关闭或没有响应,那么我将执行列表中的第二个主机名并继续执行此操作,直到您获得成功的响应。但是要确保它们也不在阻止列表中,因此我们需要遵循上述要点。
  • 如果所有服务器都已关闭或在阻止列表中,则我可以简单地记录并返回服务不可用的错误。

下面是我的 DataClient 类,它将由客户调用,他们将对象传递给方法。DataKeygetData

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_UNAVAILABLEresponse == 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

您的担忧是有道理的。首先,让我们看看原始数据执行器是做什么的:

First, it is getting list of hostnames
Next, it loops through every hostnames that do the following things:
    It checks whether the hostname is valid to send request.
    If not valid: skip. 
    Else continue.
        Generate the URL based on hostname
        Send the request
        Translate the request response to domain response
        Handle exceptions
If the hostnames is empty, generate an empty response
Return response

现在,我们可以做些什么来遵循SRP?正如我所看到的,我们可以将这些操作分组到一些组中。我能看到的是,这些操作可以分为:

HostnameValidator:        checks whether the hostname is valid to send request
--------------
HostnameRequestSender:    Generate the URL
                          Send the request
--------------
HttpToDataResponse:       Translate the request response to domain response
--------------
HostnameExceptionHandler: Handle exceptions

也就是说,一种解耦操作并遵循 SRP 的方法。还有其他方法,例如简化操作:

First, it is getting list of hostnames
If the hostnames is empty, generate an empty response
Next, it loops through every hostnames that do the following things:
    It checks whether the hostname is valid to send request
    If not valid: remove hostname
    Else: Generate the URL based on hostname
Next, it loops through every valid hostnames that do the following things:
    Send the request
    Translate the request response to domain response
    Handle exceptions
Return response

然后它也可以分为:

HostnameValidator:        checks whether the hostname is valid to send request
--------------
ValidHostnameData:        Getting list of hostnames
                          Loops through every hostnames that do the following things:
                              Checks whether the hostname is valid to send request
                              If not valid: remove hostname
                              Else: Generate the URL based on hostname
--------------
HostnameRequestSender:    Send the request
--------------
HttpToDataResponse:       Translate the request response to domain response
--------------
HostnameExceptionHandler: Handle exceptions

当然,还有其他方法可以做到这一点。我将实现细节留空,因为有很多方法可以实现它。


答案 2