什么是RSocket?它有哪些优势?

在传统Web应用开发过程中,我们都是基于HTTP协议实现请求-响应式的交互方式。这种交互方案很简单,但不够灵活,也无法应对所有的响应式应用场景。那么,有没有在网络协议层上提供更加丰富的交互方式呢?答案是肯定的,这就是我们本文要讨论的RSocket协议。

RSocket协议

在引入RSocket协议之前,我们先来讨论为什么需要这样一个协议的背景,让我们从传统的请求-响应模式的问题开始说起。

请求-响应模式的问题

我们知道常用的HTTP 协议的优势在于其广泛的适用性,有非常多的服务器和客户端实现的支持,但 HTTP 协议本身比较简单,只支持请求-响应模式。而这种模式对于很多应用场景来说是不合适的。典型的例子是消息推送,以 HTTP 协议为例,如果客户端需要获取最新的推送消息,就必须使用轮询。客户端不停的发送请求到服务器来检查更新,这无疑造成了大量的资源浪费。请求-响应模式的另外一个问题是,如果某个请求的响应时间过长,会阻塞之后的其他请求的处理。

虽然服务器发送事件(Server-Sent Events,SSE)可以用来推送消息,不过 SSE 是一个简单的文本协议,仅提供有限的功能。此外,WebSocket 可以进行双向数据传输,但由于连接造成的服务之间的紧密耦合,WebSocket的使用不符合响应式系统要求,因为协议不提供控制背压的可能性,而背压是回弹性系统的重要组成部分。

事实上,响应式编程的实施目前主要有两个障碍,一个是关系型的数据访问,另一个就是网络协议,幸运的是,响应式流规范背后的团队理解了跨网络、异步、低延迟通信的必要性。在2015年中,RSocket协议就在这样的背景下诞生了。

RSocket协议与交互模式

Socket是一种新的第7层语言无关的应用网络协议,用来解决单一的请求-响应模式以及现有网络传输协议所存在的问题,提供Java,JavaScript,C++和Kotlin等多种语言的实现版本。

RSocket RSocket是一个二进制的协议,以异步消息的方式提供4种交互模式,除了请求-响应(request/response)模式之外,还包括请求-响应流(request/stream)、发后不管(fire-and-forget)和通道(channel)这三种新的交互模式。

  1. 请求-响应模式:这是最典型也最常见的模式。发送方在发送消息给接收方之后,等待与之对应的响应消息。
  2. 请求-响应流模式:发送方的每个请求消息,都对应于接收方的一个消息流作为响应。
  3. 即发即忘模式:发送方的请求消息没有与之对应的响应。
  4. 通道模式:在发送方和接收方之间建立一个双向传输的通道。

RSocket专门设计用于与响应式风格应用配合使用,在使用RSocket协议时,背压和流量控制仍然有效。

为了更好的理解RSocket协议,让我们将它与HTTP协议做一个对比。我们知道Servlet基于HTTP协议之上的是一套Java API规范,将HTTP请求转化为一个ServletRequest对象,并将处理结果封装成一个ServletResponse对象进行返回。HTTP协议为了兼容各种应用方式,本身有一定的复杂性,性能一般。而RSocket采用的是自定义二进制协议,本身的定位就是高性能通讯协议,性能上比HTTP高出一个数量级。

在交互模式上,与HTTP的请求-响应这种单向的交互模式不同,RSocket倡导的是对等通讯,不再使用传统的理解是客户端-服务器端改进,而是在两端之间可以自由的相互发送和处理请求。RSocket协议在交互方式上可以参考下图:


使用RSocket实现远程交互

想要在应用程序中使用RSocket协议,我们需要引入如下依赖:

<dependency>

    <groupId>io.rsocket</groupId>

    <artifactId>rsocket-core</artifactId>

</dependency>

<dependency>

    <groupId>io.rsocket</groupId>

    <artifactId>rsocket-transport-netty</artifactId>

</dependency>

可以看到这里使用了rsocket-transport-netty包,该包的底层实现就是Reactor Netty组件,支持TCP 和 WebSocket协议。如果你想使用UDP协议,那么可以引入rsocket-transport-aeron包。

RSocket接口

我们先来看一下RSocket协议中最核心的接口,即RSocket接口的定义,如下所示:

import org.reactivestreams.Publisher;

import reactor.core.publisher.Flux;

import reactor.core.publisher.Mono;

public interface RSocket extends Availability, Closeable {

    

//推送元信息,数据可以自定义

Mono<Void> metadataPush(Payload payload);

    //请求-响应模式,发送一个请求并接收一个响应。该协议也比 HTTP 更具优势,因为它是异步且多路复用的

Mono<Payload> requestResponse(Payload payload);

    //即发-即忘模式,请求-响应的优化,在不需要响应时非常有用

    Mono<Void> fireAndForget(Payload payload);

   

    //请求-响应流模式,类似于返回集合的请求/响应,集合将以流的方式返回,而不是等到查询完成

    Flux<Payload> requestStream(Payload payload);

    //通道模式,允许任意交互模型的双向消息流

    Flux<Payload> requestChannel(Publisher<Payload> payloads);    

}

显然,RSocket接口通过四个方法分别实现了它所提供的四种交互模式,其中requestResponse方法返回的是一个Mono<Payload>对象,这里的Payload代表的就是一种消息对象,由两部分组成,元信息metadata和数据data,类似于常见的消息通信中的消息头和消息体的概念。

然后,我们发现fireAndForget方法返回的是一个Mono<Void>流,符合即发-即忘模式的语义。而requestStream作为请求-响应流模式的实现,与requestResponse的区别在于它的返回值是一个Flux流,而不是一个Mono对象。最后,我们注意到这几个方法的输入都是一个Payload消息对象,而不是一个响应式流对象。但requestChannel方法就不一样了,它的输入同样是一个代表响应式流的Publisher对象,意味着这种模式下的输入输出都是响应式流,也就是说可以进行客户端和服务器端之间的双向交互。

在rsocket-core包中,针对RSocket接口提供了一个抽象的实现类AbstractRSocket,对上述方法做了简单的实现封装,在具体使用过程中,我们可以基于这个AbstractRSocket类来具体提供某一个交互模式的实现逻辑,而不需要完成实现RSocket接口中的所有方法。

使用RSocket的交互模式

这里以最常见的请求-响应交互模式为例,给出使用RSocket协议的使用方法。与使用HTTP协议一样,这个过程需要构建服务器端和客户端,并通过客户端来发起请求。

我们先来看如何构建RSocket服务器端,示例代码如下所示:

    RSocketFactory.receive()

        .acceptor(((setup, sendingSocket) -> Mono.just(

            new AbstractRSocket() {

              @Override

              public Mono<Payload> requestResponse(Payload payload) {

                return Mono.just(DefaultPayload.create("Hello: " + payload.getDataUtf8()));

              }

            }

        )))

        .transport(TcpServerTransport.create("localhost", 7000))

        .start()

        .subscribe();

这里的RSocketFactory.receive()方法返回用来创建服务器的 ServerRSocketFactory 类的对象。ServerRSocketFactory 的acceptor()方法的参数是 SocketAcceptor 接口。上述代码中,我们用到了前面介绍的RSocket抽象实现类AbstractRSocket,重写了其中的requestResponse()方法,对输入的参数前面添加一个"Hello: "前缀并返回。接下来的transport()方法指定ServerTransport接口的实现类TcpServerTransport作为 RSocket 底层的传输层实现,显然,我们监听本地服务器上的7000端口。最后,通过 start().subscribe()来触发整个启动过程。

构建完服务器端,我们来构建客户端组件,如下所示:

    RSocket socket = RSocketFactory.connect()

        .transport(TcpClientTransport.create("localhost", 7000))

        .start()

        .block();

RSocketFactory.connect() 方法用来创建 RSocket 客户端,返回 ClientRSocketFactory 类的对象。接下来的 transport() 方法指定传输层 ClientTransport 实现 。和服务器端组件TcpServerTransport对应,这里使用的是TcpClientTransport来连接本地服务器上的7000端口。最后调用start().block()方法等待客户端启动并返回RSocket对象。

现在,我们就可以使用RSocket的requestResponse()方法来发送请求并获取响应了,如下所示:

    socket.requestResponse(DefaultPayload.create("World"))

        .map(Payload::getDataUtf8)

        .doOnNext(System.out::println)

        .doFinally(signalType -> socket.dispose())

        .then()

        .block();

我们可以使用DefaultPayload.create()方法来简单地创建 Payload 对象,然后通过RSocket 类的 dispose() 方法用来销毁该对象。这样,整个调用过程就结束了。执行这次请求,我们会在控制台上获取“Hello: World”。

RSocket与框架集成

通常,我们不会直接使用RSocket开发库进行应用程序的开发,而是借助于特定的开发框架。在Java领域中,Spring Boot、Spring Cloud以及Dubbo等主流开发框架都集成了RSocket协议。

集成RSocket与Spring框架

想要在Spring Boot中使用RSocket协议,我们需要引入如下依赖:

<dependency>

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-rsocket</artifactId>

</dependency>

然后,我们同样先来构建一个请求-响应式交互方式。我们可以构建如下所示一个简单Controller:

@Controller

public class HelloController {

  @MessageMapping("hello")

  public Mono<String> hello(String input) {

    return Mono.just("Hello: " + input);

  }

}

注意到这里引入了一个新的注解@MessageMapping,跟@RequestMapping注解类似,@MessageMapping是Spring中提供用来指定WebSocket、RSocket等协议中消息处理的目的地。然后,我们输入了一个String类型的参数并返回一个Mono对象,符合请求-响应相互模式的定义。

为了访问这个RSocket端点,我们需要构建一个RSocketRequester对象,构建方式如下所示:

@Autowired

RSocketRequester.Builder builder;

RSocketRequester requester = builder.dataMimeType(MimeTypeUtils.TEXT_PLAIN)

        .connect(TcpClientTransport.create(7000)).block();

基于这个RSocketRequester对象,我们就可以通过它的route方法路由到前面通过@MessageMapping注解构建的"hello"端点,如下所示:

Mono<String> response = requester.route("hello")

        .data("World")

        .retrieveMono(String.class);

我们再来看一个请求-响应流的示例,如下所示:

@MessageMapping("stream")

Flux<Message> stream(Message request) {

        return Flux

                .interval(Duration.ofSeconds(1))

                .map(index -> new Message(request.getParam, index));

}

这里我们根据输入的Message对象,返回一个Flux流,每一秒发送一个添加了Index的新Message对象。

集成RSocket与其他框架

针对其他开发框架,Dubbo 在 3.0.0-SNAPSHOT 版本里基于 RSocket 对响应式编程提供了支持,开发人员可以非常方便的使用RSocket的API。而随着Spring框架的持续升级,5.2版本中也把RSocket作为缺省的通讯协议。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/624005.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

从“山寨的”MTK芯片发展历程 同类芯片比较基础常识 如何简单识别mtk机型

联发科技&#xff08;MediaTek Inc.简称“联发科” &#xff0c;联发科起家于CD-ROM芯片&#xff0c;直接将DVD内分别承担视频和数字解码功能的两颗芯片整合到了一颗芯片上&#xff0c;并提供相应的软件方案。并一度占据大陆DVD市场60%的芯片供应量&#xff01;他将图像处理、M…

《Boosting Object Detection with Zero-Shot Day-Night Domain Adaptation》2024CVPR

域不变特征:是指在不同的数据域或环境下,特征能够保持不变或具有一定程度的鲁棒性。实现域不变特征可以在许多计算机视觉和机器学习任务中具有重要的作用,特别是在涉及跨域或跨环境的应用场景中。 以下是一些常用的实施域不变特征的方法: 1. 数据归一化:通过将数据进行归一…

uniapp h5 配置代理服务器

"devServer": {"disableHostCheck": true,"proxy": {"/api": {// 需要被代理的后台地址"target": "http://自己的地址","changeOrigin": true,"secure": false,"pathRewrite": {&q…

LLMs应被视为一种文字计算器?

编者按&#xff1a;当前&#xff0c;大语言模型已经成为自然语言处理领域的热点。LLMs 是否真的“智能”&#xff1f;它们又为我们带来了哪些启发&#xff1f;针对这些问题&#xff0c;Darveen Vijayan 为我们带来了这篇引人深思的文章。 作者主要阐释了两个观点&#xff1a;第…

WCF 通信三种模式 请求与答复、单向、双工通信

WCF 通信三种模式 请求与答复 默认模式单向双工 请求与答复 [OperationContract] string GetInfo(string id); [OperationContract] void Getxxx();即使返回值是void 也属于请求与答复模式。 缺点&#xff1a;如果用WCF在程序A中上传一个2G的文件&#xff0c;那么要想执行程…

【设计模式】JAVA Design Patterns——Abstract Factory(抽象工厂模式)

&#x1f50d;目的 提供一个用于创建相关对象家族的接口&#xff0c;而无需指定其具体类 &#x1f50d;解释 真实世界例子 要创建一个王国&#xff0c;我们需要具有共同主题的对象。精灵王国需要精灵国王、精灵城堡和精灵军队&#xff0c;而兽人王国需要兽人国王、兽人城堡和兽…

部署YUM仓库及 NFS共享服务

YUM仓库服务 部署YUM软件仓库 使用YUM工具管理软件包 一、YUM概述 1.YUM (Yellow dog Updater Modified) 基于RPM包构建的软件更新机制可以自动解决依赖关系所有软件包由集中的YUM软件仓库提供 2. 准备安装源3-1 2.1 软件仓库的提供方式 FTP服务:ftp://..HTTP服务:htt…

常见加解密算法03 - RC4逆向认识

各位聪明绝顶&#xff0c;才高八斗的读者们你们好&#xff01;今天我们主要讨论编译之后的RC4算法识别。 题外话&#xff0c;之前看到一个蛋疼的小知识&#xff0c;说“势”这个字最好不好查词典释义。我是很好奇的&#xff0c;果然后来无法直视势不可挡这个成语。 言归正传&am…

网络安全之OSPF进阶

该文针对OSPF进行一个全面的认识。建议了解OSPF的基础后进行本文的一个阅读能较好理解本文。 OSPF基础的内容请查看&#xff1a;网络安全之动态路由OSPF基础-CSDN博客 OSPF中更新方式中的触发更新30分钟的链路状态刷新。是因为其算法决定的&#xff0c;距离矢量型协议是边算边…

【微信小程序开发】深入探索事件绑定、事件冒泡、页面跳转的逻辑实现

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

Windows Qt中支持heic 图片显示

安装vcpkg&#xff1a; git clone https://github.com/microsoft/vcpkg 执行脚本&#xff1a; .\vcpkg\bootstrap-vcpkg.bat 在安装之前如果需要指定vs的编译器&#xff0c; 在如下文件中做更改&#xff0c; 我指定的是用vs2019编译的&#xff1a; D:\vcpkg\vcpkg\triplets 增…

安科瑞AIM-D100-ES光伏储能系统直流绝缘监测仪

概述 AIM-D100-ES 型直流绝缘监测仪主要用于在线监测直流不接地系统正负极对地绝缘电阻&#xff0c;当绝缘电阻低于设定值时&#xff0c;能发出预警和报警信号。 产品可测 100-1500V 的直流系统&#xff0c;可应用于储能直流系统、电动汽车充电装置、UPS 供电系统、光伏直流系…

联合四川博物院跨界,探索五粮液700余年“活窖之美”

执笔 | 尼 奥 编辑 | 扬 灵 “川酒甲天下&#xff0c;精华在宜宾。”千百年来&#xff0c;宜宾得天独厚的自然风土&#xff0c;传承巴蜀大地的臻臻韵味&#xff0c;酝酿出“美酒哉”的和美五粮液&#xff0c;奠定大国浓香的品牌基石。 5月10日&#xff0c;“中国品牌日”如…

「Python绘图」绘制同心圆

python 绘制同心圆 一、预期结果 二、核心代码 import turtle print("开始绘制同心圆") # 创建Turtle对象 pen turtle.Turtle() pen.shape("turtle") # 移动画笔到居中位置 pen.pensize(2) #设置外花边的大小 # 设置填充颜色 pen.fillcolor("green&…

JSP相关题目练习

一、前置知识 【eclipse/IDEA】如何在IDE里创建一个Java Web项目&#xff1f; 1. 实现Bean类的User实例 以一个实现Bean类User的实例。在Eclipse里调用Tomcat服务器运行。 Javabean是一种Java类&#xff0c; 通过封装属性和方法成为具有某种功能或者处理某个业务的对象&…

ai电销机器人智能系统的应用场景包括什么?

随着科技的不断进步&#xff0c;传统的销售方式已经无法满足现代企业的需求&#xff0c;电销机器人智能系统可以在各种场景中发挥作用&#xff0c;其中一些主要的应用场景包括&#xff1a; 客户服务与支持&#xff1a;通过语音识别和自然语言处理技术&#xff0c;电销机器人可以…

812寸硅片为什么没有平边(flat)?

知识星球&#xff08;星球名&#xff1a;芯片制造与封测社区&#xff0c;星球号&#xff1a;63559049&#xff09;里的学员问&#xff1a;上期种说2&#xff0c;4&#xff0c;6寸硅片都有平边&#xff0c;那为什么8&12寸硅片只有一个notch&#xff1f;为什么不能像小尺寸晶…

汇编语言程序设计-2-访问寄存器和内存

2. 访问寄存器和内存 文章目录 2. 访问寄存器和内存2.0 导学2.1 寄存器及数据存储2.2 mov和add指令2.3 确定物理地址的方法2.4 内存的分段表示法2.5 Debug的使用2.6 【代码段】CS、IP与代码段2.7 【代码段】jmp指令2.8 【数据段】内存中字的存储2.9 【数据段】用DS和[address]实…

Transformers中加载预训练模型的过程剖析(一)

使用HuggingFace的Transformers库加载预训练模型来处理下游深度学习任务很是方便,然而加载预训练模型的方法多种多样且过程比较隐蔽,这在一定程度上会给人带来困惑。因此,本篇文章主要讲一下使用不同方法加载本地预训练模型的区别、加载预训练模型及其配置的过程,藉此做个记…

ARM64汇编09 - 分支指令与模式切换

本文主要讨论两部分内容&#xff1a; 分支指令&#xff0c;B、BL 等 v7中的模式切换&#xff0c;arm切thumb&#xff0c;thumb切arm。理解了模式切换就会明白为什么在做 inline hook 时&#xff0c;有些地址需要加上1&#xff0c;加上 1 的作用是什么。 B B指令是无条件跳转…