2015-04-20 14 views
14

Tôi đang sử dụng RestTemplate làm HttpClient để thực thi URL và máy chủ sẽ trả lại chuỗi json làm phản hồi. Khách hàng sẽ gọi cho thư viện này bằng cách đi qua đối tượng DataKeyuserId trong đó.Làm thế nào để làm theo nguyên tắc Trách nhiệm duy nhất trong thực thi HttpClient của tôi?

  • Sử dụng cho userId, tôi sẽ tìm hiểu các máy mà tôi có thể nhấn để lấy dữ liệu và sau đó lưu trữ các loại máy một LinkedList, để tôi có thể thực hiện chúng liên tục là gì.
  • Sau đó tôi sẽ kiểm tra xem tên máy chủ đầu tiên có nằm trong danh sách chặn hay không. Nếu nó không có trong danh sách chặn, sau đó tôi sẽ tạo một URL với tên máy chủ đầu tiên trong danh sách và thực hiện nó và nếu phản hồi thành công thì trả về phản hồi. Nhưng giả sử rằng tên máy chủ đầu tiên nằm trong danh sách chặn, thì tôi sẽ cố gắng lấy tên máy chủ thứ hai trong danh sách và tạo url và thực thi nó, vì vậy về cơ bản, đầu tiên tìm thấy tên máy chủ không có trong danh sách chặn trước khi thực hiện URL.
  • Bây giờ, giả sử nếu chúng ta chọn tên máy chủ đầu tiên không có trong danh sách khối và thực thi URL và máy chủ nào đó không hoạt động, thì tôi sẽ thực thi tên máy chủ thứ hai trong danh sách và tiếp tục thực hiện điều này cho đến khi bạn nhận được phản ứng thành công. Nhưng hãy đảm bảo rằng chúng cũng không nằm trong danh sách chặn vì vậy chúng tôi cần theo dõi ở trên.
  • Nếu tất cả các máy chủ đều nằm trong danh sách chặn, thì tôi có thể chỉ cần đăng nhập và trả lại lỗi mà dịch vụ không khả dụng.

Dưới đây là lớp DataClient sẽ được khách hàng gọi và họ sẽ chuyển DataKey đối tượng đến phương thức 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; 
    } 
} 

Dưới đây là lớp DataExecutorTask tôi:

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

danh sách chặn tôi giữ-on nhận được cập nhật từ một sợi nền mỗi 1 phút. Nếu bất kỳ máy chủ là xuống và không đáp ứng, sau đó tôi cần phải chặn máy chủ bằng cách sử dụng này -

ShardMappings.blockHost(hostname); 

Và để kiểm tra xem bất kỳ máy chủ là trong danh sách chặn hay không, tôi sử dụng này -

ShardMappings.isBlockHost(hostname); 

Tôi đang trả lại SERVICE_UNAVAILABLE nếu máy chủ không hoạt động hoặc trong danh sách chặn, trên cơ sở response == null kiểm tra, không chắc chắn đó có phải là phương pháp phù hợp hay không.

Tôi không tuân thủ Nguyên tắc về trách nhiệm duy nhất ở đây Tôi đoán là tất cả. Bất cứ ai cũng có thể cung cấp một ví dụ cách tốt nhất để sử dụng nguyên tắc SRP ở đây là gì.

Sau khi suy nghĩ rất nhiều, tôi đã có thể trích xuất các host lớp như đưa ra dưới đây nhưng không chắc chắn cách tốt nhất để sử dụng trong lớp học trênDataExecutorTaskcủa tôi là gì.

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

Offtopic: Ưu điểm của việc sử dụng mẫu trong Spring là gì? –

+1

@PantaRhei RestTemplate là một trình bao bọc tiện lợi hơn HttpClient. Nó đơn giản hóa và tóm tắt một số trường hợp và mã sử dụng phổ biến nhất. – john

Trả lời

6

Mối quan tâm của bạn hợp lệ. Trước tiên, hãy xem những gì người thực hiện dữ liệu gốc thực hiện:

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 

Bây giờ, chúng ta có thể làm gì để theo dõi SRP? Như tôi thấy, chúng ta có thể nhóm các hoạt động đó thành một số nhóm. Những gì tôi có thể thấy là, các hoạt động này có thể được chia thành:

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 

Đó là, một cách để loại bỏ hoạt động của bạn và theo dõi SRP. Ngoài ra còn có cách tiếp cận khác, ví dụ để đơn giản hóa hoạt động của bạn:

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 

Sau đó, nó cũng có thể được chia thành:

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 

Tất nhiên cũng có những cách khác để làm điều đó. Và tôi để trống các chi tiết thực hiện vì có nhiều cách để thực hiện nó.

Các vấn đề liên quan