一、代码示例
Flux<String> flux = Flux.just("foo", "bar", "foobar");
flux.subscribe(System.out::println);
二、Flux.just方法源码追踪
首先接受一下订阅者类的基本方法
public interface Subscriber<T> {
public void onSubscribe(Subscription s);//开始订阅的方法
public void onNext(T t);//接受发布者数据的方法
public void onError(Throwable t);//出错信号处理
public void onComplete();//完成信号处理
- 生成一个FluxArray对象
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));
}
- 生成一个Subscription对象
这个类是代表订阅者和发布者一对一的生命周期类。也可以理解他是维护订阅者和发布者关系的纽带
FluxArray本身也是个Flux,它里面的值是数组,他有一个特殊的方法subscribe
,首先看它的实现
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 {
s.onSubscribe(new ArraySubscription<>(s, array));
}
}
这个方法执行时机:是当订阅者想要订阅的时候。参数s
就是订阅者,订阅者会执行onSubscribe
方法。Subscription
和他的子类 比如ArraySubscription
和 ArrayConditionalSubscription
是一个有意思的类,这个类是代表订阅者和发布者一对一的生命周期类。也可以理解他是维护订阅者和发布者关系的纽带。他有两个主要方法
public void request(long n);//请求发布者向订阅者推送消息
public void cancel();//取消推送,并且清理相关的资源
基本上Subscription
会持有订阅者的引用和发布者的引用。比如ArraySubscription
的构造方法
ArraySubscription(CoreSubscriber<? super T> actual, T[] array) {
this.actual = actual;//订阅者
this.array = array;//要发布的数据
}
- 订阅者调用onSubscribe
执行这个方法的时候,其实就是让中间类Subscription
去调用reqeust
方法,让发布者推送数据
public final void onSubscribe(Subscription s) {
if (Operators.validate(subscription, s)) {
//省略其他方法
s.request(Long.MAX_VALUE);
}
}
观察ArraySubscription
中request
方法
void fastPath() {
final T[] a = array;
final int len = a.length;
final Subscriber<? super T> s = actual;
for (int i = index; i != len; i++) {
if (cancelled) {
return;
}
T t = a[i];
if (t == null) {
s.onError(new NullPointerException("The " + i + "th array element was null"));
return;
}
s.onNext(t);
}
if (cancelled) {
return;
}
s.onComplete();
}
很清晰明了,就是循环调用发布者的数据,然后调用订阅者的onNext
方法,中间有错误调用订阅者的onError
方法, 循环结束就调用订阅者的onComplete
方法。这样整个生命周期就结束了
三、fromIterable和range等等分析
这类方法和just
基本一个套路,没什么特殊的。都可以参考just
方法
四、带背压的create方法分析
从just方法基本可以看到reactor3的套路,我们可以按照下面三个步骤进行分析
- 找到实际的Flux实现类
- 找到内部实现subscription接口的类,这个类负责发布者订阅者关系的类。
- 分析内部类subscription中的request方法
为什么subscription会是发布者的内部类呢?
演示代码:
这个例子不完整,但是只是为了理解足以
//定义发布者
Flux<String> bridge = Flux.create(sink -> {
for(int i=0;i<100;i++){
sink.next(i+"");
}
sink.complete();
});
//开启订阅
bridge.subscribe(event-> System.out.println("receive: "+event),e->{},countDownLatch::countDown);
//数据源生产事件
generateEvent(myEventProcessor);
4.1 Flux实现类
FluxCreate:他就是实现类,他的UML是这样的
类的结构还是非常清晰明了的。
4.2 内部subscription类
根据不同的背压策略,有不同的类
switch (backpressure) {
case IGNORE: {
return new IgnoreSink<>(t);
}
case ERROR: {
return new ErrorAsyncSink<>(t);
}
case DROP: {
return new DropAsyncSink<>(t);
}
case LATEST: {
return new LatestAsyncSink<>(t);
}
default: {
return new BufferAsyncSink<>(t, Queues.SMALL_BUFFER_SIZE);
}
}
下面看一下默认的BufferAsyncSink
UML图
从本质上说他还是个Subscription,还是负责维护发布者和订阅者。
subscription的request方法
下面针对BufferAsyncSink
进行分析
public void subscribe(CoreSubscriber<? super T> actual) {
//根据背压策略选择 相应的Conscription(典型的策略模式)
BaseSink<T> sink = createSink(actual, backpressure);
//订阅者 订阅发布者
actual.onSubscribe(sink);
try {
//执行传入的消费者,其实就是消息的源头进行生产
source.accept(
createMode == CreateMode.PUSH_PULL ? new SerializedSink<>(sink) :
sink);
}
catch (Throwable ex) {
Exceptions.throwIfFatal(ex);
sink.error(Operators.onOperatorError(ex, actual.currentContext()));
}
}
public final void onSubscribe(Subscription s) {
//省略荣誉代码,下面还是老套路进行s.request
s.request(Long.MAX_VALUE);
}
这个request
方法是在BufferAsyncSink
父类BaseSink中
public final void request(long n) {
if (Operators.validate(n)) {
Operators.addCap(REQUESTED, this, n);
LongConsumer consumer = requestConsumer;
if (n > 0 && consumer != null && !isCancelled()) {
consumer.accept(n);
}
//主要是这个方法
onRequestedFromDownstream();
}
}
主要针对onRequestedFromDownstream
进行跟踪
void onRequestedFromDownstream() {
drain();
}
对drain
进行跟踪
void drain() {
if (WIP.getAndIncrement(this) != 0) {
return;
}
int missed = 1;
final Subscriber<? super T> a = actual;
final Queue<T> q = queue;
for (; ; ) {
long r = requested;
long e = 0L;
while (e != r) {
if (isCancelled()) {
q.clear();
return;
}
boolean d = done;
T o = q.poll();
boolean empty = o == null;
if (d && empty) {
Throwable ex = error;
if (ex != null) {
super.error(ex);
}
else {
super.complete();
}
return;
}
if (empty) {
break;
}
a.onNext(o);
e++;
}
if (e == r) {
if (isCancelled()) {
q.clear();
return;
}
boolean d = done;
boolean empty = q.isEmpty();
if (d && empty) {
Throwable ex = error;
if (ex != null) {
super.error(ex);
}
else {
super.complete();
}
return;
}
}
if (e != 0) {
Operators.produced(REQUESTED, this, e);
}
missed = WIP.addAndGet(this, -missed);
if (missed == 0) {
break;
}
}
}
这个drain方法核心就是无限循环for (; ; )
,每次都去一个队列取数据T o = q.poll();
. 如果去到数据就调用订阅者的onNext方法传递数据a.onNext(o);
现在就开始分析这个队列是做什么的。下面看BufferAsyncSink
的类定义
static final class BufferAsyncSink<T> extends BaseSink<T> {
final Queue<T> queue;
public FluxSink<T> next(T t) {
queue.offer(t);
drain();
return this;
}
}
这个队列是类的成员变量,在调用next
方法的时候回向队列填充数据,然后调用drain
方法去循环消费。这个其实就是默认的背压实现
他不是直接发送给下面就行消费,而是先放到自己的缓冲队列中,然后在让订阅者去循环消费。
什么时候回向队列中填充数据呢?
在定义Flux的时候穿了一个消费者,到目前为止还没触发消费
//消费者
sink -> {
for(int i=0;i<100;i++){
sink.next(i+"");
}
sink.complete();
});
在方法subscribe
中会执行onSubscribe
之后就会调用它
source.accept(createMode == CreateMode.PUSH_PULL ? new SerializedSink<>(sink) :sink);
如果是推拉模式,那么就对sink
进行封装,封装成SerializedSink
(这里可以先不考虑这步骤)
一般情况下这个消费者是为了生产事件,然后放到队列中的。、
当调用sink.next(i+"");
就是调用SerializedSink
的next
方法
public FluxSink<T> next(T t) {
Objects.requireNonNull(t, "t is null in sink.next(t)");
if (sink.isCancelled() || done) {
Operators.onNextDropped(t, sink.currentContext());
return this;
}
if (WIP.get(this) == 0 && WIP.compareAndSet(this, 0, 1)) {
try {
//核心
sink.next(t);
}
catch (Throwable ex) {
Operators.onOperatorError(sink, ex, t, sink.currentContext());
}
if (WIP.decrementAndGet(this) == 0) {
return this;
}
}
else {
this.mpscQueue.offer(t);
if (WIP.getAndIncrement(this) != 0) {
return this;
}
}
drainLoop();
return this;
}
原来的BufferAsyncSink
被封装到SerializedSink
中了,并复制给变量sink
,到最后还是调用BufferAsyncSink
的next
方法。 这不就是类的设计组合嘛(优先考虑组合,然后考虑继承)
而BufferAsyncSink
的next
上面已经说过了
public FluxSink<T> next(T t) {
queue.offer(t);
drain();
return this;
}
就是先去队列中插入一个值,然后开始调用drain方法去消费。
这里思考个问题,如果发布者和订阅者在同一个线程中,这个背压或者说设计这个队列还有意义吗?
可以肯定的是没有意义,如果在同一个线程,那就是 生产一个,然后等着消费,然后在生产一个,何来背压。两者一定要在不同线程中,然后最好速率不一致,这个才是背压的使用场景