同前几篇博文,本次针对大神分享的PRC框架核心技术进行总结分享。欢迎讨论
RPC Client && RPC Server
RPC Client
1、动态代理,根据lookUp信息(接口-实现-方法)动态创建出代理类,(创建代理类==RPC服务端的目标接口)。即Lookup为远端目标接口地址,创建调用目标接口代理。
2、上下文管理,主要为标识每个请求的sessionID,保证server端返回的结果找到对应的上下文。例如同时ABC三个请求从客户端发送到server去请求,上下文就用来标识server返回的ABC response对应客户端发来的ABC request。
处理好client的请求数据后,经过序列化、经由两端统一的协议,传输到server端,进行反序列化操作解析出目标接口、参数等信息,执行invoke调用后,将结果再次序列化传输。
RPC Server
1、分发器,将请求分发到sever实例(负载策略&容错)
2、过滤器,统一拦截,在server方法执行前后进行链路调用统计、超时统计等操作
3、线程模型,下面深入总结。
核心流程
1、创建接口代理类:从客户端开始,利用动态代理创建server目标接口代理
2、序列化:将入参对象序列化
3、协议组装:将序列化方式、协议版本、协议类型、方法签名等信息组装成一个协议包,交付给传输层进行发送
4、传输,server协议:传输层将协议包发送到sever端,server端将协议包解析,从中取出接口签名、入参对象字节流信息
5、server反序列化:反序列化操作,找出接口实现类(server端真实的服务实现),invoke执行目标方法
6、server将结果序列化:Server端将返回结果按照同样的序列化方式进行序列化操作。
7、server组装协议:同样,将序列化方式、协议版本、协议类型、方法签名等信息组装成一个协议包,发送给client
8、client解析协议,反序列化结果:Client接收sever返回的协议包,经过解析协议包中返回结果流,反序列化操作,得到RPC方法调用结果
这就是完整的一个RPC请求流程 (下次问终于不至于provider、consumer、zk注册中心这码事儿了)
从RPC架构和流程不难看出,RPC核心技术包括:动态代理、序列化、协议、通讯(netty)、注册中心几块技术。动态代理就不多说了,可看上周的博文:JDK、CGLib、Javassist实现动态代理。本次重点讨论序列化反序列化、传输协议、server端的线程模型。
序列化载体:
xml:可扩展标记语言,自定义节点schema含义;但传输太多额外无用的数据(schema、节点解析)
Json:kv结构,易扩展
二进制字节流:紧凑,无额外多余数据,减少传输占用带宽;但字节顺序是固定的,例如user对象,1-8字节存储userId,8-16存储username;如果删减user属性,序列化反序列化时数据则对不上。则需要对属性添加tagId标识,tagID(sortedId)=1,则标识userId,tagId=2标识username;type标识属性类型(类型可知则知道对应占字节长度),value标识属性值;(其实这里用tagID标识属性名,根据属性名采用反射即可得到字段类型、长度信息;可能考虑效率,直接采用key标识)
这就定义好了采用字节流来序列化传输对象。
三、协议
常用的通讯协议、数据传输格式、底层传输协议、序列化方式、应用实现整理如下:
而公司自研的RPC框架采用自定义协议包。考虑到非开源,简单介绍,思路一致,非实际实现。
协议一般包括定长包头(公共属性)和变长包体(业务数据)组成。例如
1byte(版本号)|1-4byte(协议总长度)|5-8byte(序列号)|9byte(服务编号)|10byte(消息体类型)|11byte(压缩;算法)|12byte(序列化规则)
消息头总长度:12 byte
协议总长度=消息头总长度+消息体总长度(不含分界符)
序列化、协议(自定义)还需结合代码看实现逻辑,架构部同事提供的丰厚土壤,不吸收就辜负了。