Project Reactor:下游很慢

时间:2019-07-23 16:25:57

标签: java reactive-programming project-reactor

据我所知,其余的下游需要在线程池中的线程上进行处理(我将其设置为1024)

这是我的代码。

Flux<String> ips =
        Flux.fromIterable(items).map(Item::getIp);
ips
        .publishOn(Schedulers.fromExecutor(Executors.newFixedThreadPool(1024)))
        .map(ip -> {
            try {
                Request request = new Request.Builder().url("https://" + ip + ":443").build();
                Response response = okHttpClient.newCall(request).execute();
                return response.code();
            } catch (Exception e) {
            }

            return -1;
        })
        .subscribe(System.out::println);

由于某些原因,此代码与以下代码相比非常慢:

appRules
        .stream()
        .parallel()
        .map(Item::getIp)
        .forEach(ip -> {
            try {
                Request request = new Request.Builder().url("https://" + ip + ":443").build();
                Response response = okHttpClient.newCall(request).execute();
                System.out.println(response.code());
            } catch (Exception e) {
            }

            System.out.println(-1);
        });

为什么?当您受IO限制时,同时处理项目流的正确方法是什么? (而不是CPU)

1 个答案:

答案 0 :(得分:2)

执行速度较慢的原因是,默认情况下Reactor管道执行是单线程的。因此,当您使用Flux.publishOn运算符时,您只是说要在给定线程池中的线程上执行这部分管道,但不会同时在单独的线程上执行每个项目。

实现并发的一种选择是使用parallel Flux,它创建了所谓的Rails,数据可以在其中并行流动,但主要用于CPU绑定操作。

更好的选择是将阻塞代码包装在Mono中,然后将其委托给专用的线程池,类似于您所做的事情,只是这次每个任务都将获得自己的线程:

private static void reactorProcess()
{
    ExecutorService executor = Executors.newFixedThreadPool(1024);

    Flux.range(1, 1024)
        .flatMap(a -> Mono.fromRunnable(() -> simulateHttpCall())
                          .subscribeOn(Schedulers.fromExecutor(executor)))
        .blockLast();

    executor.shutdown();
}

private static void simulateHttpCall()
{
    try
    {
        Thread.sleep(100);
        System.out.println(Thread.currentThread().getName() + ": " + ZonedDateTime.now());
    } catch (InterruptedException e)
    {
        e.printStackTrace();
    }
}

我还要指出,Java并行流不是这种处理的可行替代方案。默认情况下,它使用ForkJoinPool,它也用于CPU绑定操作,并且仅使用与计算机中具有的CPU内核数量一样多的线程。

除此之外,如果您想利用反应式编程的全部功能,则应考虑使用支持无阻塞IO的HTTP客户端,例如Spring的WebClient。通过使用非阻塞HTTP客户端,您不再需要担心定义线程池,因为不会阻塞任何线程,并且固定数量的线程将能够处理数千个并发请求。