reactive stream
响应式流(反应式流):异步、非阻塞。
是一种观察者模式的扩展。当一个数据发生变化时,另一个数据或者组件也跟着变化。是一种事件流的处理模式。
Reactive programming is a declarative programming paradigm(声明式编程范式) that is based on the idea of asynchronous event processing and data streams.
asynchronous stream processing with non-blocking back pressure
实现 reactive stream 可以通过 callback 的方式,但是会有“回调地狱”的问题。
由 4 个部分组成:
- Publisher:数据生产者。有一个接口 subcribe(),用于注册订阅者。当 Publisher 生产了数据之后,通知这些订阅者。
- Subscriber:数据消费者。有 4 个接口:
- onSubcribe
- onNext
- onError
- onComplete
- Subscription:Publisher subcribe Subscriber之后创建的对象。在 Subscriber 中用于向 Publisher 请求新的数据(背压实现的核心)。
- Processor:既是 Publisher 又是 Subscriber。比如一个 Subcriber 从 Publisher 获取数据之后,需要将这个数据处理之后,再通知其他 Subscriber。
java9的 Flow 完全实现了上面的 4 个接口。
java9的参考例子 java-9-reactive-streams
back pressure
为什么需要背压
生产者和消费者速度不匹配。主要为了防止生产者生产数据过快,消费者来不及消费而被打死。
背压的实现方式
- 缓存
- 丢弃
java9 flow
@Data
@NoArgsConstructor
@AllArgsConstructor
public class VideoFrame {
private long number;
}
@Slf4j
public class VideoPlayer implements Flow.Subscriber<VideoFrame> {
Flow.Subscription subscription = null;
@Override
public void onSubscribe(Flow.Subscription subscription) {
this.subscription = subscription;
subscription.request(1);
}
@Override
public void onNext(VideoFrame item) {
log.info("play #{}" , item.getNumber());
subscription.request(10);
}
@Override
public void onError(Throwable throwable) {
log.error("There is an error in video streaming:{}" , throwable.getMessage());
}
@Override
public void onComplete() {
log.error("Video has ended");
}
}
public class VideoStreamServer extends SubmissionPublisher<VideoFrame> {
public VideoStreamServer() {
// 这里配置了 buffer 的最大容量为 2
// 提供的线程池:将生产的数据通知 subscriber 时使用的异步线程池
// 如果某个 subscriber 来不及消费数据(没有及时 request 新数据),publisher不停生产数据时,为每个 subscriber 缓存的最大数
super(Executors.newFixedThreadPool(2, new NamedThreadFactory("VideoStreamServerPublisher")), 2);
}
}
public class ReactiveStreamLearn {
@Test
public void test() throws InterruptedException {
VideoStreamServer streamServer = new VideoStreamServer();
streamServer.subscribe(new VideoPlayer());
streamServer.subscribe(new VideoPlayer2());
// submit video frames
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
AtomicLong frameNumber = new AtomicLong();
executor.scheduleWithFixedDelay(() -> {
streamServer.offer(new VideoFrame(frameNumber.getAndIncrement()), (subscriber, videoFrame) -> {
subscriber.onError(new RuntimeException("Frame#" + videoFrame.getNumber()
+ " dropped because of backpressure"));
// 返回false会丢弃该数据;如果返回 true,会再重试 1 次。
return false;
});
if (frameNumber.get() == 200) {
streamServer.close();
// streamServer.closeExceptionally(new RuntimeException("publisher closeExceptionally"));
}
}, 0, 1, TimeUnit.MILLISECONDS);
sleep(1000);
}
}