Sto usando RestTemplate
come il mio HttpClient
per eseguire l'URL e il server restituirà una stringa json come risposta. Il cliente chiamerà questa libreria passando l'oggetto DataKey
che contiene userId
.Come seguire il principio di Responsabilità Unica nel mio esecutore HttpClient?
- Utilizzando la data
userId
, io scoprire quali sono le macchine che posso colpire per ottenere i dati e quindi memorizzare quelle macchine in unLinkedList
, in modo che io possa eseguire in modo sequenziale. - Dopodiché controllerò se il primo hostname è in block list o no. Se non è presente nell'elenco di blocco, creerò un URL con il primo nome host nell'elenco e lo eseguirò e se la risposta ha esito positivo, restituire la risposta. Ma diciamo che se quel primo hostname è nella block list, allora cercherò di ottenere il secondo hostname nella lista e fare l'url ed eseguirlo, quindi in pratica, prima trovare il nome host che non è nella block list prima di fare l'URL.
- Ora, diciamo se abbiamo selezionato il primo hostname che non era nella lista dei blocchi ed eseguito l'URL e in qualche modo il server era inattivo o non rispondeva, quindi eseguirò il secondo hostname nella lista e continuerò a farlo fino ad ottenere un risposta di successo. Ma assicurati che non fossero nella lista dei blocchi, quindi dobbiamo seguire il punto precedente.
- Se tutti i server sono inattivi o in elenco di blocco, quindi posso semplicemente accedere e restituire l'errore che il servizio non è disponibile.
Qui di seguito è la mia classe DataClient che sarà chiamato dal cliente e passeranno DataKey
oggetto da getData
metodo.
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;
}
}
Qui di seguito è la mia classe 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;
}
}
mio elenco di blocco mantiene-on sempre aggiornato da un altro thread in background ogni 1 minuto. Se qualsiasi server è giù e non risponde, poi ho bisogno di bloccare quel server utilizzando questo -
ShardMappings.blockHost(hostname);
E per verificare se qualsiasi server è nella lista di blocco o no, io uso questo -
ShardMappings.isBlockHost(hostname);
Sto restituendo SERVICE_UNAVAILABLE
se i server sono inattivi o in elenco di blocco, sulla base del controllo response == null
, non sono sicuro se sia un approccio corretto oppure no.
Non sto seguente principio di responsabilità singola qui Credo che a tutti. Qualcuno può fornire un esempio su quale sia il modo migliore di utilizzare il principio SRP qui.
Dopo averci pensato molto, sono stato in grado di estrarre classe padroni di casa come indicato di seguito, ma non è sicuro qual è il modo migliore per utilizzare questo nella mia classe di sopraDataExecutorTask
.
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();
}
}
Offtopic: Qual è il vantaggio dell'utilizzo di modelli in primavera? –
@PantaRhei RestTemplate è un comodo wrapper su HttpClient. Semplifica e astrae solo alcuni dei più comuni casi d'uso e codice. – john