У меня есть веб-сервис Vert.x, который должен выполнить серию постраничных вызовов внешнего API. Внешний сервис реализует разбиение на страницы путем включения в каждый ответ поля «следующее», которое представляет собой прямую ссылку на следующую страницу данных, а также подсчета общего количества страниц, необходимых для извлечения всех данных. Вот пример ответа:
"pagination": {
"count": 1000,
"totalPages": 112,
"next": "https://some-host.com?next=some-long-alphanumeric-hash"
},
"data": [ ... ]
После выполнения первого вызова API я знаю общее количество последующих вызовов (в данном примере 111), а также URL-адрес для получения следующей страницы данных. , В синхронной среде я мог бы просто сделать что-то вроде этого:
Collection aggregatedResults;
int count = 0;
String nextUrl = "";
while (count <= total pages) {
make next request
add the chunk of data from this response to the collection
store the next URL in local variable
increment count
}
Моя стратегия с Vertx состоит в том, чтобы использовать Future
s для представления результата отдельных вызовов, а затем объединить их в цепочку вместе с CompositeFuture.all()
, Это примерно то, что у меня есть (некоторый код опущен для экономии места):
private String nextUrl; // global String
doFirstCall(client).setHandler(async -> {
if (async.failed()) {
// blah
} else {
Response response = async.result();
int totalPages = response.getTotalPages();
next = response.getNext();
List<Future> paginatedFutures = IntStream
.range(0, totalPages - 1)
.mapToObj(i -> {
Promise<Response> promise = Promise.promise();
doIndividualPaginatedCall(client, next)
.setHandler(call -> {
if (call.succeeded()) {
Response chunk = call.result();
next = chunk.getNext(); // store the next URL in global string so it can be accessed within the loop
promise.complete(chunk);
} else {
promise.fail(call.cause());
}
});
return promise.future();
})
.collect(Collectors.toList());
CompositeFuture.all(paginatedFutures).setHandler(all -> {
if (all.succeeded()) {
// Do something with the aggregated responses
}
});
}
});
Когда я запускаю этот код, первый вызов всегда выполняется успешно, и я успешно сохраняю следующий URL. Затем каждый последующий вызов нумерации страниц разбивается на тот же URL-адрес, который был получен при первом вызове, и я вижу журналы, подобные этому:
Call succeeded. i: 16, next: https://blah.com/blah?filter=next(DnF1ZXJ5VGhlbkZldGNoBQAAAAAAlMYVFjdaM2ducHBaVGJHeWV5ZjRzNGRQMXcAAAAAAJTGNhYzcWlRTDEyeVJZS05PeV84QkJlLTVnAAAAAACUxjYWa3UzUkx1MXZURG1Pc2E5WGt5RG9pdwAAAAAAlMY2FnY4TVhXajlqUmMtWEQwWU1naGZFN3cAAAAAAJTGVxZCWWFUV19XR1RXQ05DRkI0NGw4M0xB)
Call succeeded. i: 17, next: https://blah.com/blah?filter=next(DnF1ZXJ5VGhlbkZldGNoBQAAAAAAlMYVFjdaM2ducHBaVGJHeWV5ZjRzNGRQMXcAAAAAAJTGNhYzcWlRTDEyeVJZS05PeV84QkJlLTVnAAAAAACUxjYWa3UzUkx1MXZURG1Pc2E5WGt5RG9pdwAAAAAAlMY2FnY4TVhXajlqUmMtWEQwWU1naGZFN3cAAAAAAJTGVxZCWWFUV19XR1RXQ05DRkI0NGw4M0xB)
Call succeeded. i: 18, next: https://blah.com/blah?filter=next(DnF1ZXJ5VGhlbkZldGNoBQAAAAAAlMYVFjdaM2ducHBaVGJHeWV5ZjRzNGRQMXcAAAAAAJTGNhYzcWlRTDEyeVJZS05PeV84QkJlLTVnAAAAAACUxjYWa3UzUkx1MXZURG1Pc2E5WGt5RG9pdwAAAAAAlMY2FnY4TVhXajlqUmMtWEQwWU1naGZFN3cAAAAAAJTGVxZCWWFUV19XR1RXQ05DRkI0NGw4M0xB)
TLDR: как выполнить серию вызовов API с разбиением на страницы, где URL изменяется между каждым вызовом и не известен, пока не завершится предыдущий вызов? Я пытался использовать CompositeFuture.join
, но тот же эффект. Я знаю, что для последовательной компоновки вы должны использовать compose()
, но как мне составить неизвестное количество вызовов функций?