簡體   English   中英

對同一Web服務進行多個異步調用的最佳方法

[英]Best way to make multiple asynchronous calls to same web service

我正在處理的應用程序將按以下順序使用2個REST Web服務:

1)計數記錄-了解特定時間范圍內的記錄數。

2)提取記錄-一旦有了記錄數量,就需要調用此服務。 但是此服務具有一次獲取1萬條記錄的閾值。 可以說,如果第一個服務在特定時間間隔內告訴我,它有100K記錄,那么考慮到它的閾值為一萬次,我需要以分頁方式調用第二個Web服務10次。

因此,如果我要進行10次同步調用,我的應用程序將太慢而無法響應。 因此,我需要一種進行異步調用的機制。

我在后端代碼中使用spring框架,並在Web服務調用中使用rest模板。 我正在尋找找到對上述POST Web服務進行異步調用的最佳方法

我進行了一些研究,發現異步方法非常有用,如下所示: https : //spring.io/guides/gs/async-method/

如果這是我正在尋找的正確方法,或者它們是進行異步調用的更好方法,您能否指導我? 尋找您的建議,謝謝!

@Journycorner鏈接的內容是一個不錯的開始,但是由於它僅發出一個請求,因此並不能真正顯示整個圖片。 Future絕對是正確的道路。 Spring 4提供了一個返回FutureAsyncRestTemplate的事實正是您要使用的。

因此無法在我的手機上寫下完整的代碼,但這大致就是您想要做的。

@Component
public class SampleAsyncService {
    private RestTemplate restTemplate;
    private AsyncRestTemplate asyncRestTemplate;

    @Value("${myapp.batchSize:1000}")
    private int batchSize;

    public SampleAsyncService(AsyncRestTemplate asyncRestTemplate, RestTemplate restTemplate) {
        this.asyncRestTemplate = asyncRestTemplate;
        this.restTemplate = restTemplate;
    }

    public List<Record> callForRecords() {
        ResponseEntity<Integer> response = restTemplate.getForEntity("http://localhost:8081/countService",
                Integer.class);
        int totalRecords = response.getBody().intValue();
        List<Future<ResponseEntity<List<Record>>>> futures = new ArrayList<Future<ResponseEntity<List<Record>>>>();
        for (int offset = 0; offset < totalRecords;) {
            ListenableFuture<ResponseEntity<List<Record>>> future = asyncRestTemplate.exchange(
                    "http://localhost:8081/records?startRow={}&endRow={}", HttpMethod.GET, null,
                    new ParameterizedTypeReference<List<Record>>() {
                    }, offset, batchSize);
            futures.add(future);
            offset = offset + batchSize;
        }

        int responses = 0;
        List<Record> fullListOfRecords = new ArrayList<Record>();
        while (responses < futures.size()) {
            for (Future<ResponseEntity<List<Record>>> future : futures) {
                if (future.isDone()) {
                    responses++;
                    try {
                        ResponseEntity<List<Record>> responseEntity = future.get();
                        fullListOfRecords.addAll(responseEntity.getBody());
                    } catch (InterruptedException | ExecutionException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

        return fullListOfRecords;
    }

    public class Record {
    }
}

*更新*創建了完整的代碼示例。

這只是對@shawn答案的改進。 有了前面提供的實現,有時由於以下原因而遇到問題:

while (responses < futures.size()) {        
   for (Future<ResponseEntity<List<Record>>> future : futures) {
       if (future.isDone()) {
            responses++;
            try {
                ResponseEntity<List<Record>> responseEntity = future.get();
                fullListOfRecords.addAll(responseEntity.getBody());
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
        }
    }
}

在這里,如果處理了6個線程,則有時同一線程在上面的塊中允許多次輸入,因此最終會有重復的記錄作為響應。 因此,為避免這種情況,我添加了回調塊以創建最終響應,以確保沒有重復的響應,盡管我們仍然需要while(responses <futures.size())循環帶有future.get()的塊來阻塞方法以返回final組合響應,直到處理完所有異步調用。

@Component
public class SampleAsyncService {
private RestTemplate restTemplate;
private AsyncRestTemplate asyncRestTemplate;

@Value("${myapp.batchSize:1000}")
private int batchSize;

public SampleAsyncService(AsyncRestTemplate asyncRestTemplate, RestTemplate restTemplate) {
    this.asyncRestTemplate = asyncRestTemplate;
    this.restTemplate = restTemplate;
}

public List<Record> callForRecords() {
    ResponseEntity<Integer> response = restTemplate.getForEntity("http://localhost:8081/countService",
            Integer.class);
    int totalRecords = response.getBody().intValue();
    List<Future<ResponseEntity<List<Record>>>> futures = new ArrayList<Future<ResponseEntity<List<Record>>>>();
    for (int offset = 0; offset < totalRecords;) {
        ListenableFuture<ResponseEntity<List<Record>>> future = asyncRestTemplate.exchange(
                "http://localhost:8081/records?startRow={}&endRow={}", HttpMethod.GET, null,
                new ParameterizedTypeReference<List<Record>>() {
                }, offset, batchSize);
        future.addCallback(
                new ListenableFutureCallback<ResponseEntity<ChatTranscript>>() {  
                    @Override  
                    public void onSuccess(ResponseEntity<ChatTranscript> response) {  
                        fullListOfRecords.addAll(responseEntity.getBody());
                        log.debug("Success: " + Thread.currentThread());  
                    }  

                    @Override  
                    public void onFailure(Throwable t) {
                        log.debug("Error: " + Thread.currentThread());
                    }  
                }
            );
        futures.add(future);
        offset = offset + batchSize;
    }

    int responses = 0;
    List<Record> fullListOfRecords = new ArrayList<Record>();
    while (responses < futures.size()) {
        for (Future<ResponseEntity<List<Record>>> future : futures) {
            if (future.isDone()) {
                responses++;
                try {
                    future.get();
                } catch (InterruptedException | ExecutionException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    return fullListOfRecords;
}

public class Record {
}

}

如果真的必須異步,那么使用Spring Implementation似乎是一個聰明的主意。

我假設您需要在一次執行中存儲所有10萬條記錄,以便將所有數據打包到一個文件中或一次性執行一些業務邏輯。 如果不是這種情況,明智的做法是重新考慮將所有數據加載到單個執行中的需求,從而使jvm的內存使用緊張或出現內存不足錯誤。

假設是前者,異步可能是執行並行線程並捕獲和整理每個線程的響應的一種選擇。 但是,您需要使用任務執行程序的“線程池大小”來保持要並行執行的線程數的上限。 限制線程大小的另一個原因是,避免通過過多的並行調用加載合作伙伴的其余Web服務。 最終,合作伙伴Web服務正在從數據庫加載數據,這將為並行查詢執行的某些限制提供最佳性能。 希望這可以幫助!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM