一、Reactor3 创建源码分析

一、代码示例

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 和他的子类 比如ArraySubscriptionArrayConditionalSubscription 是一个有意思的类,这个类是代表订阅者和发布者一对一的生命周期类。也可以理解他是维护订阅者和发布者关系的纽带。他有两个主要方法

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

观察ArraySubscriptionrequest方法

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

下面看一下默认的BufferAsyncSinkUML图
在这里插入图片描述
从本质上说他还是个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+""); 就是调用SerializedSinknext方法

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,到最后还是调用BufferAsyncSinknext方法。 这不就是类的设计组合嘛(优先考虑组合,然后考虑继承)
BufferAsyncSinknext上面已经说过了

public FluxSink<T> next(T t) {
			queue.offer(t);
			drain();
			return this;
}

就是先去队列中插入一个值,然后开始调用drain方法去消费。

这里思考个问题,如果发布者和订阅者在同一个线程中,这个背压或者说设计这个队列还有意义吗?

可以肯定的是没有意义,如果在同一个线程,那就是 生产一个,然后等着消费,然后在生产一个,何来背压。两者一定要在不同线程中,然后最好速率不一致,这个才是背压的使用场景

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值