简体   繁体   English

Spring WebFlux和WebClient进行许多API调用

[英]Spring WebFlux & WebClient make many API calls

Recently I asked question Spring WebFlux create pool of no-blocking threads 最近我问了一个问题: Spring WebFlux创建无阻塞线程池

I got the answer, read link provided, but still don't understand the right way of doing such stuff. 我得到了答案,提供了阅读的链接,但仍然不了解正确的方法。

I use Spring WebFlux (WebClient) to write my REST service. 我使用Spring WebFlux(WebClient)编写我的REST服务。 For each incoming request I make several hundred requests to another REST service, so to make them as fast as possible I want to use no-blocking threads. 对于每个传入的请求,我都会向另一个REST服务发出数百个请求,因此为了使它们尽可能快,我想使用无阻塞线程。

Let I got request from client and I have to make 600 API calls: 让我收到客户端的请求,我必须进行600个API调用:

List<String> urls = Arrays.asList("www.example-rest.com/url1", "www.example-rest.com/url2", ..., "www.example-rest.com/url600");

  • I want to make them in parallel way and using no-blocking threads (like eventlet in Python) 我想以并行方式并使用无阻塞线程(例如Python中的eventlet)来制作它们
  • I want to create separate shared worker pool with such threads in order not to make one for each incoming request. 我想使用此类线程创建单独的共享工作池,以免为每个传入请求创建一个共享工作池。

Here is the documentation about schedulers http://projectreactor.io/docs/core/release/reference/#schedulers 这是有关调度程序的文档http://projectreactor.io/docs/core/release/reference/#schedulers

I found there information about elastic thread pool : 我找到了有关弹性线程池的信息

An elastic thread pool (Schedulers.elastic()). 弹性线程池 (Schedulers.elastic())。 It creates new worker pools as needed, and reuse idle ones. 它根据需要创建新的工作池,并重用空闲的工作池。 This is a good choice for I/O blocking work for instance. 例如,这是进行I / O阻止工作的不错选择。 Schedulers.elastic() is a handy way to give a blocking process its own thread, so that it does not tie up other resources. Schedulers.elastic()是一种为阻塞进程提供自己的线程的便捷方法,这样它就不会占用其他资源。

But I can't create new worker pool for each request, and thread inside that pool still works in blocking way. 但是我无法为每个请求创建新的工作池,并且该池中的线程仍然以阻塞的方式工作。

If anybody did similar task with Spring WebClient please provide an example, explain what is the right way. 如果有人用Spring WebClient做过类似的任务,请提供一个例子,解释什么是正确的方法。

I've done something similar... My goal was to create a method with a signature like this: 我做过类似的事情...我的目标是创建一个带有如下签名的方法:

Flux<BasicIssue> getIssues(WebClient webClient);

Because the website I was calling provided only a paged interface, I needed to feed the results of multiple REST calls into a single Flux. 因为我呼叫的网站仅提供页面界面,所以我需要将多个REST呼叫的结果反馈到单个Flux中。 Below is my implementation. 下面是我的实现。 Please note my use of CachedThreadPool. 请注意我对CachedThreadPool的使用。

Flux<BasicIssue> getIssues(WebClient webClient) {
    return Flux.generate(
        () -> new IssuesState(webClient),
        (state, sink) -> {
            BasicIssue ret = state.getNext();
            if (ret == null) {
                sink.complete();
            } else {
                sink.next(ret);
            }
            return state;
        }
}


class IssuesState {

    private final AtomicBoolean isDone = new AtomicBoolean(false);
    private final AtomicInteger threadCount = new AtomicInteger(1);
    private final Executor executor = Executors.newCachedThreadPool();
    private final LinkedBlockingQueue<BasicIssue> issueQueue = new LinkedBlockingQueue();

    public IssuesState(WebClient webClient) {
        executor.execute(() -> getNextBlock(webClient, 0));
    }

    private void getNextBlock(final WebClient webClient, final int startAt) {
        webClient
            .get()
            .uri(...)
            .header("Authorization", "Basic " + Base64Utils.encodeToString(("username:password".getBytes(UTF_8))))
            .accept(MediaType.APPLICATION_JSON)
            .retrieve()
            .bodyToMono(PageableIssue.class)
            .subscribe(pageableIssue -> {
                int maxResults = pageableIssue.getMaxResults();
                int total = pageableIssue.getTotal();
                if (startAt == 0) {
                    for (int i = startAt + maxResults; i < total; i += maxResults) {
                        threadCount.incrementAndGet();
                        final int x = i;
                        executor.execute(() -> getNextBlock(webClient, x));
                    }
                }
                synchronized (issueQueue) {
                    for (BasicIssue issue : pageableIssue.getIssues()) {
                        issueQueue.add(issue);
                    }

                    if (threadCount.decrementAndGet() == 0) {
                        isDone.set(true);
                    }
                }
            });
    }

    public BasicIssue getNext() {
        synchronized (issueQueue) {
            if (isDone.get() && issueQueue.isEmpty()) {
                return null;
            }
        }
        try {
            return issueQueue.take();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }
}

Using the above method.. 使用以上方法

getIssues(webClient)
    .subscribe(basicIssue -> System.out.println(basicIssue.getName());

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM