jdk9 响应式流

引用: https://www.cnblogs.com/IcanFixIt/p/7245377.html
引用: https://juejin.im/post/5e59bc266fb9a07ca90c2ecb

什么是流

流是由生产者生产并由一个或多个消费者消费的元素(item)的序列。

这种生产者-消费者模型也被称为source/sink模型发布者-订阅者(publisher-subscriber )模型

流的处理机制

有几种流处理机制,其中pull模型和push模型是最常见的。

  • push模型中,发布者将元素推送给订阅者。
  • pull模式中,订阅者主动拉取发布者生产的元素。

发布者和订阅者都以同样的速率工作,这是一个理想的情况,这些模式非常有效。 但是,如果他们不按同样的速率工作,这种情况下涉及的问题以及对应的解决办法。
当发布者比订阅者快的时候

  • 一种解决方案是:后者必须有一个无边界缓冲区来保存快速传入的元素,或者它必须丢弃它无法处理的元素
  • 另一个解决方案是:使用一种称为背压(backpressure )的策略。

背压(back pressure)
 如果生产者发出的信息比消费者能够处理消息最大量还要多,消费者可能会被迫一直在抓消息,耗费越来越多的资源,埋下潜在的崩溃风险。为了防止这一点,需要有一种机制使消费者可以通知生产者,降低消息的生成速度。生产者可以采用多种策略来实现这一要求,这种机制称为背压
 简单来说:

  • 背压指的发布者和订阅者之间的互动
  • 订阅者可以告诉发布者自己需要多少数据,可以调节数据流量,不会导致发布者发布数据过多导致数据浪费或压垮订阅者

响应式流

Reactive Stream 概念
 Reactive Stream (响应式流/反应流) 是JDK9引入的一套标准,是一套基于发布/订阅模式的数据处理规范。响应式流从2013年开始,作为提供非阻塞背压的异步流处理标准的倡议。它旨在解决处理元素流的问题——如何将元素流从发布者传递到订阅者,而不需要发布者阻塞,或订阅者有无限制的缓冲区丢弃
 更确切地说,Reactive流目的是“找到最小的一组接口,方法和协议,用来描述必要的操作和实体以实现这样的目标:以非阻塞背压方式实现数据的异步流”。

反应式流 (Reactive Stream) 规范诞生

Subscription 接口定义了连接发布者和订阅者的方法
Publisher<T> 接口定义了发布者的方法
Subscriber<T> 接口定义了订阅者的方法
Processor<T,R> 接口定义了处理器

Reactive Stream 规范诞生后,RxJava 从 RxJava 2 开始实现 Reactive Stream 规范 , 同时 Spring提供的Reactor 框架(WebFlux的基础) 等也相继实现了 Reactive Stream 规范

订阅者和发布者之间的交互
[图片]

  • 发布者(publisher)是潜在无限数量的有序元素的生产者。 它根据收到的要求向当前订阅者发布(或发送)元素。

  • 订阅者(subscriber)从发布者那里订阅并接收元素。

    • 发布者向订阅者发送订阅令牌(subscription token)。
    • 订阅者使用订阅令牌从发布者哪里请求多个元素。
    • 当元素准备就绪时,发布者向订阅者发送多个或更少的元素。
    • 订阅者使用订阅令牌来调整请求数据的个数
  • 订阅(subscription)表示订阅者订阅的一个发布者的令牌。 当订阅请求成功时,发布者将其传递给订阅者。 订阅者使用订阅令牌与发布者进行交互,例如请求更多的元素(request)或取消订阅(cancel)。
    -处理者(processor)充当订阅者和发布者的处理阶段。 Processor接口继承了Publisher和Subscriber接口。 它用于转换发布-订阅者管道中的元素。 Processor<T,R> 订阅类型T的数据元素,接收并转换为类型R的数据,并发布变换后的数据。 下图显示了处理者在发布者——订阅和管道中作为转换器的作用。 可以拥有多个处理者。
    在这里插入图片描述

JDK9 Flow API

JDK9中Reactive Stream的实现规范 通常被称为 Flow API ,通过java.util.concurrent.Flow 来实现响应式流.

APIs

//package java.util.concurrent;

public final class Flow {

    @FunctionalInterface
    public static interface Publisher<T> {       
     public void subscribe(Subscriber<? super T> subscriber);
    }

    public static interface Subscriber<T> {
       //发布者subscribe订阅者时触发, 发送subscription
        public void onSubscribe(Subscription subscription);
		
		//订阅者接收消息处理逻辑
        public void onNext(T item);
	   
        public void onError(Throwable throwable);
	
		//发布者 close时触发
        public void onComplete();
    }

 
    public static interface Subscription {
    	//订阅者向发布者请求数据
        public void request(long n);
        public void cancel();
    }
	
	//转换器 分别继承了 Subscriber和Publisher接口
    public static interface Processor<T,R> extends Subscriber<T>, Publisher<R> {
    }
	
	//默认缓冲池大小
  	static final int DEFAULT_BUFFER_SIZE = 256;
    public static int defaultBufferSize() {
        return DEFAULT_BUFFER_SIZE;
    }
}

demo1

private static void test01() throws InterruptedException {
        Random random = new Random();
        //1.自定义发布者, 发布数据类型为Integer
        //使用jdk9自带的SubmissionPublisher,它实现了Publisher接口
        SubmissionPublisher<Integer> publisher = new SubmissionPublisher<>();

        //2.定义订阅者
        Flow.Subscriber<Integer> subscriber = new Flow.Subscriber<Integer>() {
            private Flow.Subscription subscription;

            @Override
            public void onSubscribe(Flow.Subscription sb) {
                //建立订阅关系
                this.subscription = sb;

                //2. 请求数据 ----
                this.subscription.request(1);
            }

            @Override
            public void onNext(Integer item) {
                System.out.println("接收到数据:" + item);

                this.subscription.request(2);
                //已经达到了目标,调用cancel告诉发布者,不在接收数据
//                this.subscription.cancel();
            }

            @Override
            public void onError(Throwable throwable) {
                //出现异常
                throwable.printStackTrace();

                this.subscription.cancel();
            }

            @Override
            public void onComplete() {
                //在publisher.close();时触发..
                System.out.println("数据处理完了");
            }
        };

        // 3.发布者和订阅者 建立订阅关系
        publisher.subscribe(subscriber);

        //4.生产数据,并发布
        publisher.submit(100);
        publisher.submit(200);
        publisher.submit(300);
        publisher.submit(400);

        //5.结束后,关闭发布者
        publisher.close();

        Thread.currentThread().join(1000);
    }

demo2–Processor
Proceeor主要的作用是用具加工、处理发布者发布的消息。
下例:用来展示Processor过滤掉发布者生产的小于200的消息,并且对消息进行转换Integer --> String.
MyProcessor

static class MyProcessor extends SubmissionPublisher<String> implements Flow.Processor<Integer, String> {
        private Flow.Subscription subscription;
        @Override
        public void onSubscribe(Flow.Subscription sb) {
            //建立订阅关系
            this.subscription = sb;
            //2. 请求数据 ----
            this.subscription.request(1);
        }

        @Override
        public void onNext(Integer item) {
            System.out.println("processor接收到数据:" + item);
			
			//过滤掉小于200的数据, 
            if(item > 200) {
            	//将消息转换String
                this.submit("String MSG-" + item);
            }

            this.subscription.request(1);
        }

        @Override
        public void onError(Throwable throwable) {
            //出现异常
            throwable.printStackTrace();
            this.subscription.cancel();
        }

        @Override
        public void onComplete() {
            System.out.println("Processor:数据处理完了");
        }
    }

test-case

    private static void testProcessor() throws InterruptedException {
        Random random = new Random();
        //1.自定义发布者, 发布数据类型为Integer
        SubmissionPublisher<Integer> publisher = new SubmissionPublisher<>();

        // 2.定义处理器Processor
        MyProcessor processor = new MyProcessor();

        // 3.发布者和Processor建立关系
        publisher.subscribe(processor);

        //4.定义订阅者
        Flow.Subscriber<String> subscriber = new Flow.Subscriber<String>() {
            private Flow.Subscription subscription;

            @Override
            public void onSubscribe(Flow.Subscription sb) {
                //建立订阅关系
                this.subscription = sb;

                //2. 请求数据 ----
                this.subscription.request(1);
            }

            @Override
            public void onNext(String item) {
                System.out.println("=============subscriber接收到数据:" + item);

                this.subscription.request(2);
                //已经达到了目标,调用cancel告诉发布者,不在接收数据
//                this.subscription.cancel();
            }

            @Override
            public void onError(Throwable throwable) {
                //出现异常
                throwable.printStackTrace();

                this.subscription.cancel();
            }

            @Override
            public void onComplete() {
                //在publisher.close();时触发..
                System.out.println("数据处理完了");
            }
        };

        //5. Processor同订阅者建立关系
        processor.subscribe(subscriber);

        //4.生产数据,并发布
        publisher.submit(100);
        publisher.submit(200);
        publisher.submit(300);
        publisher.submit(400);

        //5.结束后,关闭发布者
        publisher.close();

        Thread.currentThread().join(1000);
    }

执行结果:

processor接收到数据:100
processor接收到数据:200
processor接收到数据:300
processor接收到数据:400
Processor数据处理完了
=============subscriber接收到数据:String MSG-300
=============subscriber接收到数据:String MSG-400

demo3–背压测试
实现一个场景发布者产生消息的速率 > 订阅者消费消息的速率 && 设置缓冲池大小为2

private static void testBackup() throws InterruptedException {
        //覆盖默认参数,修改缓冲池最大大小为2
        SubmissionPublisher<Integer> publisher = new SubmissionPublisher<>(ForkJoinPool.commonPool(),2){
            @Override
            public int submit(Integer item) {
                System.out.println("生产数据:"+item);
               return  super.submit(item);
            }
        };
        Flow.Subscriber<Integer> subscriber = new Flow.Subscriber<Integer>() {
            private Flow.Subscription subscription;
            @Override
            public void onSubscribe(Flow.Subscription sb) {
                this.subscription = sb;
                this.subscription.request(1);
            }

            @Override
            public void onNext(Integer item) {
                System.out.println("----------------接收到数据:" + item);
                
                //为了模拟耗时操作: 这里sleep..
                try {
                    TimeUnit.MILLISECONDS.sleep(50L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                this.subscription.request(1);
            }

            @Override
            public void onError(Throwable throwable) {
                throwable.printStackTrace();
                this.subscription.cancel();
            }

            @Override
            public void onComplete() {
                System.out.println("数据处理完了");
            }
        };

        // 3.发布者和订阅者 建立订阅关系
        publisher.subscribe(subscriber);

        //4.生产数据,并发布
        Random random = new Random();
        IntStream.rangeClosed(1,1000).forEach(data -> {
            publisher.submit(data);
        });

        //5.结束后,关闭发布者
        publisher.close();

        Thread.sleep(100000L);
    }

执行结果

生产数据:1
生产数据:2
----------------接收到数据:1
生产数据:3
生产数据:4
----------------接收到数据:2
生产数据:5
----------------接收到数据:3
生产数据:6
生产数据:7
----------------接收到数据:4
----------------接收到数据:5

可见,只要缓冲池中有超过2个未处理的数据,消息发布者就会感受到订阅方的压力,而减缓产生数据的速率。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值