Мне нужно получить элементы со всех страниц API REST с возможностью просмотра страниц.Мне также нужно начать обработку элементов, как только они будут доступны, не нужно ждать загрузки всех страниц.Для этого я использую Spring WebFlux и его WebClient и хочу вернуть Flux<Item>
.Кроме того, используемый мной REST API ограничен по скорости, и каждый ответ на него содержит заголовки с подробной информацией о текущих пределах:
- Размер текущего окна
- Оставшееся время втекущее окно
- Запрос квоты в окне
- Запросы, оставленные в текущем окне
Ответ на запрос одной страницы выглядит следующим образом:
{
"data": [],
"meta": {
"pagination": {
"total": 10,
"current": 1
}
}
}
Массив данных содержит фактические элементы, в то время как метаобъект содержит информацию о разбиении на страницы.
Мое текущее решение сначала выполняет "фиктивный" запрос, просто чтобы получить общее количество страниц и ограничения скорости.
Mono<T> paginated = client.get()
.uri(uri)
.exchange()
.flatMap(response -> {
HttpHeaders headers = response.headers().asHttpHeaders();
Limits limits = new Limits();
limits.setWindowSize(headers.getFirst("X-Window-Size"));
limits.setWindowRemaining(headers.getFirst("X-Window-Remaining"));
limits.setRequestsQuota(headers.getFirst("X-Requests-Quota");
limits.setRequestsLeft(headers.getFirst("X-Requests-Remaining");
return response.bodyToMono(Paginated.class)
.map(paginated -> {
paginated.setLimits(limits);
return paginated;
});
});
После этого я посылаю поток, содержащий номера страниц, и для каждой страницы я делаю запрос REST API, каждый запрос достаточно задерживается, чтобы он не превысил предел, и возвращаю потокизвлеченные элементы:
return paginated.flatMapMany(paginated -> {
return Flux.range(1, paginated.getMeta().getPagination().getTotal())
.delayElements(Duration.ofMillis(paginated.getLimits().getWindowRemaining() / paginated.getLimits().getRequestsQuota()))
.flatMap(page -> {
return client.get()
.uri(pageUri)
.retrieve()
.bodyToMono(Item.class)
.flatMapMany(p -> Flux.fromIterable(p.getData()));
});
});
Это работает, но я не доволен этим, потому что:
- Это делает первоначальный "фиктивный" запрос для получения количества страниц, изатем повторяет тот же запросt для получения фактических данных.
- Он получает ограничения скорости только при первоначальном запросе и предполагает, что ограничения не изменятся (например, это единственный, использующий API) - что может быть неверно,в этом случае он получит ошибку, превышающую лимит.
Поэтому мой вопрос заключается в том, как выполнить его рефакторинг, чтобы он не нуждался в первоначальном запросе (а скорее получить ограничения, номера страниц и данныес первого запроса и продолжите работу на всех страницах, обновляя (и соблюдая) ограничения.