# RpcFramework **Repository Path**: Ge-JY/rpc-framework ## Basic Information - **Project Name**: RpcFramework - **Description**: RPC框架项目后台代码(程序员鱼皮编程导航项目) - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2025-01-20 - **Last Updated**: 2025-02-11 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README V1.0:简易版本 + example-common:示例代码的公共依赖,包括接口、model等 + example-consumer:示例服务消费者代码 + example-provider:示例服务提供者代码 + rpc:RPC框架 + 提供一个Web服务器,用于接受处理请求、返回相应,可以使用Spring Boot内嵌的Tomcat、Netty等,此处使用NIO框架Vert.x + 提供一个本地服务注册器,主要目的是跑通流程,暂不引入第三方服务注册中心(nacos等) + 提供一个序列化器,用于对Java对象进行序列化和反序列化,此处使用Java原生的序列化器进行实现,也可以使用JSON、Hessian、Kryo、protobuf等 V2.0:完善优化 + 配置信息全局加载 + 支持Mock:Mock指模拟对象,通常用于测试代码中,如远程调用时,目标服务还未上线,就先模拟一个返回对象,便于跑通流程 + 序列化器 + 常用序列化器的对比 + JSON:易读性好,便于调试,跨语言支持广泛,但序列化后的数据量相对较大,不能很好地处理复杂的数据结构和循环引用 + Hessian:二进制序列化,序列化后的数据量较小,支持跨语言,但性能比JSON略低(对象需要转换为二进制格式),且对象必须实现Serializable接口 + Kryo:高性能,支持循环引用和自定义序列化器,无需实现Serializable接口,但仅支持Java,序列化格式不友好,不易读和调试 + Protobuf:高效,序列化后的数据量绩效,跨语言,但配置相对复杂,需要先定义数据结构的消息格式,序列化格式不易读懂 + 优化方向: + 使用更好的序列化器实现流程 + 由使用者指定要使用的序列化器 + 由使用者自己定制序列化器 + 优化内容 + 提供JSON、Kryo、Hessian序列化器的实现 + 提供序列化器工厂,使用工厂模式+单例模式,序列化器可以服用,没必要每次都重新创建 + 服务注册 + 通过注册中心帮助服务消费者获取服务提供者的调用地址,而不是将调用地址硬编码到项目中 + 主流的注册中心中间件有 ZooKeeper、Nacos 等,教程中使用 Etcd 存储注册信息,但 Etcd 的 0.6.x 以上版本要求 JDK11,此处先使用Redis进行注册信息缓存 + 与序列化器相同,基于SPI,保留后续扩展接口 + 优化内容: + 心跳检测和续期:通常是定期调用服务内的一个健康检查接口,判断节点是否正常提供服务;本项目用另一种思路,服务提供者初始化rpc框架、注册服务信息时,在本地缓存自身的节点注册信息,并定时更新服务注册的过期时间 + 服务节点下线通知:JVM的ShutdownHook机制,允许在JVM即将关闭前,执行一些清理工作,Spring Boot也有类似的优雅停机能力 + 消费端服务缓存:正常情况下,服务节点信息列表的更新频率不高,消费者在获取节点信息后,可以缓存在本地;同时消费者需要监听服务下线并及时更新缓存 + 注:本项目使用Redis进行服务注册实现,仅为仿照原项目etcd实现流程进行编写,未启动Redis进行测试 + 自定义协议 + HTTP协议的头信息、请求响应格式比较重,会影响网络传输性能,而RPC框架比较注重性能,因此基于传输层的TCP协议做一个自定义协议 + RPC消息需要的信息 + 魔数:用于安全校验(类似于HTTPS的安全证书) + 版本号:保证请求和响应的一致性 + 序列化方式:告诉服务端和客户端如何解析数据(类似HTTP的Content-Type内容类型) + 类型:标识是请求还是响应,还是心跳检测等其他用途 + 状态:如果是响应,记录响应的结果 + 请求id:TCP是双向通信,用于跟踪每个请求 + 请求体 + 请求体数据长度:TCP协议本身会有半包和粘包问题,用于完整获取请求体内容信息 + 使用TCP协议连续发送和接收请求时,可能出现接收到的消息与发送的消息不一致,半包是指接收到的消息比发送的消息少,粘包是指接收到的消息比发送的消息多 + 解决半包和粘包的核心思路是,在消息头中设置请求体的长度,每次只读取指定长度的信息,不完整时不读取,超出的部分留到下次再读取 + Vert.x框架中,提供了RecordParser.newFixed(messageLength),用于读取特定长度的字符 + 由于消费者ServiceProxy和提供者TcpServerHandler都需要接收Buffer,都要考虑读取数据的问题,使用**装饰者模式**进行代码封装,用RecordParser对原有的Buffer处理器进行增强 + 自定义消息结构时,需要尽量节省空间,使用轻量的类型,byte字节类型只占用1个字节、8个bit,由于Java实现bit位运算拼接比较麻烦,因此尽量给每个数据凑到整字节 + img.png + 整个消息结构设计如上图,共占用17个字节 + 此前使用HttpServer,Vert.x同样支持TCP服务器,但要注意Vert.x的TCP服务器收发的消息是Buffer类型,不能直接写入一个对象,因此需要编码器和解码器,将Java对象与Buffer进行相互转换 + 负载均衡 + 同一个服务可能会有多个提供者,为了合理利用多个节点的资源,降低单个节点的压力,要将请求转发给不同的提供者 + 常用的负载均衡技术有Nginx(七层负载均衡)、LVS(四层负载均衡)等 + 主要的负载均衡算法:轮询、随机、加权轮询、加权随机、最小连接数、IP Hash(根据客户端IP的哈希值选择服务器,保证同一客户端的请求始终被分配到同一台服务器,适用于保持会话一致性的场景) + 一致性Hash:一种经典的Hash算法,用于将请求分配到多个节点或服务器上,思路是将整个哈希值空间划分成一个环形,每个节点或服务器在环上占据一个位置,每个请求根据哈希值映射到环的一个点,顺时针寻找第一个大于等于该哈希值的节点 img.png + 这种结构可以处理**节点下线**的负载分摊问题,并可以通过将每个物理节点映射成多个虚拟节点,解决资源**倾斜**问题 + 重试机制 + 接口调用失败,有时可能只是网络不稳定或者服务提供者重启等临时性问题,服务消费者最好有自动重试的能力,提高系统可用性 + 重试机制的核心问题: + 重试条件:在什么条件下进行重试,什么条件下停止重试 + 重试时间:即重试等待策略 + 固定重试间隔:每次重试之间使用固定的时间间隔 + 指数退避重试:每次失败后,重试的间隔时间以指数级增加,以避免请求过于密集 + 随机延迟重试:每次重试之间使用随机的时间间隔,避免请求同时发生 + 可变延迟重试:根据先前重试的成功或失败情况,动态调整下一次的重试时间,如根据前一次的响应时间调整下一次重试的等待时间 + 重试工作:重试时,执行原本要做的操作,如果超过重试上限,可以通知告警让认为接入,也可以降级调用其他接口或执行其他操作 + 容错机制 + 在系统出现异常时,通过一定的策略保证系统稳定运行,提高系统的可靠性和健壮性 + 常见的容错策略: + Fail-Over 故障转移:一次调用失败后,切换另一个节点再次调用,也算是一种重试 + Fail-Back 失败自动恢复:某个功能出现调用失败或错误时,通过其他方式恢复该功能,相当于降级,如重试、调用其他服务等 + Fail-Safe 静默处理:非重要功能出现异常时,直接忽略掉,不做任何处理 + Fail-Fast 快速失败:出现调用错误时,立刻报错,交由外层调用方处理 + 容错实现方式:重试、限流、降级、熔断、超时控制等 + 框架启动优化 + 一个好用的框架,除了用户数、社区活跃度,还要简单易用易上手,最好能开箱急用 + 启动机制:将启动代码封装成一个专门的启动类,由服务提供者/服务消费者调用即可 + 服务提供者需要初始化框架配置、注册服务、启动Web服务器 + 服务消费者只需要初始化框架配置 + 注解驱动:通过一个注解,快速注册和使用服务,实现注解驱动有两种常见方式 + 主动扫描:让开发者指定要扫描的路径,遍历所有的类文件,针对有注解的类文件,执行自定义的操作 + 监听Bean加载:在Spring项目中,通过实现BeanPostProcessor接口,在Bean初始化后执行自定义操作 进一步完善扩展点: + RPC请求支持携带参数列表,如携带token进行安全校验等 + 服务管理界面,类似Nacos的注册中心面板 + 支持读取更多类型的配置文件 + 实现拦截器机制,参考Spring MVC,使用责任链模式,在服务提供者处理前后、消费者调用前后,增加拦截器,用于日志记录、安全校验等,可使用SPI机制支持用户自定义拦截器 + 自定义异常 + 支持指定版本号 + 支持消费方指定负载均衡、重试策略、容错策略、超时时间等 + 服务注册优化,支持区分服务key,缓存多个服务节点,支持服务分组