reactor3 源码分析

简介: Reactor 用于创建高效的响应式系统。Reactor 是一个用于JVM的完全非阻塞的响应式编程框架,具备高效的需求管理(即对 “背压(backpressure)”的控制)能力。它与 Java 8 函数式 API 直接集成。 本文讲述Reactor 的源码分析

本文需要知识点

lambda表达式

先写一个lambda表达式的小例子,注意,本文不是讲lambda表达式,所以这里仅仅是能够看到接下来的文章做铺垫而已。

@Test
public void test11() {
    Function<Integer, Integer> mapper = i -> i * i;//定义lambda表达式
    Integer result = mapper.apply(2);  //执行
    System.out.println(result);
}

输出结果是4
自定义lambda

//定义lambda函数式接口
@FunctionalInterface
public interface MyLambda {
    int add(int a, int b);
}

@Test
public void test12() {
    //使用自定义的lambda
    MyLambda myLambda = (a, b) -> a + b;
    result = myLambda.add(2 ,3);
    System.out.println(result);
}

输出结果5

reactor源码分析

分析之前,先看一下reactor提供的顶级抽奖接口
image

发布者
Publisher 是一个可以发送无限序列元素的发布者。他可以根据订阅者的要求来发布他们。

//发布者
public interface Publisher<T> {
    /**
     * 订阅方法
     * 请求发布者启动数据流
     * @param s   消费者
     */
    public void subscribe(Subscriber<? super T> s);
}

org.reactivestreams.Publisher#subscribe是一个工厂方法,允许调用多次,每次调用都会启动一个新的Subscription。每个Subscription只能被一个Subscriber使用;Subscriber消费者只能订阅一次Publisher。如果在执行过程中出错,则会发出error信号。

订阅者(消费者)

public interface Subscriber<T> {
    /**
     * 该方法在调用Publisher#subscribe(Subscriber)后执行
     * 在Subscription#request(long)调用之前不会有数据流消费
     * 如果订阅者想要消费更多的数据,需要调用Subscription#request(long)来请求数据。
     */
    public void onSubscribe(Subscription s);
    /**
     * 消费下一个消息
     * 在调用Subscription#request(long)方法时,Publisher会通过这个方法来通知订阅者消息
     * 
     * @param t 数据元素
     */
    public void onNext(T t);
    /**
     * 出错
     */
    public void onError(Throwable t);
    /**
     * 执行完成
     */
    public void onComplete();
}
  • 通过org.reactivestreams.Subscriber#onSubscribe官方注释可以看出,在调用onSubscribe方法之前,数据流不会消费。如果想要消费必须有消费者调用才行。这种模式是冷数据流模式。
  • org.reactivestreams.Subscriber#onSubscribe只会被调用一次
  • errorComplete是结束终止信号,当发出该信号之后不会再有任何新的信号发出
  • onNext方法可以被调用一次或多次,最多被调用的次数由Subscription#request(long)的参数决定

Subscription

public interface Subscription {
    public void request(long n);
    public void cancel();
}
  • Subscription 的生命周期是订阅者对发布者的一次消费。
  • 一个Subscription只能被一个Subscriber使用
  • Subscription不仅允许请求数据,也允许取消对数据的请求,并且支持资源清理
  • 在通过org.reactivestreams.Subscription#request发送需求信号之前,不会有任何事件产生。该方法请求的数量最大是Long.MAX_VALUE

Processor

public interface Processor<T, R> extends Subscriber<T>, Publisher<R> {
}
  • Processor实现了一种即是发布者又是消费者的组件。后续再讲map的时候会讲到。

先从最简单的实例开始入手。

@Test
public void test13() {
    Flux.just(1, 2, 3, 4, 5)
            .subscribe(new CoreSubscriber<>() {//这里传入CoreSubscriber对象作为订阅者
                @Override
                public void onSubscribe(Subscription s) {
                    log.info("onSubscribe, {}", s.getClass());
                    s.request(5);
                }
                @Override
                public void onNext(Integer integer) {
                    log.info("onNext: {}", integer);
                }
                @Override
                public void onError(Throwable t) {
                }
                @Override
                public void onComplete() {
                    log.info("onComplete");
                }
            });
}

运行程序,输出:

17:24:57.303 [main] DEBUG reactor.util.Loggers$LoggerFactory - Using Slf4j logging framework
17:24:57.308 [main] INFO MyTest - onSubscribe, class reactor.core.publisher.FluxArray$ArraySubscription
17:24:57.310 [main] INFO MyTest - onNext: 1
17:24:57.310 [main] INFO MyTest - onNext: 2
17:24:57.310 [main] INFO MyTest - onNext: 3
17:24:57.310 [main] INFO MyTest - onNext: 4
17:24:57.310 [main] INFO MyTest - onNext: 5
17:24:57.310 [main] INFO MyTest - onComplete

Flux是一个发布者,他实现了Publisher接口。我们看just方法

public static <T> Flux<T> just(T... data) {
    return fromArray(data);
}
public static <T> Flux<T> fromArray(T[] array) {
    if (array.length == 0) {
        return empty();
    }
    if (array.length == 1) {
        return just(array[0]);
    }
    return onAssembly(new FluxArray<>(array));
}

可以看到,它返回的是一个FluxArray对象,将我们传进去的参数封装为一个数组。

final class FluxArray<T> extends Flux<T> implements Fuseable, SourceProducer<T> {

    final T[] array;//保存数据

    public FluxArray(T... array) {
        this.array = Objects.requireNonNull(array, "array");
    }
    
    public static <T> void subscribe(CoreSubscriber<? super T> s, T[] array) {
        if (array.length == 0) {
            Operators.complete(s);
            return;
        }
        if (s instanceof ConditionalSubscriber) {
            s.onSubscribe(new ArrayConditionalSubscription<>((ConditionalSubscriber<? super T>) s, array));
        }
        else {//上面demo会走这个分支
             //这里的onSubscribe调用的就是demo中我们自定义的CoreSubscriber中的onSubscribe方法,方法内会打印日志并且调用request请求数据
            s.onSubscribe(new ArraySubscription<>(s, array));
        }
    }

//上面示例demo中调用的subscribe方法就是这个;actual是我们new的CoreSubscriber,array是我们初始化的1,2,3,4,5数组
    @Override
    public void subscribe(CoreSubscriber<? super T> actual) {
        subscribe(actual, array);
    }

    static final class ArraySubscription<T>
            implements InnerProducer<T>, SynchronousSubscription<T> {
    //省略
    }
    //省略
}

FluxArray继承了Flux也是一个发布者。内部有一个final 的属性array用于存储数据。
publisher#subscribe(Subscriber<? super T>)方法中调用Subscriber#onSubscribe()是固定流程。在上面org.reactivestreams.Subscriber#onSubscribe有介绍到。
接下来我们看一下Subscription。这里使用的是ArraySubscription

static final class ArraySubscription<T>
            implements InnerProducer<T>, SynchronousSubscription<T> {
        final CoreSubscriber<? super T> actual;//我们自定义的CoreSubscriber
        final T[] array;//存储数据
        int index;
        //下面这两个都是volatile ,保证了可见性
        volatile boolean cancelled;//流是否已经取消
        volatile long requested;//记录请求了多少个
        //使用AtomicLongFieldUpdater将requested进行了封装,保证了原子性更新
        @SuppressWarnings("rawtypes")
        static final AtomicLongFieldUpdater<ArraySubscription> REQUESTED =
                AtomicLongFieldUpdater.newUpdater(ArraySubscription.class, "requested");

        ArraySubscription(CoreSubscriber<? super T> actual, T[] array) {
            this.actual = actual;
            this.array = array;
        }

        @Override
        public void request(long n) {
            if (Operators.validate(n)) {
                if (Operators.addCap(REQUESTED, this, n) == 0) {//每次请求request都会更新requested属性的值
                    if (n == Long.MAX_VALUE) {
                        fastPath();
                    }
                    else {
                        slowPath(n);
                    }
                }
            }
        }

        void slowPath(long n) {
            final T[] a = array;
            final int len = a.length;
            final Subscriber<? super T> s = actual;

            int i = index;
            int e = 0;

            for (; ; ) {
                if (cancelled) {//每次都会判断事件流是否已经取消
                    return;
                }

                while (i != len && e != n) {
                    T t = a[i];

                    if (t == null) {
                        s.onError(new NullPointerException("The " + i + "th array element was null"));
                        return;
                    }
                     //调用我们自定义的CoreSubscriber.onNext()方法来处理消息。
                    s.onNext(t);

                    if (cancelled) {
                        return;
                    }

                    i++;
                    e++;
                }

                if (i == len) {//数据已经全部被消费,发送完成信号。我们这个demo中一次性取了5个数据,直接取完,所以到了这里就直接发送完成信号结束了。
                    s.onComplete();
                    return;
                }

                n = requested;

                if (n == e) {
                    index = i;
                    n = REQUESTED.addAndGet(this, -e);
                    if (n == 0) {
                        return;
                    }
                    e = 0;
                }
            }
        }
        //取消
        @Override
        public void cancel() {
            cancelled = true;
        }
    }

ArraySubscription定义了订阅消费数据的过程,在一次订阅的生命周期中,通过不断回调onNext方法来消费消息。消费完成后,通过回调onComplete()方法结束整个流程,也就是通常说的发出完成信号。
整个流程是采用的观察者模式,通过对象的传递与回调来实现的发布与消费,时序图与类图如下:
image

好了,上面介绍了关键的Publisher,Subscriber及Subscription。接下来看一下map这种即是订阅者又充当发布者的角色。
我们把上面的demo修改一下:

@Test
publicvoid test13() {
    Flux.just(1, 2, 3, 4, 5)
            .map(i -> i * i)
            .subscribe(new CoreSubscriber<>() {
                //省略,与之前相同
            });
}

直接跟进到map方法:

//这里mapper是map的参数,lambda表达式 : i->i*i
public final <V> Flux<V> map(Function<? super T, ? extends V> mapper) {
    if (this instanceof Fuseable) {
        //this是FluxArray, 他实现了Fuseable接口,所以走这一步
        return onAssembly(new FluxMapFuseable<>(this, mapper));
    }
    return onAssembly(new FluxMap<>(this, mapper));
}

看一下FluxMapFuseable<T, R>类, T 是源数据, R是经过计算后的结果数据。

final class FluxMapFuseable<T, R> extends FluxOperator<T, R> implements Fuseable {
    //lambda表达式:需要执行的计算
    final Function<? super T, ? extends R> mapper;
    
    FluxMapFuseable(Flux<? extends T> source,
            Function<? super T, ? extends R> mapper) {
        super(source);//这里source是fluxArray对象,
        this.mapper = Objects.requireNonNull(mapper, "mapper");
    }

    @Override
    @SuppressWarnings("unchecked")
    public void subscribe(CoreSubscriber<? super R> actual) {
        if (actual instanceof ConditionalSubscriber) {
            ConditionalSubscriber<? super R> cs = (ConditionalSubscriber<? super R>) actual;
            source.subscribe(new MapFuseableConditionalSubscriber<>(cs, mapper));
            return;
        }
        //这里source是fluxArray对象,
        source.subscribe(new MapFuseableSubscriber<>(actual, mapper));
    }

    static final class MapFuseableSubscriber<T, R>
            implements InnerOperator<T, R>,
                       QueueSubscription<R> {

        final CoreSubscriber<? super R>        actual;
        final Function<? super T, ? extends R> mapper;

        boolean done;
        QueueSubscription<T> s;
        int sourceMode;

        MapFuseableSubscriber(CoreSubscriber<? super R> actual,
                Function<? super T, ? extends R> mapper) {
            this.actual = actual;//这个actual是demo实例中我们自己创建的CoreSubscriber对象
            this.mapper = mapper;//map中的lambda表达式
        }

        //当调用FluxArray的subscribe后会调用这个onSubscribe方法
        public void onSubscribe(Subscription s) {
            if (Operators.validate(this.s, s)) {
                this.s = (QueueSubscription<T>) s;
                actual.onSubscribe(this);
            }
        }
        
        //调用FluxArray的request方法后,会调用这里的OnNext方法,这里充当了消费者的角色
        @Override
        public void onNext(T t) {
            if (sourceMode == ASYNC) {
                actual.onNext(null);
            }
            else {
                if (done) {
                    Operators.onNextDropped(t, actual.currentContext());
                    return;
                }
                R v;

                try {//这里使用mapper.apply(t)来执行demo中的lambda表达式,v是执行结果。这里充当了消费者的角色
                    v = Objects.requireNonNull(mapper.apply(t),
                            "The mapper returned a null value.");
                }
                catch (Throwable e) {
                    Throwable e_ = Operators.onNextError(t, e, actual.currentContext(), s);
                    if (e_ != null) {
                        onError(e_);
                    }
                    else {
                        s.request(1);
                    }
                    return;
                }
                //调用demo中我们自己创建的CoreSubscriber的onNext方法,这里其实充当了发布者的角色
                actual.onNext(v);
            }
        }
        //省略部分代码

        @Override
        public void onComplete() {
            if (done) {
                return;
            }
            done = true;
            
            actual.onComplete();
        }
        
        @Override
        public void request(long n) {
        //这个s是
            s.request(n);
        }
        @Override
        public void cancel() {
            s.cancel();
        }
        //省略部分代码
    }
}

上面可以看到,map的执行过程中,并不会产生数据,只是传送数据过程中对其执行一些计算。map即是消费者也是发布者。这个map的调用链路比较长,画个图清晰一下:
image

好了,相必有上面的图,再看这段代码就清晰多了。这里有很多对象的传递,回调,比较复杂。

相关文章
|
2月前
|
NoSQL Java Redis
Reactor实战,创建一个简单的单线程Reactor(理解了就相当于理解了多线程的Reactor)
本文通过一个简单的单线程Reactor模式的Java代码示例,展示了如何使用NIO创建一个服务端,处理客户端的连接和数据读写,帮助理解Reactor模式的核心原理。
43 0
Reactor实战,创建一个简单的单线程Reactor(理解了就相当于理解了多线程的Reactor)
|
7月前
|
监控 安全 Linux
reactor的原理与实现
前情回顾 网络IO,会涉及到两个系统对象:   一个是用户空间调用的进程或线程   一个是内核空间的内核系统 如果发生IO操作read时,会奖励两个阶段:
80 1
|
负载均衡 算法 Java
Netty源码分析系列之五:Netty多线程模型
本文主要介绍了Netty的多线程模型,它采用的是Reactor模型。处理连接请求与处理IO操作的线程隔离。基于事件轮询监听,不断获取处于就绪状态的通道。其中Boss线程池的线程负责处理连接请求,接收到accept事件之后,将对应的socket进行封装生成NioSocketChannel对象,并将其提交到workBoss线程池中,处理IO的read以及write事件。
Netty源码分析系列之五:Netty多线程模型
|
缓存 Java 测试技术
Netty实战(七)EventLoop和线程模型
简单地说,线程模型指定了操作系统、编程语言、框架或者应用程序的上下文中的线程管理的关键方面。
186 0
|
编解码 弹性计算 缓存
Netty源码和Reactor模型
Netty源码和Reactor模型
109 0
|
Java API 调度
Netty组件EventLoopGroup和EventLoop源码分析
Netty组件EventLoopGroup和EventLoop源码分析
80 0
|
设计模式 缓存 分布式计算
Netty源码剖析之线程模型
1. NIO 的类库和 API 繁杂,使用麻烦:需要熟练掌握 Selector、ServerSocketChannel、SocketChannel、ByteBuffer等。 2. 需要具备其他的额外技能:要熟悉 Java 多线程编程,因为 NIO 编程涉及到 Reactor 模式,必须对多线程和网络编程非常熟悉,才能编写出高质量的 NIO 程序。 3. 开发工作量和难度都非常大:例如客户端面临断连重连、网络闪断、半包读写、失败缓存、网络拥塞和异常流的处理等等。 4. JDK NIO 的 Bug:臭名昭著的 Epoll Bug,它会导致 Selector 空轮询,最终导致 CPU 100%
138 0
|
编解码
Netty源码剖析之Netty启动流程
了解netty启动流程,有助于学习netty,进行自定义组件扩展
141 0
|
安全 Java
Netty「源码阅读」之 EventLoop 简单介绍到源码分析
Netty「源码阅读」之 EventLoop 简单介绍到源码分析
219 0
|
JavaScript 安全 Java
深入Netty逻辑架构,从Reactor线程模型开始(一)
深入Netty逻辑架构,从Reactor线程模型开始(一)
434 0
深入Netty逻辑架构,从Reactor线程模型开始(一)