这是接上一篇文章《RSF 分布式服务框架设计》之后的续作,主要是 Hasor-RSF 协议层的设计。
RSF 的协议层设计可以很复杂也可以很简单。复杂是指这一层可以嵌套多层协议栈,让 RSF 的交互数据可以跑在各种协议之上,例如:HTTP、RTMP、也可以跑在相对底层的协议例如:TCP、UDP。
RSF 在协议层的设计上需要保留可能将来嵌套其它协议的支持,但不要设计的过于复杂。因此 RSF 的协议层的实现应该被分为两层,如下:
位于底层的传输协议可以是 TCP、UDP、RTMP、HTTP、等等各类听过或者没听过的网络协议。对于这些传输协议的要求只有一个 -- 那就是可以传输 RSF 数据包。
在 RSF 初期完全可以忽略传输协议这一层的设计,只要将 RSF 数据包丢到网络上然后远程程序接收到即可。因为传输协议无外乎是相当于给 RSF 数据包套了一层马甲,而这层马甲在初期作用并不大,可以随着完善逐步丰富。
接下来就是最关键的部分RSF 数据包的格式了,RSF 数据包的大体格式是“定长包头 + 变长包体”这种形式。并且协议被设计成为无状态的。
RSF数据交互形式:
RSF交互协议采用(请求/响应)这种模式,客户端发送某种类型的RSF数据包,服务器响应这种类型的请求。不需要握手协议。同时也是无状态的。
RSF数据包格式:
请求
* RSF 1.0 Request 协议 * --------------------------------------------------------bytes =13 * byte[1] version RSF版本(0xC1 or 0x81) * byte[8] requestID 请求ID * byte[1] keepData 保留区 * byte[3] contentLength 内容大小(max ~ 16MB) * --------------------------------------------------------bytes =10 * byte[2] servicesName-(attr-index) 远程服务名 * byte[2] servicesGroup-(attr-index) 远程服务分组 * byte[2] servicesVersion-(attr-index) 远程服务版本 * byte[2] servicesMethod-(attr-index) 远程服务方法名 * byte[2] serializeType-(attr-index) 序列化策略 * --------------------------------------------------------bytes =1 ~ 1021 * byte[1] paramCount 参数总数 * byte[4] ptype-0-(attr-index,attr-index) 参数类型1 * byte[4] ptype-1-(attr-index,attr-index) 参数类型2 * ... * --------------------------------------------------------bytes =1 ~ 1021 * byte[1] optionCount 选项参数总数 * byte[4] attr-0-(attr-index,attr-index) 选项参数1 * byte[4] attr-1-(attr-index,attr-index) 选项参数2 * ... * --------------------------------------------------------bytes =6 ~ 8192 * byte[2] attrPool-size (Max = 2047) 池大小 0x07FF * byte[4] att-length 属性1大小 * byte[4] att-length 属性2大小 * ... * --------------------------------------------------------bytes =n * dataBody 数据内容 * bytes[...]
响应
* RSF 1.0 Response 协议 * --------------------------------------------------------bytes =13 * byte[1] version RSF版本(0xC1 or 0x81) * byte[8] requestID 包含的请求ID * byte[1] keepData 保留区 * byte[3] contentLength 内容大小(max ~ 16MB) * --------------------------------------------------------bytes =8 * byte[2] status 响应状态 * byte[2] serializeType-(attr-index) 序列化策略 * byte[2] returnType-(attr-index) 返回类型 * byte[2] returnData-(attr-index) 返回数据 * --------------------------------------------------------bytes =1 ~ 1021 * byte[1] optionCount 选项参数总数 * byte[4] attr-0-(attr-index,attr-index) 选项参数1 * byte[4] attr-1-(attr-index,attr-index) 选项参数2 * ... * --------------------------------------------------------bytes =6 ~ 8192 * byte[2] attrPool-size (Max = 2047) 池大小 * byte[4] att-length 属性1大小 * byte[4] att-length 属性2大小 * ... * --------------------------------------------------------bytes =n * dataBody 数据内容 * bytes[...]
上面列出了RSF在网络通信中所使用的数据包结构。
RSF数据包的主要结构是: “Head + ContentLength + Content”,这种包结构可以很方便的在底层网络框架上予以解析而且简单易懂。相比较淘宝 HSF2.0 的协议 RSF 的扩展性还算蛮强的。
Head 头:
RSF 的协议头采用 13 字节固定长度,其中包括了RSF在通信上的 5 个基本数据(RSF协议标记、协议版本、包类型、请求ID,数据包长度)。
第一个字节中RSF目前规定只有:“0xC1” 和 “0x81” 两个值。其中第1个二进制位用于表示RSF数据包,第2个二进制位用来表示请求或者响应,后面6个二进制位用来表示RSF协议版本。
所以有了:“0x80”可以用来检验是否为RSF数据包、“0xC0”用来表示RSF 请求数据包、“0x80”用来表示RSF响应数据包。所以依照这个规则“B2、F2、DA、FF”等值都是合法的,不过因为目前RSF协议版本只有1.0,所以只有 0xC1 和 0x81 是有效的。
接下来8个字节用来表示一个 long 值,这个值是请求ID。请求ID的作用是区分同一客户端在并发调用的情况下区分不同调用而设计的。
在接下来的 1 个字节为保留位。随后的 3 个字节表示一个 int 类型,这个值表示的是 RSF 头之后的 RSF 数据包总长度。这样就限制了RSF数据包大小最约 16MB。
Content 结构:
RSF 的包体中协议格式和传输的数据是分离设计的,这种设计会让协议本身的结构显得非常清晰。所有的属性值都会保存在属性池中,并且以其在属性池中位值加以替代。例如:
服务名为:“org.example.hello.MyService”的信息转换为二进制之后,将其放入属性池,然后用其在属性池中的位置加以表示(2字节 short 类型)。
此外对于键值这种结构的数据将会采用(4字节来表示),其中 key 由前两个字节表示,value用后两位字节表示。而这 4个字节恰好可以使用 int 类型来表示。
依照上面两个规则就形成了 RSF 请求响应的数据结构。
最后是属性池的设计,属性池的最大容量是 65535 条。依照现有的RSF协议规定根本无法达到这个量级,所以是十分安全的。
属性池中的数据都是 4 字节,用来表述内容大小。例如上面那个服务名其在属性池中的值是 27。
最后 dataBody 部分将会按照属性池中的顺序一次将属性内容排列下去。
假设提取位于属性池 3 号位置的属性属性值是 23,其前面两个属性大小均为 10 字节,那么 3号属性的实际位置就是 dataBody区域的 20~43 位置的数据。
属性池的 0 号属性:
属性池中 0 号属性是 RSF 协议中规定的固定属性,其属性值为 0。作为 0 长度的属性在 dataBody 区域是不会产生任何数据输出。