# TinyRPC **Repository Path**: CodeNoobPromising/tiny-rpc ## Basic Information - **Project Name**: TinyRPC - **Description**: TinyRPC是一款自写的建议RPC框架,基于Java+Etcd+Vert.x+自定义协议实现。开发者可以引入SpringBootStarter,通过注解和配置文件快速使用TinyRPC,实现简易版的远程调用服务,另外支持通过SPI机制动态扩展序列化器,负载均衡器,提供重试和容错策略等等。 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-03-03 - **Last Updated**: 2025-03-08 ## Categories & Tags **Categories**: Uncategorized **Tags**: rpc ## README # TinyRPC ## 介绍 TinyRPC是一款自写的建议RPC框架,基于Java+Etcd+Vert.x+自定义协议实现。开发者可以引入SpringBootStarter,通过注解和配置文件快速使用TinyRPC,实现简易版的远程调用服务,另外支持通过SPI机制动态扩展序列化器,负载均衡器,提供重试和容错策略等等。 ## 技术选型 * ⭐️ Vert.x 框架 * Vert.x 使用 Netty 作为其网络通信层的基础框架。Netty 提供了高性能的异步事件驱动模型,Vert.x 在此基础上构建了更上层的抽象和工具。 * ⭐️ Etcd 云原生存储中间件(jetcd 客户端) * ZooKeeper 分布式协调工具(curator 客户端)(暂时没写) * ⭐️ SPI 机制(读取配置加载用户自定义或者系统指定的工具) * ⭐️ 多种序列化器 * JSON 序列化 * Kryo 序列化 * Hessian 序列化 * ⭐️ 多种设计模式 * 双检锁单例模式 * 工厂模式 * 代理模式 * 装饰者模式 * ⭐️ Spring Boot Starter 开发 * 反射和注解驱动 * Guava Retrying 重试库 ## 项目目录介绍 * tiny-rpc-core:TinyRPC 框架正式代码(项目启动后扩展的核心代码) * tiny-rpc-proto:TinyRPC 框架初始版(项目启动阶段的简易实现) * tiny-rpc-example-common:示例代码公用模块 * tiny-rpc-example-consumer:示例服务消费者 * tiny-rpc-example-provider:示例服务提供者 * tiny-rpc-example-springboot-consumer:示例服务消费者(Spring Boot 框架) * tiny-rpc-example-springboot-provider:示例服务提供者(Spring Boot 框架) * tiny-rpc-spring-boot-starter:注解驱动的 RPC 框架,可在 Spring Boot 项目中快速使用 ## 注册中心 1. 数据分布式存储:集中的注册信息数据存储、读取和共享 2. 服务注册:服务提供者上报服务信息到注册中心 3. 服务发现:服务消费者从注册中心拉取服务信息 4. 心跳检测测:定期检查服务提供者的存活状态 5. 服务注销:手动剔除节点、或者自动剔除失效节点 6. 更多优化点:比如注册中心本身的容错、服务消费者缓存等。 ### Etcd介绍及启动 * etcd 本地启动占用2379和2380端口 * 2379:提供HTTP APi服务,直接和etcdctl客户端交互 * 2380:集群中节点间通讯 ![img_3.png](img/img_3.png) ### EtcdKeeper - Etcd的可视化控制界面 ![img_4.png](img/img_4.png) ### 设计Etcd的KV存储结构 使用 **层级结构**,将服务理解为文件夹,将服务对应的多个节点理解为文件夹下的文件,可以通过服务名称用**前缀** 查询的方式查询到某个服务的所有节点 键名规则: `/业务前缀/服务名/服务节点地址` eg: key=/service/localhost:8080 ### 心跳检测及续期机制 ![img_8.png](img/img_8.png) ### 服务节点下线机制 下线分主动下线和被动下线 * 被动下线即服务提供方异常退出,这一点可以直接由Etcd提供的过期机制实现(等到过期后自动删除注册信息) * 主动下线即服务主动退出,于是该在服务提供方主动退出之前将注册信息清除 核心是**主动下线**,交给JVM的*ShutdownHook*机制,**在JVM即将关闭之前执行工作** ```Java /** * 框架初始化,支持传入自定义配置 * todo protoss版本:我们设置了消费方和提供方与注册中心的SPI机制配置化方式的建立连接,在此需要注册中心的初始化操作 * * @param newRpcConfig */ public static void init(TinyRpcConfig newRpcConfig) { rpcConfig = newRpcConfig; log.info("TinyRPC init..., config: {}", newRpcConfig.toString()); // 通过RPC的配置获取注册中心的配置项,于是初始化注册中心 RegistryCenterConfig registryCenterConfig = rpcConfig.getRegistryCenterConfig(); RegistryCenter registryCenter = RegistryCenterFactory.getRegistryCenterByType( registryCenterConfig.getRegistryCenterType()); registryCenter.registryCenterInit(registryCenterConfig); log.info("RegistryCenter init..., config:{}", registryCenterConfig); // 创建并注册ShutdownHook,在JVM退出前执行注册中心下线流程(将对应服务节点及时清楚注册信息) Runtime.getRuntime().addShutdownHook(new Thread(registryCenter::destroyRegistryCenter)); } ``` ### Etcd的watch监听机制 框架的设计提供消费方拉取注册中心中的服务列表缓存信息,而服务列表缓存也应该根据服务的状态动态变化。这就靠Etcd的watch监听机制实现。 以下是监听机制的步骤简述:(需要防止重复监听同一个key代表的服务列表) - 服务消费方-监听-Etcd - 服务提供者-从Etcd下线 - Etcd-通知服务消费方【服务下线信息】 - 服务消费方-处理-清除目标服务缓存信息 ## 自定义协议 HTTP协议是RPC框架网络传输的一种可选方式,然而RPC框架一般注重性能,HTTP协议请求响应格式以及头部信息很重,严重影响RPC网络传输的性能。因此自拟一套RPC的协议,自己定义请求响应格式实现高性能、灵活、安全的RPC框架。 ### 网络传输 * HTTP协议本身无状态,每次请求都是独立的,请求响应前都需要**重新建立和关闭连接** * 即便HTTP协议后续版本引入了Keep-Alive的持久连接,但HTTP和RPC框架的协议都属于应用层协议,性能不如传输层协议TCP ### 消息结构 用最少的空间传递必要信息 请求头结构: * magic【1 byte】:安全证书(也像JVM对编译得到的.class字节码中的魔数)、 * version【1 byte】:版本号,保证请求和响应的一致性 * serializationId【1 byte】:序列化方式 * type【Req/Res 1 byte】:区别请求或响应的类型 * status【1 byte】:响应结果 * TinyRPC Request ID【8 byte】:TCP请求唯一标识 * Body Length【4 byte】:应对TCP可能出现的半包、粘包问题,根据请求体数据长度获取完整body内容 * TinyRPC Request Body:请求体(内容) 因为协议是我们自拟的,请求头信息总长 17 byte,按照我们定义的顺序组成一个字节数组,就可以对封装的数组做【编码】和【解码】操作 【编码】:从空的Buffer缓冲区中按顺序写入字节数组的内容 【解码】:从已写入Buffer缓冲区的内容顺序读取,还原编码数据 ![img_11.png](img/img_11.png) ## 代码进度 ### Mock服务测试 ![img.png](img/img.png) ### Mock增强服务测试 ![img_1.png](img/img_1.png) ### SPI机制加载序列化器 配置自定义 hessian=com.york.tinyrpc.protoss.serializer.HessianSerializer json=com.york.tinyrpc.protoss.serializer.JsonSerializer Kryo=com.york.tinyrpc.protoss.serializer.KryoSerializer ![img_2.png](img/img_2.png) ### Etcd实现服务注册与发现 1. 单测 ![img_5.png](img/img_5.png) 2. 服务发现未获取到有效服务时见如下情况 ![img_6.png](img/img_6.png) 3. 服务发现获取到有效服务调用正常如下 ![img_7.png](img/img_7.png) ### Etcd监听示例 下方是消费方调用时【维护消费方从注册中心中已拉取到的服务列表缓存】的动态变化情况,基于Etcd的watch监听机制实现 【消费方】是【服务发现】的主要调用角色,消费方在第一次调用时触发了【服务发现】而把服务列表缓存,后续的调用都从缓存中获取该信息,且该缓存是可以信任的,因为是基于watch监听机制的,一切Key的动态变化都会触发在缓存中的状态 ![img_9.png](img/img_9.png) ### 测试基于Vert.x的TCP服务端和客户端之间的连接 ![img_10.png](img/img_10.png) ### 测试基于Vert.x的TCP协议消费提供方服务 ![img_12.png](img/img_12.png)