[英]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提供了一個返回Future
的AsyncRestTemplate
的事實正是您要使用的。
因此無法在我的手機上寫下完整的代碼,但這大致就是您想要做的。
@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.