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 源码分析](https://blog.csdn.net/liubenlong007/article/details/86706232)

相关推荐
Dcs16 分钟前
VSCode等多款主流 IDE 爆出安全漏洞!插件“伪装认证”可执行恶意命令!
java
保持学习ing21 分钟前
day1--项目搭建and内容管理模块
java·数据库·后端·docker·虚拟机
京东云开发者33 分钟前
Java的SPI机制详解
java
超级小忍1 小时前
服务端向客户端主动推送数据的几种方法(Spring Boot 环境)
java·spring boot·后端
程序无bug1 小时前
Spring IoC注解式开发无敌详细(细节丰富)
java·后端
小莫分享1 小时前
Java Lombok 入门
java
程序无bug1 小时前
Spring 对于事务上的应用的详细说明
java·后端
食亨技术团队1 小时前
被忽略的 SAAS 生命线:操作日志有多重要
java·后端
苦学编程的谢1 小时前
Maven
java·maven·intellij-idea
考虑考虑1 小时前
Maven 依赖范围(Scope)
java·后端·maven