简体   繁体   English

使用 Reactive Streams 的 Reactive Pull-Based BackPressure

[英]Reactive Pull-Based BackPressure using Reactive Streams

This is my understanding of the subject.这是我对这个问题的理解。

There is Publisher and subscriber.有发布者和订阅者。

The pseudo code for Publisher and Subscriber is something like,发布者和订阅者的伪代码类似于,

Publisher{
    Subscriber s;
    subscribe(Subscriber s){
        this.s = s;
        s.onSubscribe(new Subscription(){
            onRequest(int n){
                List<Message> messages = service.getMessages(n);
                s.onNext(messages);
            }

            onCancel(){
                s.onComplete();
            }
        });
    }
}

Subscriber{
    Subscription s;
    onSubscribe(Subscription s){
        this.s = s;
        s.request(5);
    }

    onNext(List<Message> messages){
        messages.stream().parallel().map(this::process).collect(toList());
        s.request(5);
    }

    onComplete(){}

    onError(e){}

    private boolean process(Message m){
        //process message and return true/false according to whether it passed/failed.
    }
}

I understood like, according to the ability of the application, the subscriber will call the request.我的理解是,根据应用程序的能力,订阅者会调用请求。 When the application is healthy, subscriber can process fast and request messages more time.当应用程序健康时,订阅者可以快速处理并请求消息更多时间。 If the application is under load, subscriber will request the next batch only after processing the current batch.如果应用程序负载不足,则订阅者只有在处理完当前批次后才会请求下一个批次。 If processing takes time, lesser no of requests for more messages.如果处理需要时间,则对更多消息的请求较少。 The flow of the messages will be according to the capability of the application.消息流将取决于应用程序的能力。

Is my understanding correct?我的理解正确吗?

How is it different from a simple loop?它与简单的循环有何不同?

while(true){
    List<Message> messages = service.getMessages(5);
    messages.stream().parallel().map(this:process).collect(toList());
}

In this case also, the next batch of messages are read only after processing the messages concurrently.在这种情况下,也只有在并发处理消息后才读取下一批消息。 In this case also, when the application is performing well, more messages will be read.在这种情况下,当应用程序运行良好时,将读取更多消息。 If slow messages will be read less frequently.如果较慢的消息将不那么频繁地被读取。

How are these 2 approaches different?这两种方法有何不同? Is the differences all about different types of Schedulers that are available?差异是否都与可用的不同类型的调度程序有关? I do not understand, what exactly is the advantage here.我不明白,这里到底有什么优势。

Update 1更新 1

Ok, I understand some advantages of the reactive pull based approach over simple loop.好的,我了解基于反应拉动的方法相对于简单循环的一些优势。

If a subscriber requests n items, is it necessary for the Publisher to call onNext() n times on the subscriber?如果订阅者请求 n 项,发布者是否需要对订阅者调用 onNext() n 次? Or would it also be fine, if the Publisher calls the Subscriber once with a list of n elements (Like in the previous snippet)?或者,如果发布者使用包含 n 个元素的列表调用订阅者一次(就像在前面的代码片段中一样),也可以吗? If n onNext() calls needs to be made, the subscriber is becoming little more complex.如果需要进行 n 个 onNext() 调用,则订阅者会变得稍微复杂一些。

Publisher{
    Subscriber s;
    subscribe(Subscriber s){
        this.s = s;
        s.onSubscribe(new Subscription(){
            request(int n){
                service.getMessagesAsyc(n, (List<Message> messages) -> messages.stream().forEach(s::onNext));
            }

            onCancel(){
                s.onComplete();
            }
        });
    }
}

Subscriber{
    Subscription s;
    COUNT = 5;
    volatile int i = COUNT;

    onSubscribe(Subscription s){
        this.s = s;
        s.request(COUNT);
    }

    onNext(Message message){
        CompletableFuture.runAsync(() -> process(message));
        requestMessagesIfNeeded();
    }

    private synchronized requestmessagesIfNeeded(){
        if(0 == i--){
            i = COUNT;
            s.request(COUNT);
        }
    }

    private boolean process(Message m){
        //process message and return true/false according to whether it passed/failed.
    }
}

If the subscriber can be passed a list of n messages, there are also few other advantages.如果可以向订阅者传递一个包含 n 条消息的列表,那么还有其他一些优势。 Suppose, the Subscriber needs to acknowledge only the successfully processed messages, it is very easy to do that in the first approach using a batch acknowledge API.假设,订阅者只需要确认成功处理的消息,使用批量确认 API 在第一种方法中很容易做到这一点。

    Map<Boolean, List<Message>> partitioned = messages.stream().parallel().collect(partitioningBy(this::process));
service.ackowledge(partitioned.get(true));
s.request(5);   

The second approach, where I get one message each on onNext(), it looks much more difficult to achieve it.第二种方法,我在 onNext() 上各收到一条消息,实现它看起来要困难得多。

onRequest(int n){ List<Message> messages = service.getMessages(n); s.onNext(messages); }

This is an incorrect view on Reactive Streams.这是对 Reactive Streams 的错误观点。 The request tells the Publisher it can do n onNext() calls.request告诉Publisher它可以执行n onNext()调用。 Often, that means the Subscription implementation representing an active connection between a source and consumer will handle the request call.通常,这意味着代表源和消费者之间活动连接的Subscription实现将处理request调用。

How is it different from a simple loop?它与简单的循环有何不同?

Reactive Streams allows non-blocking consumption; Reactive Streams 允许非阻塞消费; your example blocks a thread until getMessages() could retreive the List of messages.您的示例会阻塞一个线程,直到getMessages()可以检索消息List The nice property of working with Publisher s is that you don't have to consume events differently whether or not the source Publisher is blocking or non-blocking.使用Publisher好处是,无论源Publisher是阻塞还是非阻塞,您都不必以不同的方式使用事件。 It gives an uniform programming model for both cases.它为这两种情况提供了统一的编程模型。 If one day getMessages() receives a non-blocking variant, the downstream consuming its wrapper Publisher doesn't have to be changed.如果有一天getMessages()接收到一个非阻塞变体,则不必更改使用其包装器Publisher的下游。

Is the differences all about different types of Schedulers that are available?差异是否都与可用的不同类型的调度程序有关?

A Scheduler represents an abstraction over an asynchronous boundary: the thread that is producing events and the thread that is consuming those events. Scheduler代表异步边界上的抽象:产生事件的线程和消耗这些事件的线程。 The different Scheduler implementations manage their threads in a different manner, depending what the utilization of those threads are going to be.不同的Scheduler实现以不同的方式管理它们的线程,这取决于这些线程的利用率。 Some threads will execute computationally intensive tasks, some threads will block due to interactions with non-reactive sources.一些线程将执行计算密集型任务,一些线程将由于与非反应性源的交互而阻塞。 The Schedulers class gives descriptions what the various standard implementations are for. Schedulers类描述了各种标准实现的用途。

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

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