reactive stream & back pressure

149 阅读2分钟

reactive stream

响应式流(反应式流):异步、非阻塞。

是一种观察者模式的扩展。当一个数据发生变化时,另一个数据或者组件也跟着变化。是一种事件流的处理模式。

Reactive programming is a declarative programming paradigm(声明式编程范式) that is based on the idea of asynchronous event processing and data streams.

asynchronous stream processing with non-blocking back pressure

实现 reactive stream 可以通过 callback 的方式,但是会有“回调地狱”的问题。

由 4 个部分组成:

  • Publisher:数据生产者。有一个接口 subcribe(),用于注册订阅者。当 Publisher 生产了数据之后,通知这些订阅者。
  • Subscriber:数据消费者。有 4 个接口:
    • onSubcribe
    • onNext
    • onError
    • onComplete
  • Subscription:Publisher subcribe Subscriber之后创建的对象。在 Subscriber 中用于向 Publisher 请求新的数据(背压实现的核心)。
  • Processor:既是 Publisher 又是 Subscriber。比如一个 Subcriber 从 Publisher 获取数据之后,需要将这个数据处理之后,再通知其他 Subscriber。

image.png

java9的 Flow 完全实现了上面的 4 个接口。

java9的参考例子 java-9-reactive-streams

back pressure

为什么需要背压

生产者和消费者速度不匹配。主要为了防止生产者生产数据过快,消费者来不及消费而被打死。

背压的实现方式

  • 缓存
  • 丢弃

java9 flow

@Data
@NoArgsConstructor
@AllArgsConstructor
public class VideoFrame {
    private long number;
}
@Slf4j
public class VideoPlayer implements Flow.Subscriber<VideoFrame> {

    Flow.Subscription subscription = null;

    @Override
    public void onSubscribe(Flow.Subscription subscription) {
        this.subscription = subscription;
        subscription.request(1);
    }

    @Override
    public void onNext(VideoFrame item) {
        log.info("play #{}" , item.getNumber());
        subscription.request(10);
    }

    @Override
    public void onError(Throwable throwable) {
        log.error("There is an error in video streaming:{}" , throwable.getMessage());
    }

    @Override
    public void onComplete() {
        log.error("Video has ended");
    }
}
public class VideoStreamServer extends SubmissionPublisher<VideoFrame> {

    public VideoStreamServer() {
        // 这里配置了 buffer 的最大容量为 2
        // 提供的线程池:将生产的数据通知 subscriber 时使用的异步线程池
        // 如果某个 subscriber 来不及消费数据(没有及时 request 新数据),publisher不停生产数据时,为每个 subscriber 缓存的最大数
        super(Executors.newFixedThreadPool(2, new NamedThreadFactory("VideoStreamServerPublisher")), 2);
    }
}
public class ReactiveStreamLearn {

    @Test
    public void test() throws InterruptedException {
        VideoStreamServer streamServer = new VideoStreamServer();
        streamServer.subscribe(new VideoPlayer());
        streamServer.subscribe(new VideoPlayer2());

        // submit video frames
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
        AtomicLong frameNumber = new AtomicLong();
        executor.scheduleWithFixedDelay(() -> {
            streamServer.offer(new VideoFrame(frameNumber.getAndIncrement()), (subscriber, videoFrame) -> {
                subscriber.onError(new RuntimeException("Frame#" + videoFrame.getNumber()
                        + " dropped because of backpressure"));
                // 返回false会丢弃该数据;如果返回 true,会再重试 1 次。
                return false;
            });
            if (frameNumber.get() == 200) {
                streamServer.close();
                // streamServer.closeExceptionally(new RuntimeException("publisher closeExceptionally"));
            }
        }, 0, 1, TimeUnit.MILLISECONDS);

        sleep(1000);
    }

}

参考