响应式编程初探-自定义实现Reactive Streams规范
最近在学响应式编程,这里先记录下,响应式编程的一些基础内容
1.名词解释
Reactive Streams、Reactor、WebFlux以及响应式编程之间存在密切的关系,它们共同构成了在Java生态系统中处理异步和响应式编程的一系列工具和框架。
-
Reactive Streams:
- Reactive Streams 是一个规范,定义了一组接口和协议,用于处理异步数据流的背压。它包括发布者(Publisher)、订阅者(Subscriber)、订阅(Subscription)和处理器(Processor)等接口。
- Reactive Streams 规范的目标是提供一种标准的方式来处理异步数据流,解决背压问题。Java标准库从Java 9开始提供了
java.util.concurrent.Flow
类,定义了Reactive Streams规范。
-
Reactor:
- Reactor 是一个基于Reactive Streams规范的响应式编程框架。它提供了一组用于构建异步、事件驱动、响应式应用程序的工具和库。Reactor 的核心是 Flux(表示一个包含零到多个元素的异步序列)和 Mono(表示一个包含零或一个元素的异步序列)。
- Reactor 通过提供响应式的操作符,如
map
、filter
、flatMap
等,使得开发者能够方便地进行数据流的转换和处理。
-
WebFlux:
- WebFlux 是Spring Framework 5引入的响应式编程支持。它构建在 Reactor 之上,提供了一套用于构建异步、非阻塞、响应式的Web应用程序的API。WebFlux支持使用Reactive Streams处理HTTP请求和响应。
- Spring WebFlux 可以用于构建反应式的RESTful服务,支持使用注解的方式定义路由和处理器函数。
-
响应式编程:
- 响应式编程是一种编程范式,强调数据流和变化的传播。在这个范式中,数据源产生数据并通知观察者,观察者相应地处理这些数据。这种方式更容易处理异步操作和事件。
- 在Java中,响应式编程通常涉及到使用类似于Reactor或RxJava的库,这些库提供了响应式的操作符和工具。
综上所述,Reactive Streams 提供了规范,Reactor 是一个实现了该规范的响应式编程框架,而WebFlux是Spring对于响应式编程的支持。它们共同致力于构建异步、非阻塞、响应式的应用程序。响应式编程则是一种更广义的编程范式,与Reactive Streams和Reactor等具体实现密切相关。
2.Reactive Streams 规范
2.1.Reactive Streams规范定义
在java.util.concurrent.Flow
类中,定义了Reactive Streams规范
- Publisher(发布者):负责生成数据流,并向订阅者发送数据。
- Subscriber(订阅者):表示数据流的消费者,它订阅一个或多个发布者,并接收数据。
- Subscription(订阅):表示订阅关系的接口,用于控制数据流的请求和取消。
- Processor(处理器):充当发布者和订阅者的中间组件,可以对数据进行转换和处理。
2.2.API方法
1. Publisher(发布者):
interface Publisher<T> {void subscribe(Subscriber<? super T> subscriber);
}
subscribe(Subscriber<? super T> subscriber)
: 用于订阅数据流。当订阅者调用这个方法时,发布者将建立与订阅者的订阅关系,并开始推送数据。
2. Subscriber(订阅者):
interface Subscriber<T> {void onSubscribe(Subscription subscription);void onNext(T item);void onError(Throwable throwable);void onComplete();
}
-
onSubscribe(Subscription subscription)
: 在订阅关系建立时调用。通过这个方法,订阅者可以持有Subscription
对象,以便后续请求数据和取消订阅。 -
onNext(T item)
: 在接收到新元素时调用。订阅者通过这个方法处理收到的数据。 -
onError(Throwable throwable)
: 在数据流中出现错误时调用。订阅者通过这个方法处理错误情况。 -
onComplete()
: 在数据流完成时调用。通知订阅者数据流结束,不再有新的元素。
3. Subscription(订阅):
interface Subscription {void request(long n);void cancel();
}
-
request(long n)
: 用于请求订阅者处理指定数量的元素。订阅者通过这个方法告知发布者它可以处理多少个元素。 -
cancel()
: 用于取消订阅关系。当订阅者不再需要接收数据时,调用此方法取消订阅。
4. Processor(处理器):
interface Processor<T, R> extends Subscriber<T>, Publisher<R> {
}
Processor
接口是 Subscriber
和 Publisher
的组合,表示一个中间处理组件,可以同时充当订阅者和发布者的角色。
-
Subscriber
部分的方法:onSubscribe(Subscription subscription)
,onNext(T item)
,onError(Throwable throwable)
,onComplete()
。 -
Publisher
部分的方法:subscribe(Subscriber<? super R> subscriber)
。表示Processor
可以被其他订阅者订阅。
5.泛型T
泛型T即为数据流
这些方法共同构成 Reactive Streams 协议,定义了发布者和订阅者之间的协作方式,以及订阅者如何处理数据流。在实际的使用中,这些方法的实现通常需要考虑异步处理、背压机制等方面,以确保响应式编程的目标得以实现。
2.3.工作流程
在 Reactive Streams 中,Publisher
、Subscriber
、Subscription
和 Processor
之间的协作流程如下:
有时间再补流程图
-
Publisher(发布者):
Publisher
是异步产生数据流的组件,它通过subscribe
方法允许订阅者订阅。subscribe
方法会接收一个Subscriber
对象作为参数。- 当
Publisher
有新数据准备好时,通过调用订阅者的onNext
方法将数据推送给订阅者。
interface Publisher<T> {void subscribe(Subscriber<? super T> subscriber); }
-
Subscriber(订阅者):
Subscriber
是数据流的消费者,通过实现Subscriber
接口来接收来自发布者的数据。订阅者通过调用subscription.request(n)
请求一定数量的数据,处理数据时通过onNext
方法接收元素。- 当订阅者无法处理更多的元素时,可以调用
subscription.cancel()
来取消订阅。
interface Subscriber<T> {void onSubscribe(Subscription subscription);void onNext(T item);void onError(Throwable throwable);void onComplete(); }
-
Subscription(订阅):
Subscription
表示订阅关系,它在onSubscribe
方法中被传递给订阅者。通过Subscription
,订阅者可以请求数据和取消订阅。- 订阅者通过
request(long n)
方法请求处理 n 个元素,通过cancel()
方法取消订阅。
interface Subscription {void request(long n);void cancel(); }
-
Processor(处理器):
Processor
是一个同时实现了Publisher
和Subscriber
接口的中间组件,可以作为数据流的处理器,对数据进行转换和处理。Processor
既能接收数据,也能发布数据。它将onNext
、onError
和onComplete
方法委托给下游的订阅者,并将数据推送给上游的发布者。
interface Processor<T, R> extends Subscriber<T>, Publisher<R> { }
这些接口一起构成了 Reactive Streams 的基本协议。发布者产生数据,订阅者订阅数据流并通过 onNext
方法接收元素,订阅者通过 request
方法请求处理一定数量的元素,同时可以通过 cancel
方法取消订阅。Processor
则可以用于在订阅者和发布者之间进行数据转换和处理。在 Reactive Streams 的实现中,这些接口的方法调用是异步进行的,以支持非阻塞的数据流处理。
3.自定义实现Reactive Streams规范
自己实现了一个,参考了SubmissionPublisher
- 同步实现的
- 功能不完善
- 有bug
class MyPublisher implements Flow.Publisher<String>{MySubscription<String> subscription;public int request ;public void publish(String item){subscription.items.add(item);while (true) {if (request > 0) {for (int i = 0; i < request; i++) {if (!subscription.items.isEmpty()) {try {Object o = subscription.items.get(subscription.items.size() - 1);subscription.subscriber.onNext(o.toString());subscription.items.remove(o);}catch (Exception e){subscription.subscriber.onError(e);return;}}}}if (subscription.items.isEmpty()) {break;}}}@Overridepublic void subscribe(Flow.Subscriber<? super String> subscriber) {System.out.println("第一步:绑定订阅者" );MySubscription<String> subscription = new MySubscription<>(subscriber,this);this.subscription = subscription;subscriber.onSubscribe(subscription);}}class MySubscriber implements Flow.Subscriber<String>{private Flow.Subscription subscription;@Overridepublic void onSubscribe(Flow.Subscription subscription) {System.out.println("第二步:接收Subscription" );this.subscription = subscription;// 请求订阅者处理的元素数量subscription.request(1);}@Overridepublic void onNext(String item) {System.out.println("第四步:推送数据" );System.out.println("MySubscriber 消费了item = " + item);subscription.request(1);}@Overridepublic void onError(Throwable throwable) {System.out.println("出异常了 = " + throwable);}@Overridepublic void onComplete() {}}class MySubscription<T> implements Flow.Subscription{final Flow.Subscriber<? super T> subscriber;final MyPublisher publisher;List items = new ArrayList();public MySubscription(Flow.Subscriber<? super T> subscriber, MyPublisher publisher) {this.subscriber = subscriber;this.publisher = publisher;}@Overridepublic void request(long n) {this.publisher.request++;System.out.println("第三步:拉取请求" );}@Overridepublic void cancel() {}
}
public class FlowDemo {public static void main(String[] args) {MyPublisher myPublisher = new MyPublisher();MySubscriber mySubscriber = new MySubscriber();myPublisher.subscribe(mySubscriber);myPublisher.publish("111");myPublisher.publish("222");myPublisher.publish(null);}
}
4.Jdk实现Reactive Streams使用示例
class SimplePublisher implements Flow.Publisher<Integer> {private final SubmissionPublisher<Integer> publisher = new SubmissionPublisher<>();public void publishItems() {for (int i = 1; i <= 5; i++) {publisher.submit(i);}// 发布者完成发布publisher.close();}@Overridepublic void subscribe(Flow.Subscriber<? super Integer> subscriber) {publisher.subscribe(subscriber);}
}class SimpleSubscriber implements Flow.Subscriber<Integer> {private Flow.Subscription subscription;@Overridepublic void onSubscribe(Flow.Subscription subscription) {this.subscription = subscription;// 请求订阅者处理的元素数量subscription.request(1);}@Overridepublic void onNext(Integer item) {System.out.println("Received item: " + item);// 处理完一个元素后请求下一个subscription.request(1);}@Overridepublic void onError(Throwable throwable) {System.err.println("Error occurred: " + throwable.getMessage());}@Overridepublic void onComplete() {System.out.println("Processing completed.");}
}public class ReactiveStreamsExample {public static void main(String[] args) throws InterruptedException {// 创建发布者和订阅者SimplePublisher simplePublisher = new SimplePublisher();SimpleSubscriber simpleSubscriber = new SimpleSubscriber();// 订阅者订阅发布者simplePublisher.subscribe(simpleSubscriber);// 发布者发布数据simplePublisher.publishItems();// 睡一觉,确保数据处理完成Thread.sleep(3000);}
}
学习打卡:Java学习笔记-day05-响应式编程初探-自定义实现Reactive Streams规范
相关文章:

响应式编程初探-自定义实现Reactive Streams规范
最近在学响应式编程,这里先记录下,响应式编程的一些基础内容 1.名词解释 Reactive Streams、Reactor、WebFlux以及响应式编程之间存在密切的关系,它们共同构成了在Java生态系统中处理异步和响应式编程的一系列工具和框架。 Reactive Streams…...

如何使用LightPicture+cpolar搭建个人云图床随时随地公网访问
文章目录 1.前言2. Lightpicture网站搭建2.1. Lightpicture下载和安装2.2. Lightpicture网页测试2.3.cpolar的安装和注册 3.本地网页发布3.1.Cpolar云端设置3.2.Cpolar本地设置 4.公网访问测试5.结语 1.前言 现在的手机越来越先进,功能也越来越多,而手机…...

华媒舍:高效率的新闻资讯新闻媒体宣发套餐内容推广计划方案
怎样让自己的新闻资讯可以被大众孰知,变成了每一个新闻媒体宣发者一同存在的困难。下面我们就给大家介绍一套高效率的新闻资讯新闻媒体宣发套餐内容推广计划方案,致力于帮助新闻媒体宣发者提升宣发高效率,提高新闻资讯的传播性。 1.新闻媒体宣…...

MySQL使用通配符进行数据搜索以及过滤
目录 1.什么是通配符? 2.通配符之→百分号(%) 3.通配符之→下划线(_) 4.通配符使用注意事项 *本文涉及概念来源于图灵程序设计丛书,数据库系列——《MySQL必知必会》 1.什么是通配符? 通配符(wildcard) :用来匹配值的一部分…...

Overleaf IEEE白嫖即将失效!
之前白嫖Overleaf用IEEE的,最长只能到一月份了!(官方回复) 翻译一下: IEEE不支持这种Collaboratec白嫖了已经白嫖的,到2024年1月份过期没有白嫖的,已经无法获得了...

条件控制生成---相关论文集合
1. IP-Adapter 论文地址 解决问题: 如何将图片作为prompt输入网络,并无需更改开源模型参数 解决思路: 新增一个cross-attention layers,结果与text prompt的cross-attention layers结果相加后输入网络,只需要训练Wk, …...

揭秘亚马逊、ebay测评系统:从稳定环境搭建到防关联技术
在亚马逊、ebay平台上进行测评、lu卡和lu货、采退等活动,首要问题是确保环境的安全性和稳定性。一个稳定的环境是进行测评和lu卡、lu货、采退的基础,如果无法解决安全性问题,那么从事这些项目就不值得。我们在环境技术研发领域已经有l七年的经…...

街机模拟游戏逆向工程(HACKROM)教程:[3]街机的ROM与RAM
简介 在街机模拟器中运行一个街机游戏,我们除了需要一个模拟器工具 ,也需要有一个街机的ROM文件。街机的ROM文件,称之为Read-Only Memory,可以理解为只读存储器。在 ROM文件中,包括了游戏运行所需要的指令代码&#x…...

Element UI CascaderPanel级联组件使用和踩坑总结
Element UI CascaderPanel级联组件使用和踩坑总结 问题背景 需求中需要用到Element UI的 CascaderPanel组件,并且支持多选,定制化需求,比如某节点被选择,等价于该节点下面所有子节点都被选择, CascaderPanel组件返回…...

Oracle全系列版本官网下载保姆及教程
Oracle全系列版本官网下载方法 下面以下载Oracle12cR2为例说明下载的整个过程。 基本步骤如下: 先注册一个Oracle账号并登录;进入到客户下载页面搜索要下载的数据库版本;得到Oracle下载器(Oracle_SSN_DML_xxxxx.exe),注意…...

漏洞扫描是最该被防范的安全问题
在当今的网络环境中,漏洞扫描是一项至关重要的任务。随着技术的不断进步,网络攻击的威胁也在持续增长,而漏洞扫描是防范这些威胁的关键手段之一。 在某平台发起的“网络安全从业人员现状调查”中,在“哪些与网络安全息息相关&…...

Unity 工具 之 Azure 微软连续语音识别ASR的简单整理
Unity 工具 之 Azure 微软连续语音识别ASR的简单整理 目录 Unity 工具 之 Azure 微软连续语音识别ASR的简单整理 一、简单介绍 二、实现原理 三、注意实现 四、实现步骤 五、关键脚本 一、简单介绍 Unity 工具类,自己整理的一些游戏开发可能用到的模块&#x…...
MLP-Mixer: An all-MLP Architecture for Vision
Abstract 在计算机视觉领域,卷积神经网络(CNNs)是首选的模型。最近,基于注意力机制的网络,如Vision Transformer,也变得流行起来。在这篇论文中,我们展示了卷积和注意力虽然都足以实现良好的性能,但它们两者都不是必需的。我们提出了MLP-Mixer,这是一种仅基于多层感知…...
redis前缀匹配数据迁移数据
背景: 阿里云的dts不支持前缀匹配迁移。 调研发现RedisShake可以前缀匹配迁移。 https://github.com/tair-opensource/RedisShake proxy 代理模式 阿里云的redis cluster 默认是proxy 代理模式, 不支持增量迁移。 如果要支持增量迁移需要开启 redis clu…...

云贝教育 |【技术文章】存储对象的LIBRARY CACHE LOCK/PIN实验(一)
注: 本文为云贝教育 刘峰 原创,请尊重知识产权,转发请注明出处,不接受任何抄袭、演绎和未经注明出处的转载。 实验环境 操作系统:Red Hat Enterprise Linux release 8.8 (Ootpa) 数据库:oracle Version 19.3.0.0.0 …...
C# 快速模指数运算 快速求余运算
此方法解决这样一个问题,就是a^b mod m 的余数是多少。 如果直接计算a^b,方次很大的时候,会溢出,而且时间很长。 当然指数很小的时候直接用自带的Math函数就行,如果指数很大的时候,可以用以下的方法。 原…...
Chisel入门初步0
注:以下所有配置在Ubuntu22.04笔记本中运行 chisel模板构建 复制项目模板文件 git clone https://github.com/schoeberl/chisel-examples.git安装vscode插件Metals 打开顶层目录,并设置为项目文件夹 打开终端输入 tree -L 3 # 查看三层目录结构得到…...
MySQL 8.0中移除的功能(一)
以下项目已经过时并在MySQL 8.0中被删除。如果有替代方案,请务必更新应用程序以使用这些替代方案。 对于在MySQL 8.0中被删除的功能,如果从MySQL 5.7源复制到MySQL 8.0副本时,可能会导致语句失败,或者在源和副本上产生不同的效果…...

可抓取性和可索引性:它们是什么以及如何影响搜索引擎优化
什么是可抓取性? 网页的可抓取性是指搜索引擎(如谷歌)发现网页的难易程度。 谷歌发现网页的过程称为爬行。它使用称为网络爬虫(也称为机器人或蜘蛛)的计算机程序。这些程序会跟踪网页之间的链接,以发现新…...

Django教程第4章 | Web开发实战-三种验证码实现
系列:Django学习教程 验证码的存在是为了防止系统被暴力破解攻击,几乎每个系统都有验证码。下面将介绍三种生成验证码方式。 您可以根据你自己的需要进行学习。 手动生成验证码 安装绘图依赖,利用的是画图模块 PIL 以及随机模块 random 在后…...

CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...
解锁数据库简洁之道:FastAPI与SQLModel实战指南
在构建现代Web应用程序时,与数据库的交互无疑是核心环节。虽然传统的数据库操作方式(如直接编写SQL语句与psycopg2交互)赋予了我们精细的控制权,但在面对日益复杂的业务逻辑和快速迭代的需求时,这种方式的开发效率和可…...

EtherNet/IP转DeviceNet协议网关详解
一,设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络,本网关连接到EtherNet/IP总线中做为从站使用,连接到DeviceNet总线中做为从站使用。 在自动…...
uniapp中使用aixos 报错
问题: 在uniapp中使用aixos,运行后报如下错误: AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...
腾讯云V3签名
想要接入腾讯云的Api,必然先按其文档计算出所要求的签名。 之前也调用过腾讯云的接口,但总是卡在签名这一步,最后放弃选择SDK,这次终于自己代码实现。 可能腾讯云翻新了接口文档,现在阅读起来,清晰了很多&…...

Linux部署私有文件管理系统MinIO
最近需要用到一个文件管理服务,但是又不想花钱,所以就想着自己搭建一个,刚好我们用的一个开源框架已经集成了MinIO,所以就选了这个 我这边对文件服务性能要求不是太高,单机版就可以 安装非常简单,几个命令就…...
跨平台商品数据接口的标准化与规范化发展路径:淘宝京东拼多多的最新实践
在电商行业蓬勃发展的当下,多平台运营已成为众多商家的必然选择。然而,不同电商平台在商品数据接口方面存在差异,导致商家在跨平台运营时面临诸多挑战,如数据对接困难、运营效率低下、用户体验不一致等。跨平台商品数据接口的标准…...

UE5 音效系统
一.音效管理 音乐一般都是WAV,创建一个背景音乐类SoudClass,一个音效类SoundClass。所有的音乐都分为这两个类。再创建一个总音乐类,将上述两个作为它的子类。 接着我们创建一个音乐混合类SoundMix,将上述三个类翻入其中,通过它管理每个音乐…...

路由基础-路由表
本篇将会向读者介绍路由的基本概念。 前言 在一个典型的数据通信网络中,往往存在多个不同的IP网段,数据在不同的IP网段之间交互是需要借助三层设备的,这些设备具备路由能力,能够实现数据的跨网段转发。 路由是数据通信网络中最基…...

高效的后台管理系统——可进行二次开发
随着互联网技术的迅猛发展,企业的数字化管理变得愈加重要。后台管理系统作为数据存储与业务管理的核心,成为了现代企业不可或缺的一部分。今天我们要介绍的是一款名为 若依后台管理框架 的系统,它不仅支持跨平台应用,还能提供丰富…...