如何在我的 HttpClient 执行器中遵循单一职责原则?
How to follow Single Responsibility principle in my HttpClient executor?
我使用 RestTemplate
作为我的 HttpClient
来执行 URL 并且服务器将 return 返回一个 json 字符串作为响应。客户将通过传递其中包含 userId
的 DataKey
对象来调用此库。
- 使用给定的
userId
,我将找出我可以访问哪些机器来获取数据,然后将这些机器存储在LinkedList
中,以便我可以顺序执行它们.
- 之后我会检查第一个主机名是否在阻止列表中。如果它不在阻止列表中,那么我将使用列表中的第一个主机名创建一个 URL 并执行它,如果响应成功,则 return 返回响应。但是假设如果第一个主机名在阻止列表中,那么我将尝试获取列表中的第二个主机名并生成 url 并执行它,所以基本上,在创建 URL.
之前首先找到不在阻止列表中的主机名
- 现在,假设我们选择了不在阻止列表中的第一个主机名并执行了 URL 并且服务器以某种方式关闭或没有响应,那么我将执行列表中的第二个主机名并保留这样做直到你得到一个成功的回应。 但请确保它们也不在阻止列表中,因此我们需要遵循上述要点。
- 如果所有服务器都关闭或在阻止列表中,那么我可以简单地记录并 return 服务不可用的错误。
下面是我的 DataClient class,它将被客户调用,他们会将 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 class:
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);
我正在 returning SERVICE_UNAVAILABLE
如果服务器已关闭或在阻止列表中,基于 response == null
检查, 不确定这是否是一个正确的方法。
我想我根本没有遵循单一职责原则。
任何人都可以提供一个例子,在这里使用 SRP 原则的最佳方法是什么。
经过深思熟虑,我能够像下面给出的那样提取主机 class 但不确定 在我上面使用它的最佳方法是什么 DataExecutorTask
class.
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();
}
}
您的担心是有道理的。先来看看原始数据执行器是干什么的:
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
当然还有其他方法可以做到。我将实现细节留空,因为有很多实现方法。
我使用 RestTemplate
作为我的 HttpClient
来执行 URL 并且服务器将 return 返回一个 json 字符串作为响应。客户将通过传递其中包含 userId
的 DataKey
对象来调用此库。
- 使用给定的
userId
,我将找出我可以访问哪些机器来获取数据,然后将这些机器存储在LinkedList
中,以便我可以顺序执行它们. - 之后我会检查第一个主机名是否在阻止列表中。如果它不在阻止列表中,那么我将使用列表中的第一个主机名创建一个 URL 并执行它,如果响应成功,则 return 返回响应。但是假设如果第一个主机名在阻止列表中,那么我将尝试获取列表中的第二个主机名并生成 url 并执行它,所以基本上,在创建 URL. 之前首先找到不在阻止列表中的主机名
- 现在,假设我们选择了不在阻止列表中的第一个主机名并执行了 URL 并且服务器以某种方式关闭或没有响应,那么我将执行列表中的第二个主机名并保留这样做直到你得到一个成功的回应。 但请确保它们也不在阻止列表中,因此我们需要遵循上述要点。
- 如果所有服务器都关闭或在阻止列表中,那么我可以简单地记录并 return 服务不可用的错误。
下面是我的 DataClient class,它将被客户调用,他们会将 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 class:
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);
我正在 returning SERVICE_UNAVAILABLE
如果服务器已关闭或在阻止列表中,基于 response == null
检查, 不确定这是否是一个正确的方法。
我想我根本没有遵循单一职责原则。 任何人都可以提供一个例子,在这里使用 SRP 原则的最佳方法是什么。
经过深思熟虑,我能够像下面给出的那样提取主机 class 但不确定 在我上面使用它的最佳方法是什么 DataExecutorTask
class.
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();
}
}
您的担心是有道理的。先来看看原始数据执行器是干什么的:
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
当然还有其他方法可以做到。我将实现细节留空,因为有很多实现方法。