协议:怎么设计可扩展且向后兼容的协议?

浏览器收到命令后会封装一个请求,并把请求发送到 DNS 解析出来的 IP 上,通过抓包工具我们可以抓到请求的数据包,如下图所示:

协议的作用

RPC 请求在发送到网络中之前,他需要把方法调用的请求参数转成二进制;转成二进制后,写入本地 Socket 中,然后被网卡发送到网络设备中。

但在传输过程中,RPC 并不会把请求参数的所有二进制数据整体一下子发送到对端机器上,中间可能会拆分成好几个数据包,也可能会合并其他请求的数据包(合并的前提是同一个 TCP 连接上的数据),至于怎么拆分合并,这其中的细节会涉及到系统参数配置和 TCP 窗口大小。对于服务提供方应用来说,他会从 TCP 通道里面收到很多的二进制数据,那这时候怎么识别出哪些二进制是第一个请求的呢?

为了避免语义不一致的事情发生,我们就需要在发送请求的时候设定一个边界,然后在收到请求的时候按照这个设定的边界进行数据分割。这个边界语义的表达,就是我们所说的协议。

如何设计协议

从 RPC 的作用说起,相对于 HTTP 的用处,RPC 更多的是负责应用间的通信,所以性能要求相对更高。但** HTTP 协议的数据包大小相对请求数据本身要大很多,又需要加入很多无用的内容,比如换行符号、回车符等;还有一个更重要的原因是,HTTP 协议属于无状态协议**,客户端无法对请求和响应进行关联,每次请求都需要重新建立连接,响应完成后再关闭连接。因此,对于要求高性能的 RPC 来说,HTTP 协议基本很难满足需求,所以 RPC 会选择设计更紧凑的私有协议。

那怎么设计一个私有 RPC 协议呢?

RPC 每次发请求发的大小都是不固定的,所以我们的协议必须能让接收方正确地读出不定长的内容。

先设计一个固定长度的:先固定一个长度(比如 4 个字节)用来保存整个请求数据大小,读取固定长度的位置里面的值,值的大小就代表协议体的长度,接着再根据值的大小来读取协议体的数据,整个协议可以设计成这样:

不定长协议:



但这种协议只是实现了正确的断句效果,在RPC中是行不通的,因为服务提供方并不知道它的序列化方式是什么,也就不能将二进制数据还原成对象。因此,需要将序列化方式单独拿出来,类似协议长度一样用固定的长度存放,这些需要固定长度存放的参数统称为“协议头”,整个协议就被拆分为:协议头协议体两部分。

在协议头里面,我们除了会放协议长度、序列化方式,还会放一些像协议标示、消息 ID、消息类型这样的参数,而协议体一般只放请求接口方法、请求的业务参数值和一些扩展属性。协议头是由一堆固定的长度参数组成,而协议体是根据请求接口和参数构造的,长度属于可变的,具体协议如下图所示:

定长协议:

1:Bit Offset——标识协议的其实位置

2:魔术位——标识是什么协议

3:整体长度——标识整个协议有多长,减去协议头长度就是协议体长度

4:头长度——标识协议头的长度,因为头是可扩展的,所以具体长度不固定,需要标识一下

5:协议版本——标识当前协议的版本,用于协议兼容性控制

6:消息类型——标识消息的类型,对于文本的需要,这里也需要嘛?协议类型可能是对象?可能是XML文件?可能是JSON码?正常来讲应该都是对象才对,让用于反序列化,猜测是为了扩展预留的

7:序列化方式——用于消息的序列化和反序列化

8:消息ID——用于表示请求和响应的关系

9:协议头扩展字段——用于扩展协议头,是协议具有扩展性,更加的灵活可控

10协议体——协议的内容,一堆堆的二进制数据,双方沟通的东西

协议头——规定信息转换的规则

协议体——信息真正的内容,由于在传输层对人不友好对应用程序也不友好需要转换一下

可扩展的协议

上面的协议为定长协议,那如果如果想在协议体重放一些扩展属性怎么办?

协议体里面是可以加新的参数,但这里有一个关键点,就是协议体里面的内容都是经过序列化出来的,也就是说你要获取到你参数的值,就必须把整个协议体里面的数据经过反序列化出来。但在某些场景下,这样做的代价有点高啊!

所以为了保证能平滑地升级改造前后的协议,我们有必要设计一种支持可扩展的协议。其关键在于让协议头支持可扩展,扩展后协议头的长度就不能定长了。

那要实现读取不定长的协议头里面的内容,在这之前肯定需要一个固定的地方读取长度,所以需要一个固定的写入协议头的长度。整体协议就变成了三部分内容:固定部分、协议头内容、协议体内容,前两部分我们还是可以统称为“协议头”,具体协议如下:

可扩展协议:

设计一个简单的 RPC 协议并不难,难的就是怎么去设计一个可“升级”的协议。不仅要让我们在扩展新特性的时候能做到向下兼容,而且要尽可能地减少资源损耗,所以我们协议的结构不仅要支持协议体的扩展,还要做到协议头也能扩展。

PS:

RPC 不直接用 HTTP 协议的一个原因是无法实现请求跟响应关联,每次请求都需要重新建立连接,响应完成后再关闭连接,所以我们要设计私有协议。那么在 RPC 里面,我们是怎么实现请求跟响应关联的呢?

一般RPC为了性能,会采用异步通信的方式,请求响应对应关联,就需要一个类似身份证号的ID,消息ID

RPC:设计可扩展且向后兼容的协议的更多相关文章

  1. 轻量级RPC设计与实现第五版(最终版)

    在最近一段时间里,通过搜集有关资料加上自己的理解,设计了一款轻量级RPC,起了一个名字lightWeightRPC.它拥有一个RPC常见的基本功能.主要功能和特点如下: 利用Spring实现依赖注入与 ...

  2. 指令集架构 x86-64 x86架构的64位拓展,向后兼容于16位及32位的x86架构

    https://zh.wikipedia.org/wiki/X86 x86泛指一系列英特尔公司用于开发处理器的指令集架构,这类处理器最早为1978年面市的"Intel 8086"C ...

  3. protobuf 向前兼容向后兼容

    http://blog.163.com/jiang_tao_2010/blog/static/12112689020114305013458/ 不错的protobuf.. protobuf的编码方式: ...

  4. 基于OAS设计可扩展OpenAPI

    前言 随着互联网行业的兴起,开发模式已逐步转换为微服务自治:小团队开发微服务,然后通过Restful接口相互调用.开发者们越来越渴望能够使用一种“官话”进行流畅的沟通,甚至实现多种编程语言系统的自动化 ...

  5. 【荐】说说CSS Hack 和向后兼容

    人一旦习惯了某些东西就很难去改,以及各种各样的原因,新的浏览器越来越多,而老的总淘汰不了.增长总是快于消亡导致了浏览器兼容是成了谈不完的话题.说 到浏览器兼容,CSS HACK自然而然地被我们想起.今 ...

  6. 实践javascript美术馆的小案例,学习到的东西还是蛮多的,包括javascript编程中的预留退路、分离javascript、以及实现向后兼容等

    javascript美术馆(改进2) 一.javascript编程过程中的好习惯 1.实现预留退路 js被禁掉,图片也可以显示出来,href属性带有图片路径 <script src=" ...

  7. 《javascript dom编程艺术》笔记(一)——优雅降级、向后兼容、多个函数绑定onload函数

    刚刚开始自学前端,如果不对请指正:欢迎各位技术大牛指点. 开始学习<javascript dom编程艺术>,整理一下学习到的知识.今天刚刚看到第六章,记下get到的几个知识点. 优雅降级 ...

  8. 采用truelicense进行Java规划license控制 扩展可以验证后,license 开始结束日期,验证绑定一个给定的mac住址

    采用truelicense进行Java规划license控制 扩展可以验证后,license 开始结束日期,验证绑定一个给定的mac住址. Truelicense 它是一个开源java license ...

  9. html与JacaScript中的重要思想:预留后路、向后兼容、js分离

    以一个简单的web程序为例 详细设计模式请配合代码及凝视食用 <!DOCTYPE html> <!-- 1 预留退路:假设用户禁用了js.链接还能正常显示吗?(href) 2 分离j ...

  10. 系统扩展与 macOS 不兼容

    系统扩展与 macOS 不兼容 某些系统扩展与当前版本的 macOS 不兼容或将与后续 macOS 版本不兼容 https://support.apple.com/zh-cn/HT210999 ref ...

随机推荐

  1. 2024.11.21随笔&联考总结(补)

    前言 都过了几天了,但是还是大概写一下吧,希望不要耽误太多时间. 考试 第一题签到题直接做. 第二题一眼是矩阵乘法优化 dp,然后大概看了几眼先不管去看第三题.然后第三题是数学题,感觉很可做.然后看到 ...

  2. 使用 SK 进行向量操作

    先祝大家 2025 新年好. 在 2024 年落地的 LLM 应用来看,基本上都是结合 RAG 技术来使用的.因为绝大多数人跟公司是没有 fine-turning 的能力的.不管是在难度还是成本的角度 ...

  3. 面试题32 - II. 从上到下打印二叉树 II

    地址:https://leetcode-cn.com/problems/cong-shang-dao-xia-da-yin-er-cha-shu-ii-lcof/ <?php /** 面试题32 ...

  4. 互联网寒冬下,如何写好一份.NET求职简历?附带简洁免费的简历模板!!!

    前言 在当今互联网行业的寒冬时期,每一位求职者都面临着更为激烈的竞争环境,如何在众多.NET候选人中脱颖而出,成为企业心仪的对象,用心准备一份简历显得尤为重要.简历不仅是个人职业经历的简要概述,更是向 ...

  5. verilator书写C++版模块testbench

    默认顶层模型名称为top,环境名称为contextp const std::unique_ptr<VerilatedContext> contextp{new VerilatedConte ...

  6. IvorySQL 4.0 发布:全面支持 PostgreSQL 17

    2024 年 12 月 24 日,IvorySQL 4.0 发布了! 这是 2024 年以来发布的最新版本,相较于上个版本,有了较大的变化. 该版本有多项增强:全面支持 PostgreSQL 17,新 ...

  7. Python设置递归最大深度

    博客地址:https://www.cnblogs.com/zylyehuo/ import sys sys.setrecursionlimit(100000) # 设置最大递归深度,默认是3000

  8. Top 出海 AI 公司招技术!HIX.AI & Pollo.ai | 深圳

    HIX.AI & Pollo.ai 招聘高级海外 AI 产品经理 (25-45K) 岗位职责: 负责Web 端海外 AI 产品的规划与策划,负责产品需求分析及原型设计,并制定方案推动产品研发落 ...

  9. Radmin远程自动登入管理工具

    功能说明: Radmin远程自动登入管理工具,服务器登入密码采用加密方式存储,软件可添加,编辑,删除服务器列表以及扫描服务器是否在线. 连接方式:有完全控制,仅查看,文件传输,关机等功能. 使用说明: ...

  10. [每日算法 - 华为机试] leetcode345 :反转字符串中的元音字母「双指针」

    入口 力扣https://leetcode.cn/problems/reverse-vowels-of-a-string/submissions/ 题目描述 给你一个字符串 s ,仅反转字符串中的所有 ...