webflux源码解析(2)-reactor

目录

  • [1. 接口概述](#1. 接口概述)
  • [2. 方法执行](#2. 方法执行)
    • [2.1 subscribe方法说明](#2.1 subscribe方法说明)
    • [2.2 processor 方法说明](#2.2 processor 方法说明)
  • [3. 其它示例说明](#3. 其它示例说明)
  • 参考:

webflux框架是基于Project Reactor构建的,是一种响应式的编码方式,因此webflux的源码跟其它框架看起来差别较大。代码实际执行时也是异步的,哪怕通过debug断点跟进流程,也显得有些难懂。

为了看明白webflux的代码,本文梳理了reactor的基本接口设计、代码执行逻辑。

通过逐个方法的跟进,可以发现,所谓的响应式编程并不是魔法,对于具体的方法而言,还是一个线程同步、阻塞的去执行。所谓的响应式,需要相对应的客户端配合,这个待后续文章讨论。

1. 接口概述

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


发布者
Publisher 是一个可以发送无限序列元素的发布者,允许调用多次,每次调用都会启动一个新的Subscription。每个Subscription只能被一个Subscriber使用;Subscriber消费者只能订阅一次Publisher。

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

订阅者(消费者)

其中Subscriber#onSubscribe只会被调用一次

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();
}

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> {
}

2. 方法执行

2.1 subscribe方法说明

示例:

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");
		}
	});

以下是调用的方法跟踪,调用 Flux.just 方法:


查看示例中的代码可知 subscribe() 方法调用的是 FluxArray#subscribe

通过 FluxArray#subscribe 方法调用到我们定义的 onSubscribe 方法,进而调用 FluxArray.ArraySubscription#request 方法

FluxArray.ArraySubscription#slowPath 方法中循环拉去数组中的元素,执行onNext方法,最后执行 onComplete 方法

[!NOTE] 总结

  • Publisher中定义了数据源
  • Subscriber 中定义了数据处理的动作
  • Subscription 中定义了数据处理的过程,编排了 Subscriber 中定义的方法

2.2 processor 方法说明

示例:

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

跟进执行的方法到 Flux#map 方法:

之后调用的 subscribe 方法是 FluxMapFuseable#subscribe

此时执行 FluxArray#subscribe 传入的不再是 FluxArray.ArraySubscription 对象,变成了 FluxMapFuseable.MapFuseableSubscriber 对象了,由于中间过程的组装,之后会进一步调用至 FluxMapFuseable.MapFuseableSubscriber#onSubscrib

此处调用 onSubscribe 方法时,传入的参数为this,即位为 MapFuseableSubscriber 对象,因此执行s.request(5) 时,s 是 MapFuseableSubscriber 对象,实际执行的方法是
FluxMapFuseable.MapFuseableSubscriber#request

最终还是委托 FluxArray.ArraySubscription#request 方法执行。由于这一层封装,导致执行 onNext 方法时,执行的是 FluxMapFuseable.MapFuseableSubscriber#onNext 方法,这里充当了消费者的角色

真的很绕

3. 其它示例说明

这是一个更通用的场景,通过异步任务定义Publisher

测试示例:

public void test(){  
    Mono.fromCallable(() -> {  
                // 执行阻塞操作,例如数据库查询  
                return "Some blocking operation result";  
            })  
            .subscribe(result -> {  
                System.out.println("Result: " + result);  
            });  
}

调用方法 Mono#fromCallable 时,返回的是 MonoCallable 对象

调用 subscribe() 方法时,调用的是 Mono#subscribe 方法,但会逐层封装(此处封装为LambdaMonoSubscriber

最终在此会调用到不同的实现,此处 this 即为 MonoCallable 对象

调用的subscribe最终为:MonoCallable#subscribe

actual 是 LambdaMonoSubscriber 对象,调用其 subscribe 方法

查看 LambdaMonoSubscriber#onSubscribe

继续跟进,调用MonoSubscriber#request

因此,此处actual.onSubscribe(sds)并没有实际意义

LambdaMonoSubscriber.complete 方法执行时,调用的是 LambdaMonoSubscriber 的onNext、onComplete方法,实际执行的就是示例中定义的
System.out.println("Result: " + result)

至此,全部方法执行完毕。

参考:

[1] reactor3 源码分析

相关推荐
陈大爷(有低保)7 分钟前
UDP Socket聊天室(Java)
java·网络协议·udp
kinlon.liu21 分钟前
零信任安全架构--持续验证
java·安全·安全架构·mfa·持续验证
王哲晓42 分钟前
Linux通过yum安装Docker
java·linux·docker
java6666688881 小时前
如何在Java中实现高效的对象映射:Dozer与MapStruct的比较与优化
java·开发语言
Violet永存1 小时前
源码分析:LinkedList
java·开发语言
执键行天涯1 小时前
【经验帖】JAVA中同方法,两次调用Mybatis,一次更新,一次查询,同一事务,第一次修改对第二次的可见性如何
java·数据库·mybatis
Jarlen1 小时前
将本地离线Jar包上传到Maven远程私库上,供项目编译使用
java·maven·jar
蓑 羽1 小时前
力扣438 找到字符串中所有字母异位词 Java版本
java·算法·leetcode
Reese_Cool1 小时前
【C语言二级考试】循环结构设计
android·java·c语言·开发语言