从零实现RPC框架之:4协议设计

2023-10-29

前言

一提到协议,最先想到的可能是 TCP 协议、UDP 协议等等,这些网络传输协议的实现以及应用层的HTTP协议。

其实rpc协议和http协议都属于应用层协议

可能你会问:“前面你不是说了 HTTP 协议跟 RPC 都属于应用层协议,那有了现成的 HTTP 协议,为啥不直接用,还要为 RPC 设计私有协议呢?”

这还要从 RPC 的作用说起,相对于 HTTP(1.1/1.0) 的用处,RPC 更多的是负责应用间的通信,所以性能要求相对更高。但 HTTP 协议的数据包大小相对请求数据本身要大很多,又需要加入很多无用的内容,比如换行符号、回车符等;(当然这一点http2.0已经改为二进制流式)

还有一个更重要的原因是,HTTP 协议属于无状态协议,客户端无法对请求和响应进行关联,每次请求都需要重新建立连接,响应完成后再关闭连接。因此,对于要求高性能的 RPC 来说,HTTP 协议基本很难满足需求,所以 RPC 会选择设计更紧凑的私有协议。

协议的作用

协议的作用:

  1. 协议通过定义规则、格式和语义来约定数据如何在网络间传输。一次成功的 RPC 需要通信的两端都能够按照协议约定进行网络字节流的读写和对象转换。如果两端对使用的协议不能达成一致,就会出现鸡同鸭讲,无法满足远程通信的需求。
  2. 同时 通过包的长度,解决TCP粘包和拆包的问题

如何设计协议?

在设计协议前,我们先梳理下要完成 RPC 通信的时候,在协议里面需要放哪些内容。

img

来看下 市面上的rpc框架采用的协议

RPC 协议的选择

协议是 RPC 的核心,它规范了数据在网络中的传输内容和格式。除必须的请求、响应数据外,通常还会包含额外控制数据,如单次请求的序列化方式、超时时间、压缩方式和鉴权信息等。

协议的内容包含三部分

  • 数据交换格式: 定义 RPC 的请求和响应对象在网络传输中的字节流内容,也叫作序列化方式
  • 协议结构: 定义包含字段列表和各字段语义以及不同字段的排列方式
  • 协议通过定义规则、格式和语义来约定数据如何在网络间传输。一次成功的 RPC 需要通信的两端都能够按照协议约定进行网络字节流的读写和对象转换。如果两端对使用的协议不能达成一致,就会出现鸡同鸭讲,无法满足远程通信的需求。

在这里插入图片描述

RPC 协议的设计需要考虑以下内容:

  • 通用性: 统一的二进制格式,跨语言、跨平台、多传输层协议支持
  • 扩展性: 协议增加字段、升级、支持用户扩展和附加业务元数据
  • 性能:As fast as it can be
  • 穿透性:能够被各种终端设备识别和转发:网关、代理服务器等 通用性和高性能通常无法同时达到,需要协议设计者进行一定的取舍。

HTTP/1.1

比于直接构建于 TCP 传输层的私有 RPC 协议,构建于 HTTP 之上的远程调用解决方案会有更好的通用性,如WebServices 或 REST 架构,使用 HTTP + JSON 可以说是一个事实标准的解决方案。

选择构建在 HTTP 之上,有两个最大的优势:

  • HTTP 的语义和可扩展性能很好的满足 RPC 调用需求。
  • 通用性,HTTP 协议几乎被网络上的所有设备所支持,具有很好的协议穿透性。

但也存在比较明显的问题:

  • 典型的 Request – Response 模型,一个链路上一次只能有一个等待的 Request 请求。会产生 HOL。
  • Human Readable Headers,使用更通用、更易于人类阅读的头部传输格式,但性能相当差
  • 无直接 Server Push 支持,需要使用 Polling Long-Polling 等变通模式

DUBBO

/dev-guide/images/dubbo_protocol_header.jpg

dubbo默认RPC协议是使用dubbo协议。dubbo协议分为报文头(也叫做Header)和报文体(也叫做Payload)。协议头占16个字节,协议体是可变长的,协议体是具体的请求/响应数据。

各个字段的详细介绍:
报文头:

  • Magic - Magic High & Magic Low (2字节)
    标识协议版本号,Dubbo 协议:0xdabb,该字段是一个常量值。

  • Req/Res (1 bit)
    标识是请求或响应。请求: 1; 响应: 0。

  • 2 Way (1 bit)
    仅在 Req/Res 为1(请求)时才有用,标记是否期望从服务器返回值。如果需要来自服务器的返回值,则设置为1。一般应用发送的请求都是1。

  • Event (1 bit)
    标识是否是事件消息,例如,心跳事件。如果这是一个事件,则设置为1。

  • Serialization ID (5 bit)
    标识序列化类型:比如 fastjson 的值为6。

  • Status (8 bits) 1字节
    仅在 Req/Res 为0(响应)时有用,用于标识响应的状态。

    • 20 - OK
    • 30 - CLIENT_TIMEOUT
    • 31 - SERVER_TIMEOUT
    • 40 - BAD_REQUEST
    • 50 - BAD_RESPONSE
    • 60 - SERVICE_NOT_FOUND
    • 70 - SERVICE_ERROR
    • 80 - SERVER_ERROR
    • 90 - CLIENT_ERROR
    • 100 - SERVER_THREADPOOL_EXHAUSTED_ERROR
  • Request ID (64 bits) 8字节
    标识唯一请求。类型为long。该值是一个自增值。每申请一次增加1。

  • Data Length (32 bits) 4字节
    序列化后的内容长度(标识协议体的长度),按字节计数。int类型。

报文体:

  • Variable Part

被特定的序列化类型(由序列化ID标识) 序列化后的内容,每个部分都是一个byte[]或者Byte

如果是请求包 ( Req/Res = 1),则每个部分依次为:

Dubbo version,dubbo协议版本号,比如在2.7.5版本里面,dubbo version是2.0.2
Service name,服务接口名
Service version,服务的group值
Method name,方法名
Method parameter types,参数类型
Method arguments,参数值
Attachments,附录

如果是响应包(Req/Res = 0),则每个部分依次为:
返回值类型(byte),标识从服务器端返回的值类型:
异常:RESPONSE_WITH_EXCEPTION=0
正常响应值: RESPONSE_VALUE=1
返回空值:RESPONSE_NULL_VALUE=2
带附录的异常返回值:RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS=3
带附录的正常响应值:RESPONSE_VALUE_WITH_ATTACHMENTS=4
带附录的空值:RESPONSE_NULL_VALUE_WITH_ATTACHMENTS=5
返回值:从服务端返回的响应bytes,如果返回值类型是2或者5,该字段是空
Attachments:当返回值类型是3、4、5时,则在响应包里面添加附录信息,在2.7.5版本里面,附录值只有dubbo协议的版本号,也就是2.0.2。

从协议设计上可以看出,报文体最大不能超过2^31字节,相当于2G大小。

Dubbo协议特性:

  • 连接个数:单连接(默认)
  • 连接方式:长连接(默认)
  • 传输协议:TCP(默认)
  • 传输方式:NIO 异步传输(默认)
  • 序列化:Hessian 二进制序列化(官方文档中也有建议使用kryo)
  • 适用范围:传入传出参数数据包较小(建议小于100K),消费者比提供者个数多,单一消费者无法压满提供者,尽量不要用 dubbo 协议传输大文件或超大字符串。
  • 适用场景:常规远程服务方法调用

针对协议特性,我想到了下面几个问题,我们来看一下dubbo如何解决的。

  1. 为什么dubbo适合传入传出较小的参数数据包?当传输大包的时候会有什么影响?

在官网上也对这个问题进行了分析。官网是从网络负载上分析的,包越大,网络负载越大,每秒传输的请求就越少。
首先dubbo最大可以传输2G的数据,dubbo没有对报文压缩,如果传输大报文,造成网络传输数据量过大,可能会造成网络拥塞;默认消费端发送请求数据后等待服务端返回,大数据包造成网络传输时间长,消费端长时间等待;dubbo最大只能传输2G的数据,过大的包,dubbo无法处理;dubbo是为在微服务环境下快速响应请求的场景设计的,传输大数据包与此设计相违背。

  1. dubbo协议为什么不适合传大包?

因为dubbo协议采用 单一长链接,如果每次请求的数据包大小为500KByte,假设网络为千兆网卡,每条连接最大为7MByte,

单个服务提供者的TPS最大为:128MByte / 500KByte = 262

单个消费者调用单个服务提供者的 TPS(每秒处理事务数)最大为:7MByte / 500KByte = 14。

所以,服务端受网卡限制。不受单挑连接限制。单挑连接受带宽限制

前提:单一长链接

分为服务端和消费端进行分析:

服务端受网卡约束,(会接收多条连接(消费端),单个连接不会成为瓶颈)
1024Mbit=128MByte

单个连接为什么最大7MByte?(56Mbps)

  1. Mbps=Mbit/s即兆比特每秒(1,000,000bit/s),Million bits per second的缩写是一种传输速率单位,指每秒传输的位(比特)数量。

  2. 传输速率是指设备的的数据交换能力,也叫“带宽”,单位是Mbps(兆位/秒),目前主流的集线器带宽主要有10Mbps、54Mbps/100Mbps自适应型、100Mbps和150Mbps四种。

所以,服务端受网卡限制。不受单条连接限制。单条连接受带宽限制。

通俗讲就是服务端接收多个客户端,不受单个连接传输速率限制。千兆网卡1024Mbit(128MByte).
单条连接传输受带宽限制,如果带宽为54Mbps,那么最大每秒传输为54Mbps/8约等于7MByte。

  1. 为什么使用长单连接?

避免每次握手的时间开销。

在Dubbo 文档中也提到了单连接设计的原因:

因为服务的现状大都是服务提供者少,通常只有几台机器,而服务的消费者多,可能整个网站都在访问该服务,比如 Morgan 的提供者只有 6 台提供者,却有上百台消费者,每天有 1.5 亿次调用,如果采用常规的 hessian 服务,服务提供者很容易就被压垮,通过单一连接,保证单一消费者不会压死提供者,长连接,减少连接握手验证等,并使用异步 IO,复用线程池,防止 C10K 问题。

  1. Netty使用异步数据传输,那么dubbo在哪些位置使用了异步?

异步有很多好处:可以使用少量线程处理大量请求,避免客户端等待,减少资源占用。
对于消费端,dubbo使用异步的地方是等待服务端返回值,可以通过参数“async”设置是否异步等待。
对于服务端,Netty收到请求后,将请求交给handler,handler使用异步线程调用最终的服务。异步线程中完成下面几件事:请求报文反序列化,构建Response对象,调用过滤器,访问最终的服务,构造响应报文,响应报文序列化,将返回结果发送到消费端。

GRPC

gRPC是google开源的高性能跨语言的RPC方案。gRPC的设计目标是在任何环境下运行,支持可插拔的负载均衡,跟踪,运行状况检查和身份验证。它不仅支持数据中心内部和跨数据中心的服务调用,它也适用于分布式计算的最后一公里,将设备,移动应用程序和浏览器连接到后端服务。

  • https://grpc.io/
  • https://github.com/grpc/grpc

下面从一个真实的gRPC SayHello请求,查看它在HTTP/2上是怎样实现的。用wireshark抓包:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8QcpjcQX-1651302509573)(http://hengyunabc.github.io/img/wireshark-grpc.png)]

可以看到下面这些Header:

Header: :authority: localhost:50051
Header: :path: /helloworld.Greeter/SayHello
Header: :method: POST
Header: :scheme: http
Header: content-type: application/grpc
Header: user-agent: grpc-java-netty/1.11.0

基于HTTP/2 协议的优点

1. 标准:HTTP/2 是一个公开的标准

Google本身把这个事情想清楚了,它并没有把内部的Stubby开源,而是选择重新做。现在技术越来越开放,私有协议的空间越来越小。

2. 实践检验:HTTP/2 是一个经过实践检验的标准

HTTP/2是先有实践再有标准,这个很重要。很多不成功的标准都是先有一大堆厂商讨论出标准后有实现,导致混乱而不可用,比如CORBA。HTTP/2的前身是Google的SPDY,没有Google的实践和推动,可能都不会有HTTP/2。

3.多端支持性:HTTP/2 天然支持物联网、手机、浏览器

实际上先用上HTTP/2的也是手机和手机浏览器。移动互联网推动了HTTP/2的发展和普及。

4.多语言支持性:基于HTTP/2 多语言的实现容易

只讨论协议本身的实现,不考虑序列化。

  • 每个流行的编程语言都会有成熟的HTTP/2 Client
  • HTTP/2 Client是经过充分测试,可靠的
  • 用Client发送HTTP/2请求的难度远低于用socket发送数据包/解析数据包

5.HTTP/2支持Stream和流控

stream就是http2的一个最小的数据单元 也就是数据包 一个包分为Header frame 和 data frame

在业界,有很多支持stream的方案,比如基于websocket的,或者rsocket。但是这些方案都不是通用的。

HTTP/2里的Stream还可以设置优先级,尽管在rpc里可能用的比较少,但是一些复杂的场景可能会用到。

6.HTTP/2 安全性有保证

  • HTTP/2 天然支持SSL,当然gRPC可以跑在clear text协议(即不加密)上。
  • 很多私有协议的rpc可能自己包装了一层TLS支持,使用起来也非常复杂。开发者是否有足够的安全知识?使用者是否配置对了?运维者是否能正确理解?
  • HTTP/2 在公有网络上的传输上有保证。比如这个CRIME攻击,私有协议很难保证没有这样子的漏洞。

7.HTTP/2 鉴权成熟

  • 从HTTP/1发展起来的鉴权系统已经很成熟了,可以无缝用在HTTP/2上
  • 可以从前端到后端完全打通的鉴权,不需要做任何转换适配

比如传统的rpc dubbo,需要写一个dubbo filter,还要考虑把鉴权相关的信息通过thread local传递进去。rpc协议本身也需要支持。总之,非常复杂。实际上绝大部分公司里的rpc都是没有鉴权的,可以随便调。

基于HTTP/2 的缺点

1. rpc的元数据的传输不够高效

尽管HPAC可以压缩HTTP Header,但是对于rpc来说,确定一个函数调用,可以简化为一个int,只要两端去协商过一次,后面直接查表就可以了,不需要像HPAC那样编码解码。
可以考虑专门对gRPC做一个优化过的HTTP/2解析器,减少一些通用的处理,感觉可以提升性能。

2.HTTP/2 里一次gRPC调用需要解码两次

一次是HEADERS frame,一次是DATA frame。

gRPC选择基于HTTP/2,那么它的性能肯定不会是最顶尖的。但是对于rpc来说中庸的qps可以接受,通用和兼容性才是最重要的事情。

  • 官方的benchmark:https://grpc.io/docs/guides/benchmarking.html
  • https://github.com/hank-whu/rpc-benchmark

NettyRPC中的协议设计

设计协议需要考虑的问题:

在本次造轮子项目中设计协议时借鉴并改进了dubbo协议,可以在此dubbo基础上做一些优化或者增加一些其他的特色,我这里举几个例子:

  1. 对报文体压缩,需要在报文头增加压缩类型字段,好处是减少报文包大小,但是处理报文时增加资源消耗;
    1. 可以直接进行压缩 也可以 接受方再根据压缩方法 进行解压
    2. 暴露方可以直接
  2. Data Length、Request ID字段占了比较大的空间,但是很多时候,这两个字段的值都很小,也就是说有效数字比较少,对于这样的字段可以采用变长整数表示,数值非常小时,只需要使用一个字节来存储,数值稍微大一点可以使用 2 个字节,再大一点就是 3 个字节等等;
  3. 极端情况如果对报文压缩过后 仍然较大 可以采用分片传输 借鉴IP层分片 DF MF字段 以及 片便宜长度。

总包体加入了 解决了

 0     1     2     3     4        5     6     7     8         9          10      11     12  13  14   15 16
 +-----+-----+------+-----+--------+----+----+----+------+-----------+-------+----- --+-----+-----+-------+
 |magic code|version|       full length      |标识 |    RequestId                                         |
 +-----------------------+--------+---------------------+-----------+-----------+-----------+------------+
 |                                                                                                       |
 |                                         body                                                          |
 |                                                                                                       |
 |                                        ... ...                                                        |
 +-------------------------------------------------------------------------------------------------------+
 2B   magic code(魔法数)   1B version(版本)   4B full length(消息长度)    2Bit  messageType(消息类型)
 2bit compress(压缩类型) 4bit codec(序列化类型)    8B    requestId(请求的Id

编码代码如下:

@Slf4j
public class RpcMessageEncoder extends MessageToByteEncoder<RpcMessage> {
    private static final AtomicLong ATOMIC_LONG = new AtomicLong(0);

    @Override
    protected void encode(ChannelHandlerContext ctx, RpcMessage rpcMessage, ByteBuf out) {
        try {
            out.writeBytes(RpcConstants.MAGIC_NUMBER);
            out.writeByte(RpcConstants.VERSION);
            // leave a place to write the value of full length
            //这里挺重要的 先留着 总长度的位置
            out.writerIndex(out.writerIndex() + 4);
            byte messageType = rpcMessage.getMessageType();
            out.writeByte(messageType);
            out.writeByte(rpcMessage.getCodec());
            out.writeByte(CompressTypeEnum.GZIP.getCode());
            out.writeLong(ATOMIC_LONG.getAndIncrement());
            // build full length
            byte[] bodyBytes = null;
            //总长度 先设置为报头长度
            int fullLength = RpcConstants.HEAD_LENGTH;
            // if messageType is not heartbeat message,fullLength = head length + body length
            if (messageType != RpcConstants.HEARTBEAT_REQUEST_TYPE
                    && messageType != RpcConstants.HEARTBEAT_RESPONSE_TYPE) {
                // serialize the object
                String codecName = SerializationTypeEnum.getName(rpcMessage.getCodec());
                log.info("codec name: [{}] ", codecName);
                Serializer serializer = ExtensionLoader.getExtensionLoader(Serializer.class)
                        .getExtension(codecName);
                bodyBytes = serializer.serialize(rpcMessage.getData());
                // compress the bytes
                String compressName = CompressTypeEnum.getName(rpcMessage.getCompress());
                Compress compress = ExtensionLoader.getExtensionLoader(Compress.class)
                        .getExtension(compressName);
                bodyBytes = compress.compress(bodyBytes);
                fullLength += bodyBytes.length;
            }

            if (bodyBytes != null) {
                out.writeBytes(bodyBytes);
            }
            int writeIndex = out.writerIndex();
            out.writerIndex(writeIndex - fullLength + RpcConstants.MAGIC_NUMBER.length + 1);
            out.writeInt(fullLength);
            out.writerIndex(writeIndex);
        } catch (Exception e) {
            log.error("Encode request error!", e);
        }

    }


}

协议头已经占据 2字节

4B = 32位 最大长度 2 32次方 -

消息类型分为:

ping pong req response

0 1 2 3

参考:

https://blog.csdn.net/hengyunabc/article/details/81120904

https://grpc.io/

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

从零实现RPC框架之:4协议设计 的相关文章

随机推荐

  • Linux ——objdump和readelf的使用

    在一个程序编译结束后 会生成 o的文件 但往往当我们用vim命令查看这个文件内容时 就全是乱码 像这样 那么我们应该如何查看 o文件呢 objdump和readelf的使用 objdump 首先先让我们了解一下什么是odjdump 作用 查
  • Gson的使用

    一 Gson的基本用法 生成json Gson gson new Gson User user new User 用户 28 String jsonObject gson toJson user name 用户 age 28 解析JSON
  • vue-格式化时间-封装时间过滤器

    一 全局封装时间处理过滤器 filter js export default function originVal const dt new Date originVal const y dt getFullYear const m dt
  • QT中QMainWindow、QWidget、QDialog的区别

    QMainWindow 详细描述 QMainWindow类提供一个有菜单条 锚接窗口 例如工具条 和一个状态条的主应用程序窗口 主窗口通常用在提供一个大的中央窗口部件 例如文本编辑或者绘制画布 以及周围菜单 工具条和一个状态条 QMainW
  • 2022 阿里全球数学竞赛获奖名单公布,其中 00 后选手占了一半多,如何评价这一现象?

    2022 阿里全球数学竞赛获奖名单公布 其中 00 后选手占了一半多 如何评价这一现象 写在前面 本届比赛共吸引55000余人报名 其中00后占比超4成 而在获奖的77人中 00后更是拿走了一半的奖项 00后 作为新时代科研新势力 正在慢慢
  • 系统接口日志记录-AOP

    在进行系统开发的时候 系统提供给前端或者第三方使用的接口 要对接口的调用情况 接口的接收的参数 返回的结果 调用者 调用接口的ip等 进行记录 通过Spring AOP的 环绕通知 可以很容易实现该功能 实现该功能对调用接口数据的记录也便于
  • zigbee中有几个不同的频率,是啥意思?有啥联系和区别

    zigbee中有几个不同的频率 是啥意思 有啥联系和区别 2011 05 30 09 03 cnsxgh 分类 工程技术科学 浏览1209次 channel 2405Mhz Pan ID 0x0628 工作频率 2 4Ghz 提问者采纳 2
  • JAVA中的时间大小比较

    1 时间的比较 import java text DateFormat import java text ParseException import java text SimpleDateFormat import java util D
  • Windows遇到ERR_NETWORK_ACCESS_DENIED处理方案

    问题描述 用了总部vpn 总是打不开总部资源 之前可以一直提示 禁止访问互联网ERR NETWORK ACCESS DENIED 郁闷了好几天 今天自己查查资料解决了 说明 问题总是能解决的 只是需要耐心 解决步骤 打开cmd 切以管理员身
  • window.open 使用方法总结

    1 最基本的弹出窗口代码 因为着是一段javascripts代码 所以它们应该放在之间 是对一些版本低的浏览器起作用 在这些老浏览器中不会将标签中的代码作为文本显示出来 要养成这个好习惯啊 window open test html 用于
  • 蓝牙之八-HFP

    HFP 在调试安卓的HFP client时遇到了如下问题 其中有一个E提示 因为AT命令的错误 所有创建SLC失败 然后断开RFCOMM链接 表现出来的是已经配对的手机不停的断开重连 HFP协议 HFP Hands free Profile
  • 2020重庆市法治理论知识考试答案查询收集

    重庆市法治理论知识考试平台考试题 下面就是搜集答案的代码 我得到的题目就下面这些 答案没放上来 我这也有 文档下载地址 链接 https pan baidu com s 1rLxqMdFUPxCjEVdpepq wg 提取码 dnyq 复制
  • 【HIT-软件构造】使用Gradle脱离IDE环境

    在软件构造实验中 我们将代码提交到GitHub仓库前 实验还要求我们使用其他工具build我们的实验代码以脱离环境 这些工具包括Ant Maven Gradle Travis CI等 然而这些构建工具不论是在线的还是离线的 都需要基于Gro
  • RestFul风格详解

    文章目录 一 前言 二 什么是RestFul风格 三 传统风格与RestFul风格对比 1 传统方式操作资源 2 RestFul方式操作资源 四 RestFul代码演示 1 代码展示 2 拓展情景 五 使用method属性指定请求类型 六
  • latex全文设置段间距_latex 标题、段落及行距

    资源 本文主要介绍了如何使用 titlesec 宏包设置各级标题样式 这里 是其官方手册 本文主要参考了该手册 如果想了解更多详情请直接查阅该手册 此外本文还对缩进 段距 行距的内容进行的简单的介绍 设置标题样式 标题样式的设置分为三个部分
  • 【leetcode刷题】-- 回文数(C++反转数组解法)

    题目链接 https leetcode cn com problems palindrome number 给你一个整数 x 如果 x 是一个回文整数 返回 true 否则 返回 false 回文数是指正序 从左向右 和倒序 从右向左 读都
  • 《生成对抗网络》综述(附257页ppt下载)

    地址 https sites google com view berkeley cs294 158 sp20 主要由UC伯克利教授Pieter Abbeel主讲 他曾师从吴恩达 现任伯克利机器人学习实验室主任 伯克利人工智能研究 BAIR
  • Python之小数转化为百分数

    1 有一个小数0 2233 我们需要将它转换成22 33 的形式 h 0 2233 print 2f h 100 这两行代码中 我们使用了三个百分符号 第一个百分符号靠近 2f 表示的就是以一个float的形式输出 2表示将保留两位小数进行
  • [ 环境搭建篇 ] 安装python环境并配置环境变量(附python3.10.3安装包)

    博主介绍 博主介绍 大家好 我是 PowerShell 很高兴认识大家 主攻领域 渗透领域 数据通信 通讯安全 web安全 面试分析 点赞 评论 收藏 养成习惯 一键三连 欢迎关注 一起学习 一起讨论 一起进步 文末有彩蛋 作者水平有限 欢
  • 从零实现RPC框架之:4协议设计

    前言 一提到协议 最先想到的可能是 TCP 协议 UDP 协议等等 这些网络传输协议的实现以及应用层的HTTP协议 其实rpc协议和http协议都属于应用层协议 可能你会问 前面你不是说了 HTTP 协议跟 RPC 都属于应用层协议 那有了