如何在Reactor中进行分页?

Bab*_*nam 2 java spring project-reactor spring-webflux spring-webclient

我向第三方 Web 服务重复发出分页 WebClient 请求。我现在的实现可以工作,但正在阻塞

到目前为止我的实现:

var elementsPerPage = 10;
Flux
    .generate(
        () -> 0,
        (pageIndex, emitter) -> {
            BlahServiceResponse blahServiceResponse =
                webClient
                    .get()
                    .uri("/blah?pageIndex={pageIndex}", pageIndex)
                    .retrieve()
                    .bodyToMono(BlahServiceResponse.class)
                    .block(); // Yuck!!!
            if (blahServiceResponse.getStudents().size() > 0) {
                emitter.next(blahServiceResponse);
            } else {
                emitter.complete();
            }
            return pageIndex + elementsPerPage;
        }
    )
    .subscribe(System.out::println); // Replace me with actual logic
Run Code Online (Sandbox Code Playgroud)

出于可以理解的原因,如果上面的代码更改为以下内容,则会引发“IllegalStateException:生成器没有调用任何 SynchronousSink 方法”异常:

webClient
    .get()
    ...
    .bodyToMono(BlahServiceResponse.class)
    .subscribe(emitter::next);
Run Code Online (Sandbox Code Playgroud)

所以我开始寻找一个异步接收器并意识到它是 Flux|MonoSink。但据我所知,Flux 中没有构建器方法支持使用 Flux|MonoSink 生成有状态元素。

我是否遗漏了一些东西,是否有更优雅的方法?

Mar*_*nyi 5

静态分页

如果您提前知道页面索引并且有生成它的规则。

var elementsPerPage = 10;

Flux.generate(
        () -> 0,
        (pageIndex, emitter) -> {
            if (pageIndex < 30) {
                emitter.next(pageIndex);
            } else {
                emitter.complete();
            }
            return pageIndex + elementsPerPage;
        })
        .flatMap(pageIndex -> webClient
                .get()
                .uri("/blah?pageIndex={pageIndex}", pageIndex)
                .retrieve()
                .bodyToMono(BlahServiceResponse.class))
        .subscribe(System.out::println);
Run Code Online (Sandbox Code Playgroud)

动态分页

如果下一页索引依赖于最后查询的页面。

public static void main(String[] args) {
    var elementsPerPage = 10;

    callWithPageIndex(0)
            .expand(pagedResponse -> {
                if (pagedResponse.getResponse().isEmpty()) {
                    return Mono.empty();
                } else {
                    return callWithPageIndex(pagedResponse.getPageIndex() + elementsPerPage);
                }
            })
            .subscribe(System.out::println);
}

private static Mono<PagedResponse<BlahServiceResponse>> callWithPageIndex(Integer pageIndex) {
    return webClient
            .get()
            .uri("/blah?pageIndex={pageIndex}", pageIndex)
            .retrieve()
            .bodyToMono(BlahServiceResponse.class)
            .map(response -> new PagedResponse<>(pageIndex, response));
}

@lombok.Value
static class PagedResponse<T> {
    int pageIndex;
    T response;
}
Run Code Online (Sandbox Code Playgroud)