常问问题
为什么要使用新协议?
可以在Motivations中找到我们创建新协议的动机的完整解释。
一些主要原因包括:
- 支持超出请求/响应的交互模型,例如流式响应和推送
- 跨网络边界的应用程序级流控制语义(有界批量大小的异步拉/推)
- 二进制,多路复用使用单个连接
- 支持跨传输连接恢复长期订阅
- 需要一个应用程序协议才能使用传输协议,如WebSockets和Aeron
为什么不用XYZ呢?
最终,所有上述动机都可以通过足够的努力在大多数事情上完成。那些参与启动这个项目的人希望更清洁,更正规化。换句话说,希望有一个不是黑客的解决方案。
为什么不是HTTP / 2?
HTTP / 2是多比HTTP / 1的浏览器和请求/响应文档传输更好,但不幸的是不暴露超出请求/响应交互模型,也不支持的应用程序级的流控制。
以下是HTTP / 2规范和常见问题解答中的一些引用,这些引用对于提供HTTP / 2定位的上下文非常有用:
“HTTP的现有语义保持不变。”
“……从应用程序的角度来看,协议的功能基本没有改变……”
“这项工作被特许用于修改有线协议 - 即,如何将HTTP标头,方法等放到线路上,而不是改变HTTP的语义。”
此外,“推送承诺”专注于为标准Web浏览行为填充浏览器缓存:
“推送的响应始终与客户的明确请求相关联。”
这意味着我们仍然需要SSE或WebSockets(并且SSE是文本协议,因此需要Base64编码为UTF-8)才能进行推送。
HTTP / 2意味着更好的HTTP / 1.1,主要用于网站浏览器中的文档检索。对于应用程序,我们可以比HTTP / 2做得更好。
另请参阅RSocket Motivations文档。
QUIC怎么样?
此时QUIC没有暴露或足够有用。如果/何时发生变化,我们希望将其用作RSocket的传输层。
RSocket专门用于在QUIC之类的东西上进行分层。QUIC提供可靠的传输,拥塞控制,字节级流控制和多路复用字节流。RSocket在这些事物之上层叠消息流的二进制成帧和行为语义(单向和双向),消息级流控制,恢复等。
RSocket规范是在考虑分层的情况下创建的,因此在像TCP这样的协议上,RSocket包括帧长度和流ID。但是在HTTP / 2或QUIC之类的东西上,RSocket会跳过这些并使用HTTP / 2或QUIC提供的那些。
请参阅“RSocket协议:成帧格式”:
当使用不提供兼容帧的传输协议时,帧长度必须预先添加到RSocket帧。
并参见“RSocket协议:帧头格式”:
包括多路分解在内的传输协议,如HTTP / 2,如果所有各方都同意,可以省略流ID字段。谈判和协议的手段留给运输协议。
为什么“反应流” request(n)流量控制?
如果没有完成工作单元(不是字节)的应用程序反馈,很容易导致“行头阻塞”,压倒网络和应用程序缓冲区,并在服务器上生成比客户端可以处理的更多数据。当在单个连接上复用多个流时,这尤其糟糕,其中一个流可能使所有其他流饿死。应用层request(n)语义允许消费者发出信号,表明它可以在每个流上接收多少,并允许生产者将多个流交织在一起。
以下是有关使用TCP并仅依赖其流量控制时可能出现的一些问题的更多详细信息:
- 数据由发送方和接收方的TCP缓冲,这意味着无法理解在用户级别完成的操作。
- 需要发送大型工作单元(大于TCP发送方或接收方的缓冲区)的发送方陷入行为不良的情况,其中TCP连接将在完全空和空之间循环,并且大大低估了缓冲(以及吞吐量)。
- TCP处理单个发送器/接收器对,并且Reactive Streams允许多个发送器和/或多个接收器(稍微),并且(最重要地)将传输层处的数据接收与应用程序消耗控制分离。应用程序可能希望人为地减慢或限制处理,而不是从传输中提取数据。
这一切都归结为TCP的设计目的(不会超出接收器OS缓冲区空间或网络队列)以及Reactive Streams流控制的目的(允许推/拉应用程序工作单元语义,其他传播模型和应用程序)控制何时准备好更多或不准备)。这种明确的关注点分离对于任何真正的系统有效运行都是必要的。
这说明了为什么每个在应用程序级别没有内置流控制的解决方案(除了MQTT,AMQP和STOMP之外提到的几乎所有解决方案)都不适合使用,以及为什么RSocket包含应用程序级别流量控制作为一流的要求。
连接设置要求
这实际上与交换SETTINGS帧的HTTP / 2要求相同 - 请参阅:
HTTP / 2和RSocket都需要与初始交换进行有状态连接。
传输层
HTTP / 2 需要TCP。RSocket 需要TCP,WebSockets或Aeron。
我们无意通过HTTP / 1.1运行它。我们也不打算在仅仅通过HTTP / 1.1 API(如浏览器公开)的情况下运行HTTP / 2,尽管可以在概念上进行探索(使用SSE或分块编码)。如果使用公开底层字节流的HTTP / 2实现,那么HTTP / 2可以用作传输(实际上这在RSocket的至少一个生产用途中完成)。
代理
对HTTP / 2行为正确的代理将对RSocket正常运行。
帧长
根据运输情况,它是可选的。
在TCP上,它将被包括在内。在Aeron或WebSockets上不需要它。
状态跨越连接
我们将此确定为此协议层的不必要优化,因为必须参与应用程序才能使其工作。应用程序在连接之间保持状 实现可忽略不计的增益也非常复杂。许多分布式系统实现无法正确处理这些类型的问题。
然而,RSocket协议确实为客户端和服务器提供了必要的通信机制,以维持状态并在新的传输连接上重新建立会话。
适应未来发展
没有办法完全满足未来的需求,但我们已经通过以下方式尝试了面向未来的RSocket:
- 帧类型具有扩展的保留值
- 错误代码具有扩展的保留值
- 安装程序有一个版本字段
- 所有字段都根据当前已知的给定要求进行了调整(例如
streamId支持4b请求) - 额外的标志有足够的空间
- 分离数据和元数据
- 在安装程序中使用MimeType消除与编码的耦合
此外,我们陷入了面向连接的HTTP / 2和TCP语义,因此连接行为不是异常或特殊的。
除了这些因素之外,TCP自1977年以来一直存在。我们不希望它在不久的将来被淘汰。QUIC在未来几年看起来是TCP的合法替代品。由于HTTP / 2已经在QUIC上工作,我们认为没有理由为什么RSocket也不能在QUIC上工作。
优先级,QoS,OOB
通过元数据,应用级逻辑和应用控制发射,允许优先级,QoS,OOB。RSocket不强制执行排队模型,也不强制执行排放模型,也不执行处理模型。为了有效地使用QoS,它需要控制所有方面。如果没有app逻辑以及底层网络层的合作,这实际上是不可能的。这也是HTTP / 2不进入该区域的原因相同,只是简单地提供表达意图的方法。使用元数据,RSocket甚至不需要这样做。
为什么需要取消?
现代分布式系统拓扑往往具有多级请求扇出。这意味着一个级别的一个请求可能会导致对多个后端的数十个请求。能够取消请求可以节省大量的工作。
什么是取消的示例用例?
让我们假设一个服务器负责计算Pi的第n位数。客户端向该服务器发送请求,但稍后意识到它不再需要/需要响应(出于任意原因)。它可以取消它(服务器可能甚至没有开始工作),而不是让服务器徒劳地进行计算。
请求流的示例用例是什么?
让我们想象一下聊天服务器; 您希望接收聊天服务器中所述的所有消息,但您不想轮询或连续轮询(长轮询技术)。另一个例子可能是您想要收听特定的聊天室并忽略所有其他消息。
什么是“即发即弃”与“请求 - 响应”的示例用例?
有些请求不需要响应,只要忽略任何发送响应的失败就可以了,“即发即弃”是正确的解决方案。一个例子可能是UDP不合适的环境中的非关键指标跟踪。
为什么二元?
请参阅HTTP / 2 FAQ:为什么是HTTP / 2二进制文件?
二进制编码不会使调试更难吗?
是的,但权衡是值得的。
二进制编码使得阅读消息对于人类来说更加困难,但它也使得阅读它们对于机器来说更容易。通过不解码内容也可以获得显着的性能提升。因为我们估计机器会读取99.9999999%的消息,所以我们决定让机器更容易阅读。
存在用于分析二进制协议交换的现有工具,并且可以容易地编写新工具和扩展以解码二进制RSocket格式并呈现人类可读文本。
调试协议有什么工具?
Wireshark是推荐的工具。该插件位于https://github.com/rsocket/rsocket-wireshark。
为什么这些不同的流量控制方法需要超出传输层提供的范围?
TCP流量控制旨在根据远程端的消耗率控制发送器/阅读器的字节速率。使用RSocket,流在同一传输连接上复用,因此在RSocket级别进行流控制实际上是强制性的。
RSocket流量控制有哪些帮助的示例用例?
流量控制有助于应用程序发出消耗响应的信号。这可确保我们永远不会溢出应用程序层上的任何队列。依赖TCP流控制不起作用,因为我们在同一连接上复用流。
RSocket流量控制如何表现?
有两种类型的流量控制:
- 一个是由请求-提供ñ(请在无流定义的语义读取规格为详尽的细节)。
- 第二个是通过Protocol文档中定义的租约语义提供的。
RSocket如何使数据中心的客户端负载均衡器受益?
每个RSocket都提供一个可用性编号,抽象地表示其发送流量的能力。例如,当客户端没有有效租约时,它会公开“0.0”可用性,表明它无法发送任何流量。这些额外的信息与已经使用的任何负载平衡策略相结合,可以为客户提供更多信息,从而做出更明智的决策。
为什么多路复用效率更高?
看到:
多路复用是否与流水线相当?
不行。流水线需要按请求的顺序读取响应。
例如,具有流水线:一个客户端发送reqA,reqB,reqC。它具有接收顺序对策:respA,respB,respC。
随着复用,同一个客户端可以以任意顺序接收响应,例如respB,respA,respC。
流水线操作会引入线路阻塞并降低系统性能。
为什么“TLS错误启动”策略对建立连接很有用?
在尊重租约语义时,在客户端和服务器之间建立RSocket需要一次往返(⇒SETUP,⇐LEASE,⇒REQUEST)。在慢速网络上或当连接延迟很重要时,此往返是有害的。这就是为什么你有可能不尊重租约,然后可以立即发送你的请求(⇒设置,⇒请求)。
Setup框架上有效负载数据的示例用例是什么?
您可能希望在RSocket建立时将数据传递给您的应用程序,而不是在RSocket上重新实现连接协议。RSocket允许您在SETUP帧旁边发送信息。例如,客户端可以使用它来发送其凭据。
为何选择多种互动模式
交互模型可以简化为一个:“请求通道”。每个其他交互模型都是请求通道的子类型,但它们是专门的,有两个原因:
- 从客户的角度来看易于使用。
- 性能。
那么为什么“RSocket”这个名字呢?
它最初是ReactiveSocket,但缩写为RSocket:
- 因为写作和发言时间较短
- 停止过度使用“被动”一词
也就是说,“R”仍然指的是“反应性”来自“ReactiveSocket”,它带来了我们的后续问题:“Reactive”不是一个完全炒作的流行语吗?
不幸的是,这个词已成为一个流行语,并且过度使用。
但是,这个库与几个项目直接相关,其中“Reactive”是其名称和架构模式的重要组成部分。具体来说,RSocket实现,使用或遵循这些项目和库中的原则,因此名称: