├── .gitignore ├── README.md ├── docs ├── structure.jpg └── tutorial.jpg ├── example-common ├── .gitignore ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── yupi │ └── example │ └── common │ ├── model │ └── User.java │ └── service │ └── UserService.java ├── example-consumer ├── .gitignore ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── yupi │ │ └── example │ │ └── consumer │ │ ├── ConsumerExample.java │ │ ├── EasyConsumerExample.java │ │ └── UserServiceProxy.java │ └── resources │ └── application.properties ├── example-provider ├── .gitignore ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── yupi │ │ └── example │ │ └── provider │ │ ├── EasyProviderExample.java │ │ ├── ProviderExample.java │ │ └── UserServiceImpl.java │ └── resources │ └── application.properties ├── example-springboot-consumer ├── .gitignore ├── pom.xml └── src │ ├── main │ └── java │ │ └── com │ │ └── yupi │ │ └── examplespringbootconsumer │ │ ├── ExampleServiceImpl.java │ │ └── ExampleSpringbootConsumerApplication.java │ └── test │ └── java │ └── com │ └── yupi │ └── examplespringbootconsumer │ └── ExampleServiceImplTest.java ├── example-springboot-provider ├── .gitignore ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── yupi │ └── examplespringbootprovider │ ├── ExampleSpringbootProviderApplication.java │ └── UserServiceImpl.java ├── yu-rpc-core ├── .gitignore ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── yupi │ │ │ └── yurpc │ │ │ ├── RpcApplication.java │ │ │ ├── bootstrap │ │ │ ├── ConsumerBootstrap.java │ │ │ └── ProviderBootstrap.java │ │ │ ├── config │ │ │ ├── RegistryConfig.java │ │ │ └── RpcConfig.java │ │ │ ├── constant │ │ │ └── RpcConstant.java │ │ │ ├── exception │ │ │ └── RpcException.java │ │ │ ├── fault │ │ │ ├── retry │ │ │ │ ├── FixedIntervalRetryStrategy.java │ │ │ │ ├── NoRetryStrategy.java │ │ │ │ ├── RetryStrategy.java │ │ │ │ ├── RetryStrategyFactory.java │ │ │ │ └── RetryStrategyKeys.java │ │ │ └── tolerant │ │ │ │ ├── FailBackTolerantStrategy.java │ │ │ │ ├── FailFastTolerantStrategy.java │ │ │ │ ├── FailOverTolerantStrategy.java │ │ │ │ ├── FailSafeTolerantStrategy.java │ │ │ │ ├── TolerantStrategy.java │ │ │ │ ├── TolerantStrategyFactory.java │ │ │ │ └── TolerantStrategyKeys.java │ │ │ ├── loadbalancer │ │ │ ├── ConsistentHashLoadBalancer.java │ │ │ ├── LoadBalancer.java │ │ │ ├── LoadBalancerFactory.java │ │ │ ├── LoadBalancerKeys.java │ │ │ ├── RandomLoadBalancer.java │ │ │ └── RoundRobinLoadBalancer.java │ │ │ ├── model │ │ │ ├── RpcRequest.java │ │ │ ├── RpcResponse.java │ │ │ ├── ServiceMetaInfo.java │ │ │ └── ServiceRegisterInfo.java │ │ │ ├── protocol │ │ │ ├── ProtocolConstant.java │ │ │ ├── ProtocolMessage.java │ │ │ ├── ProtocolMessageDecoder.java │ │ │ ├── ProtocolMessageEncoder.java │ │ │ ├── ProtocolMessageSerializerEnum.java │ │ │ ├── ProtocolMessageStatusEnum.java │ │ │ └── ProtocolMessageTypeEnum.java │ │ │ ├── proxy │ │ │ ├── MockServiceProxy.java │ │ │ ├── ServiceProxy.java │ │ │ └── ServiceProxyFactory.java │ │ │ ├── registry │ │ │ ├── EtcdRegistry.java │ │ │ ├── LocalRegistry.java │ │ │ ├── Registry.java │ │ │ ├── RegistryFactory.java │ │ │ ├── RegistryKeys.java │ │ │ ├── RegistryServiceCache.java │ │ │ ├── RegistryServiceMultiCache.java │ │ │ └── ZooKeeperRegistry.java │ │ │ ├── serializer │ │ │ ├── HessianSerializer.java │ │ │ ├── JdkSerializer.java │ │ │ ├── JsonSerializer.java │ │ │ ├── KryoSerializer.java │ │ │ ├── Serializer.java │ │ │ ├── SerializerFactory.java │ │ │ └── SerializerKeys.java │ │ │ ├── server │ │ │ ├── HttpServer.java │ │ │ ├── HttpServerHandler.java │ │ │ ├── VertxHttpServer.java │ │ │ └── tcp │ │ │ │ ├── TcpBufferHandlerWrapper.java │ │ │ │ ├── TcpServerHandler.java │ │ │ │ ├── VertxTcpClient.java │ │ │ │ └── VertxTcpServer.java │ │ │ ├── spi │ │ │ └── SpiLoader.java │ │ │ └── utils │ │ │ └── ConfigUtils.java │ └── resources │ │ └── META-INF │ │ ├── rpc │ │ ├── custom │ │ │ └── com.yupi.yurpc.serializer.Serializer │ │ └── system │ │ │ ├── com.yupi.yurpc.fault.retry.RetryStrategy │ │ │ ├── com.yupi.yurpc.fault.tolerant.TolerantStrategy │ │ │ ├── com.yupi.yurpc.loadbalancer.LoadBalancer │ │ │ ├── com.yupi.yurpc.registry.Registry │ │ │ └── com.yupi.yurpc.serializer.Serializer │ │ └── services │ │ └── com.yupi.yurpc.serializer.Serializer │ └── test │ └── java │ └── com │ └── yupi │ └── yurpc │ ├── fault │ └── retry │ │ └── RetryStrategyTest.java │ ├── loadbalancer │ └── LoadBalancerTest.java │ ├── protocol │ └── ProtocolMessageTest.java │ └── registry │ └── RegistryTest.java ├── yu-rpc-easy ├── .gitignore ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── yupi │ └── yurpc │ ├── model │ ├── RpcRequest.java │ └── RpcResponse.java │ ├── proxy │ ├── ServiceProxy.java │ └── ServiceProxyFactory.java │ ├── registry │ └── LocalRegistry.java │ ├── serializer │ ├── JdkSerializer.java │ └── Serializer.java │ └── server │ ├── HttpServer.java │ ├── HttpServerHandler.java │ └── VertxHttpServer.java └── yu-rpc-spring-boot-starter ├── .gitignore ├── pom.xml └── src └── main └── java └── com └── yupi └── yurpc └── springboot └── starter ├── annotation ├── EnableRpc.java ├── RpcReference.java └── RpcService.java └── bootstrap ├── RpcConsumerBootstrap.java ├── RpcInitBootstrap.java └── RpcProviderBootstrap.java /.gitignore: -------------------------------------------------------------------------------- 1 | ### Java template 2 | # Compiled class file 3 | *.class 4 | .idea 5 | 6 | # Log file 7 | *.log 8 | 9 | # BlueJ files 10 | *.ctxt 11 | 12 | # Mobile Tools for Java (J2ME) 13 | .mtj.tmp/ 14 | 15 | # Package Files # 16 | *.jar 17 | *.war 18 | *.nar 19 | *.ear 20 | *.zip 21 | *.tar.gz 22 | *.rar 23 | 24 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 25 | hs_err_pid* 26 | replay_pid* 27 | 28 | ### JetBrains template 29 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 30 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 31 | 32 | # User-specific stuff 33 | .idea/**/workspace.xml 34 | .idea/**/tasks.xml 35 | .idea/**/usage.statistics.xml 36 | .idea/**/dictionaries 37 | .idea/**/shelf 38 | 39 | # AWS User-specific 40 | .idea/**/aws.xml 41 | 42 | # Generated files 43 | .idea/**/contentModel.xml 44 | 45 | # Sensitive or high-churn files 46 | .idea/**/dataSources/ 47 | .idea/**/dataSources.ids 48 | .idea/**/dataSources.local.xml 49 | .idea/**/sqlDataSources.xml 50 | .idea/**/dynamic.xml 51 | .idea/**/uiDesigner.xml 52 | .idea/**/dbnavigator.xml 53 | 54 | # Gradle 55 | .idea/**/gradle.xml 56 | .idea/**/libraries 57 | 58 | # Gradle and Maven with auto-import 59 | # When using Gradle or Maven with auto-import, you should exclude module files, 60 | # since they will be recreated, and may cause churn. Uncomment if using 61 | # auto-import. 62 | # .idea/artifacts 63 | # .idea/compiler.xml 64 | # .idea/jarRepositories.xml 65 | # .idea/modules.xml 66 | # .idea/*.iml 67 | # .idea/modules 68 | # *.iml 69 | # *.ipr 70 | 71 | # CMake 72 | cmake-build-*/ 73 | 74 | # Mongo Explorer plugin 75 | .idea/**/mongoSettings.xml 76 | 77 | # File-based project format 78 | *.iws 79 | 80 | # IntelliJ 81 | out/ 82 | 83 | # mpeltonen/sbt-idea plugin 84 | .idea_modules/ 85 | 86 | # JIRA plugin 87 | atlassian-ide-plugin.xml 88 | 89 | # Cursive Clojure plugin 90 | .idea/replstate.xml 91 | 92 | # SonarLint plugin 93 | .idea/sonarlint/ 94 | 95 | # Crashlytics plugin (for Android Studio and IntelliJ) 96 | com_crashlytics_export_strings.xml 97 | crashlytics.properties 98 | crashlytics-build.properties 99 | fabric.properties 100 | 101 | # Editor-based Rest Client 102 | .idea/httpRequests 103 | 104 | # Android studio 3.1+ serialized cache file 105 | .idea/caches/build_file_checksums.ser 106 | 107 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 鱼皮 RPC 框架 2 | 3 | > 从 0 到 1,带你开发自己的 RPC 框架 4 | > 5 | > 鱼皮原创项目教程系列:https://yuyuanweb.feishu.cn/wiki/SePYwTc9tipQiCktw7Uc7kujnCd 6 | 7 | 8 | 9 | ## 项目介绍 10 | 11 | 基于 Java + Etcd + Vert.x 的高性能 RPC 框架,用新颖的技术栈从 0 到 1 带大家开发轮子。教程由浅入深,可以学习并实践基于 Vert.x 的网络服务器、序列化器、基于 Etcd 和 ZooKeeper 的注册中心、反射、动态代理、自定义网络协议、多种设计模式(单例 / 工厂 / 装饰者等)、负载均衡器设计、重试和容错机制、Spring Boot Starter 注解驱动开发等,大幅提升架构设计能力。 12 | 13 | 项目分为基础版和扩展版: 14 | 15 | - 基础版只需学几个小时,就能写在简历上的小项目~ 16 | - 扩展版将是充满亮点的技术类项目,搭配一个业务项目,让简历更有竞争力。 17 | 18 | > 开始学习或了解详情:[手写 RPC 框架(24 年最新)](https://yuyuanweb.feishu.cn/wiki/EweIwSYsXiEvCDkzS8BcTiAonyc) 19 | > 20 | > 开源地址:https://github.com/liyupi/yu-rpc 21 | > 22 | > [教程第一章可免费学习](https://www.code-nav.cn/course/1768543954720022530/section/1768545847093518337) 23 | > 24 | > [导学视频 - RPC 讲解](https://www.bilibili.com/video/BV1AJ4m1H7XL) 25 | 26 | 27 | 28 | ## 项目展示 29 | 30 | 目录结构: 31 | 32 | ![](./docs/tutorial.jpg) 33 | 34 | 35 | 详细的保姆级文字教程: 36 | 37 | ![](./docs/structure.jpg) 38 | 39 | 40 | 41 | ## 技术选型 42 | 43 | ### 后端 44 | 45 | 后端技术以 Java 为主,但所有的思想和设计都是可以复用到其他语言的,代码不同罢了。 46 | 47 | - ⭐️ Vert.x 框架 48 | - ⭐️ Etcd 云原生存储中间件(jetcd 客户端) 49 | - ZooKeeper 分布式协调工具(curator 客户端) 50 | - ⭐️ SPI 机制 51 | - ⭐️ 多种序列化器 52 | - JSON 序列化 53 | - Kryo 序列化 54 | - Hessian 序列化 55 | - ⭐️ 多种设计模式 56 | - 双检锁单例模式 57 | - 工厂模式 58 | - 代理模式 59 | - 装饰者模式 60 | - ⭐️ Spring Boot Starter 开发 61 | - 反射和注解驱动 62 | - Guava Retrying 重试库 63 | - JUnit 单元测试 64 | - Logback 日志库 65 | - Hutool、Lombok 工具库 66 | 67 | 68 | 69 | ## 源码目录 70 | 71 | - yu-rpc-core:鱼皮 RPC 框架核心代码 72 | - yu-rpc-easy:鱼皮 RPC 框架简易版(适合新手入门) 73 | - example-common:示例代码公用模块 74 | - example-consumer:示例服务消费者 75 | - example-provider:示例服务提供者 76 | - example-springboot-consumer:示例服务消费者(Spring Boot 框架) 77 | - example-springboot-provider:示例服务提供者(Spring Boot 框架) 78 | - yu-rpc-spring-boot-starter:注解驱动的 RPC 框架,可在 Spring Boot 项目中快速使用 79 | 80 | 81 | 82 | ## 项目教程大纲 83 | 84 | 这个项目内容非常多,大家可以看看有没有自己想学的知识点。 85 | 86 | 87 | 88 | ### 第一章:RPC 框架简易版 89 | 90 | 1. RPC 基本概念和作用 91 | 2. RPC 框架实现思路 | 基本设计 92 | 3. RPC 框架实现思路 | 扩展设计 93 | 4. 简易版 RPC 开发 | 项目初始化 94 | 5. 简易版 RPC 开发 | web 服务器 95 | 6. 简易版 RPC 开发 | 本地服务注册器 96 | 7. 简易版 RPC 开发 | 序列化器 97 | 8. 简易版 RPC 开发 | 请求处理器 98 | 9. 简易版 RPC 开发 | 消费者代理 99 | 10. 简易版 RPC 开发 | 测试验证 100 | 101 | 102 | 103 | ### 第二章:RPC 框架扩展版 104 | 105 | 1. 全局配置加载 | 扩展版项目初始化 106 | 2. 全局配置加载 | 配置加载实现 107 | 3. 全局配置加载 | 维护全局配置对象 108 | 4. 接口 Mock 设计实现 109 | 5. 序列化器 | 主流序列化器对比 110 | 6. 序列化器 | 多种序列化器实现 111 | 7. 序列化器 | SPI 机制 112 | 8. 序列化器 | 可扩展序列化器实现(SPI + 工厂模式) 113 | 9. 注册中心 | 注册中心核心能力 114 | 10. 注册中心 | 注册中心技术选型 115 | 11. 注册中心 | Etcd 云原生中间件入门 116 | 12. 注册中心 | 基于 Etcd 实现注册中心 117 | 13. 注册中心 | 可扩展注册中心实现(SPI + 工厂模式) 118 | 14. 注册中心优化 | 心跳检测和续期机制 119 | 15. 注册中心优化 | 服务节点下线机制 120 | 16. 注册中心优化 | 消费端服务缓存 121 | 17. 注册中心优化 | 缓存更新(Etcd 监听机制) 122 | 18. 注册中心优化 | ZooKeeper 注册中心实现 123 | 19. 自定义协议 | 需求分析及方案设计 124 | 20. 自定义协议 | 消息结构设计(参考 Dubbo) 125 | 21. 自定义协议 | 网络传输设计(基于 Vert.x 实现 TCP 服务器) 126 | 22. 自定义协议 | 编码 / 解码器 127 | 23. 自定义协议 | TCP 请求处理器 128 | 24. 自定义协议 | TCP 请求客户端 129 | 25. 自定义协议 | 粘包半包问题分析 130 | 26. 自定义协议 | 使用 Vert.x 解决粘包半包问题 131 | 27. 自定义协议 | 客户端代码优化(装饰者模式) 132 | 28. 负载均衡 | 负载均衡概念和常用算法 133 | 29. 负载均衡 | 一致性 Hash 134 | 30. 负载均衡 | 多种负载均衡器实现 135 | 31. 负载均衡 | 可扩展负载均衡器实现(SPI + 工厂模式) 136 | 32. 重试机制 | 重试等待策略 137 | 33. 重试机制 | 重试方案设计 138 | 34. 重试机制 | 多种重试策略实现 139 | 35. 重试机制 | 可扩展重试策略实现(SPI + 工厂模式) 140 | 36. 容错机制 | 容错策略和实现方式 141 | 37. 容错机制 | 容错方案设计 142 | 38. 容错机制 | 多种容错策略实现 143 | 39. 容错机制 | 可扩展容错策略实现(SPI + 工厂模式) 144 | 40. 启动机制 | 框架快速启动类 145 | 41. 启动机制 | 注解驱动设计 146 | 42. 启动机制 | Spring Boot Starter 注解驱动实现 147 | 43. 项目扩展思路 148 | 149 | 150 | 151 | ## 完整项目教程学习 152 | 153 | 点击 [加入编程导航](https://yuyuanweb.feishu.cn/wiki/SDtMwjR1DituVpkz5MLc3fZLnzb) ,鱼皮往期 [所有原创项目](https://yuyuanweb.feishu.cn/wiki/SePYwTc9tipQiCktw7Uc7kujnCd) 均可学习。 154 | 155 | -------------------------------------------------------------------------------- /docs/structure.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liyupi/yu-rpc/d6865886a56999859b106847fd8edb0d284b0faa/docs/structure.jpg -------------------------------------------------------------------------------- /docs/tutorial.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liyupi/yu-rpc/d6865886a56999859b106847fd8edb0d284b0faa/docs/tutorial.jpg -------------------------------------------------------------------------------- /example-common/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | !**/src/main/**/target/ 4 | !**/src/test/**/target/ 5 | 6 | ### IntelliJ IDEA ### 7 | .idea/modules.xml 8 | .idea/jarRepositories.xml 9 | .idea/compiler.xml 10 | .idea/libraries/ 11 | *.iws 12 | *.iml 13 | *.ipr 14 | 15 | ### Eclipse ### 16 | .apt_generated 17 | .classpath 18 | .factorypath 19 | .project 20 | .settings 21 | .springBeans 22 | .sts4-cache 23 | 24 | ### NetBeans ### 25 | /nbproject/private/ 26 | /nbbuild/ 27 | /dist/ 28 | /nbdist/ 29 | /.nb-gradle/ 30 | build/ 31 | !**/src/main/**/build/ 32 | !**/src/test/**/build/ 33 | 34 | ### VS Code ### 35 | .vscode/ 36 | 37 | ### Mac OS ### 38 | .DS_Store -------------------------------------------------------------------------------- /example-common/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.yupi 8 | example-common 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | 14 | org.apache.maven.plugins 15 | maven-compiler-plugin 16 | 17 | 8 18 | 8 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /example-common/src/main/java/com/yupi/example/common/model/User.java: -------------------------------------------------------------------------------- 1 | package com.yupi.example.common.model; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * 用户 7 | * 8 | * @author 程序员鱼皮 9 | * @learn 编程宝典 10 | * @from 编程导航知识星球 11 | */ 12 | public class User implements Serializable { 13 | 14 | private String name; 15 | 16 | public String getName() { 17 | return name; 18 | } 19 | 20 | public void setName(String name) { 21 | this.name = name; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example-common/src/main/java/com/yupi/example/common/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.yupi.example.common.service; 2 | 3 | import com.yupi.example.common.model.User; 4 | 5 | /** 6 | * 用户服务 7 | * 8 | * @author 程序员鱼皮 9 | * @learn 编程宝典 10 | * @from 编程导航知识星球 11 | */ 12 | public interface UserService { 13 | 14 | /** 15 | * 获取用户 16 | * 17 | * @param user 18 | * @return 19 | */ 20 | User getUser(User user); 21 | 22 | /** 23 | * 用于测试 mock 接口返回值 24 | * 25 | * @return 26 | */ 27 | default short getNumber() { 28 | return 1; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /example-consumer/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | !**/src/main/**/target/ 4 | !**/src/test/**/target/ 5 | 6 | ### IntelliJ IDEA ### 7 | .idea/modules.xml 8 | .idea/jarRepositories.xml 9 | .idea/compiler.xml 10 | .idea/libraries/ 11 | *.iws 12 | *.iml 13 | *.ipr 14 | 15 | ### Eclipse ### 16 | .apt_generated 17 | .classpath 18 | .factorypath 19 | .project 20 | .settings 21 | .springBeans 22 | .sts4-cache 23 | 24 | ### NetBeans ### 25 | /nbproject/private/ 26 | /nbbuild/ 27 | /dist/ 28 | /nbdist/ 29 | /.nb-gradle/ 30 | build/ 31 | !**/src/main/**/build/ 32 | !**/src/test/**/build/ 33 | 34 | ### VS Code ### 35 | .vscode/ 36 | 37 | ### Mac OS ### 38 | .DS_Store -------------------------------------------------------------------------------- /example-consumer/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.yupi 8 | example-consumer 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | com.yupi 14 | yu-rpc-core 15 | 1.0-SNAPSHOT 16 | 17 | 18 | com.yupi 19 | example-common 20 | 1.0-SNAPSHOT 21 | 22 | 23 | 24 | cn.hutool 25 | hutool-all 26 | 5.8.16 27 | 28 | 29 | 30 | org.projectlombok 31 | lombok 32 | 1.18.30 33 | provided 34 | 35 | 36 | 37 | 38 | 39 | 40 | org.apache.maven.plugins 41 | maven-compiler-plugin 42 | 43 | 8 44 | 8 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /example-consumer/src/main/java/com/yupi/example/consumer/ConsumerExample.java: -------------------------------------------------------------------------------- 1 | package com.yupi.example.consumer; 2 | 3 | import com.yupi.example.common.model.User; 4 | import com.yupi.example.common.service.UserService; 5 | import com.yupi.yurpc.bootstrap.ConsumerBootstrap; 6 | import com.yupi.yurpc.proxy.ServiceProxyFactory; 7 | 8 | /** 9 | * 服务消费者示例 10 | * 11 | * @author 程序员鱼皮 12 | * @learn 编程宝典 13 | * @from 编程导航知识星球 14 | */ 15 | public class ConsumerExample { 16 | 17 | public static void main(String[] args) { 18 | // 服务提供者初始化 19 | ConsumerBootstrap.init(); 20 | 21 | // 获取代理 22 | UserService userService = ServiceProxyFactory.getProxy(UserService.class); 23 | User user = new User(); 24 | user.setName("yupi"); 25 | // 调用 26 | User newUser = userService.getUser(user); 27 | if (newUser != null) { 28 | System.out.println(newUser.getName()); 29 | } else { 30 | System.out.println("user == null"); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /example-consumer/src/main/java/com/yupi/example/consumer/EasyConsumerExample.java: -------------------------------------------------------------------------------- 1 | package com.yupi.example.consumer; 2 | 3 | import com.yupi.example.common.model.User; 4 | import com.yupi.example.common.service.UserService; 5 | import com.yupi.yurpc.proxy.ServiceProxyFactory; 6 | 7 | /** 8 | * 简易服务消费者示例 9 | * 10 | * @author 程序员鱼皮 11 | * @learn 编程宝典 12 | * @from 编程导航知识星球 13 | */ 14 | public class EasyConsumerExample { 15 | 16 | public static void main(String[] args) { 17 | // 静态代理 18 | // UserService userService = new UserServiceProxy(); 19 | // 动态代理 20 | UserService userService = ServiceProxyFactory.getProxy(UserService.class); 21 | User user = new User(); 22 | user.setName("yupi"); 23 | // 调用 24 | User newUser = userService.getUser(user); 25 | if (newUser != null) { 26 | System.out.println(newUser.getName()); 27 | } else { 28 | System.out.println("user == null"); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /example-consumer/src/main/java/com/yupi/example/consumer/UserServiceProxy.java: -------------------------------------------------------------------------------- 1 | package com.yupi.example.consumer; 2 | 3 | import cn.hutool.http.HttpRequest; 4 | import cn.hutool.http.HttpResponse; 5 | import com.yupi.example.common.model.User; 6 | import com.yupi.example.common.service.UserService; 7 | import com.yupi.yurpc.model.RpcRequest; 8 | import com.yupi.yurpc.model.RpcResponse; 9 | import com.yupi.yurpc.serializer.JdkSerializer; 10 | import com.yupi.yurpc.serializer.Serializer; 11 | 12 | import java.io.IOException; 13 | 14 | /** 15 | * 用户服务静态代理 16 | * 17 | * @author 程序员鱼皮 18 | * @learn 编程宝典 19 | * @from 编程导航知识星球 20 | */ 21 | public class UserServiceProxy implements UserService { 22 | 23 | public User getUser(User user) { 24 | // 指定序列化器 25 | final Serializer serializer = new JdkSerializer(); 26 | 27 | // 构造请求 28 | RpcRequest rpcRequest = RpcRequest.builder() 29 | .serviceName(UserService.class.getName()) 30 | .methodName("getUser") 31 | .parameterTypes(new Class[]{User.class}) 32 | .args(new Object[]{user}) 33 | .build(); 34 | try { 35 | // 序列化(Java 对象 => 字节数组) 36 | byte[] bodyBytes = serializer.serialize(rpcRequest); 37 | 38 | // 发送请求 39 | try (HttpResponse httpResponse = HttpRequest.post("http://localhost:8080") 40 | .body(bodyBytes) 41 | .execute()) { 42 | byte[] result = httpResponse.bodyBytes(); 43 | // 反序列化(字节数组 => Java 对象) 44 | RpcResponse rpcResponse = serializer.deserialize(result, RpcResponse.class); 45 | return (User) rpcResponse.getData(); 46 | } 47 | } catch (IOException e) { 48 | e.printStackTrace(); 49 | } 50 | 51 | return null; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /example-consumer/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | rpc.name=yurpc 2 | rpc.version=2.0 3 | rpc.mock=false 4 | rpc.serializer=jdk -------------------------------------------------------------------------------- /example-provider/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | !**/src/main/**/target/ 4 | !**/src/test/**/target/ 5 | 6 | ### IntelliJ IDEA ### 7 | .idea/modules.xml 8 | .idea/jarRepositories.xml 9 | .idea/compiler.xml 10 | .idea/libraries/ 11 | *.iws 12 | *.iml 13 | *.ipr 14 | 15 | ### Eclipse ### 16 | .apt_generated 17 | .classpath 18 | .factorypath 19 | .project 20 | .settings 21 | .springBeans 22 | .sts4-cache 23 | 24 | ### NetBeans ### 25 | /nbproject/private/ 26 | /nbbuild/ 27 | /dist/ 28 | /nbdist/ 29 | /.nb-gradle/ 30 | build/ 31 | !**/src/main/**/build/ 32 | !**/src/test/**/build/ 33 | 34 | ### VS Code ### 35 | .vscode/ 36 | 37 | ### Mac OS ### 38 | .DS_Store -------------------------------------------------------------------------------- /example-provider/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.yupi 8 | example-provider 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | com.yupi 14 | yu-rpc-core 15 | 1.0-SNAPSHOT 16 | 17 | 18 | com.yupi 19 | example-common 20 | 1.0-SNAPSHOT 21 | 22 | 23 | 24 | cn.hutool 25 | hutool-all 26 | 5.8.16 27 | 28 | 29 | 30 | org.projectlombok 31 | lombok 32 | 1.18.30 33 | provided 34 | 35 | 36 | 37 | 38 | 39 | 40 | org.apache.maven.plugins 41 | maven-compiler-plugin 42 | 43 | 8 44 | 8 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /example-provider/src/main/java/com/yupi/example/provider/EasyProviderExample.java: -------------------------------------------------------------------------------- 1 | package com.yupi.example.provider; 2 | 3 | import com.yupi.example.common.service.UserService; 4 | import com.yupi.yurpc.registry.LocalRegistry; 5 | import com.yupi.yurpc.server.HttpServer; 6 | import com.yupi.yurpc.server.VertxHttpServer; 7 | 8 | /** 9 | * 简易服务提供者示例 10 | * 11 | * @author 程序员鱼皮 12 | * @learn 编程宝典 13 | * @from 编程导航知识星球 14 | */ 15 | public class EasyProviderExample { 16 | 17 | public static void main(String[] args) { 18 | // 注册服务 19 | LocalRegistry.register(UserService.class.getName(), UserServiceImpl.class); 20 | 21 | // 启动 web 服务 22 | HttpServer httpServer = new VertxHttpServer(); 23 | httpServer.doStart(8080); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /example-provider/src/main/java/com/yupi/example/provider/ProviderExample.java: -------------------------------------------------------------------------------- 1 | package com.yupi.example.provider; 2 | 3 | import com.yupi.example.common.service.UserService; 4 | import com.yupi.yurpc.bootstrap.ProviderBootstrap; 5 | import com.yupi.yurpc.model.ServiceRegisterInfo; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | /** 11 | * 服务提供者示例 12 | * 13 | * @author 程序员鱼皮 14 | * @learn 编程宝典 15 | * @from 编程导航知识星球 16 | */ 17 | public class ProviderExample { 18 | 19 | public static void main(String[] args) { 20 | // 要注册的服务 21 | List> serviceRegisterInfoList = new ArrayList<>(); 22 | ServiceRegisterInfo serviceRegisterInfo = new ServiceRegisterInfo<>(UserService.class.getName(), UserServiceImpl.class); 23 | serviceRegisterInfoList.add(serviceRegisterInfo); 24 | 25 | // 服务提供者初始化 26 | ProviderBootstrap.init(serviceRegisterInfoList); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /example-provider/src/main/java/com/yupi/example/provider/UserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.yupi.example.provider; 2 | 3 | import com.yupi.example.common.model.User; 4 | import com.yupi.example.common.service.UserService; 5 | 6 | /** 7 | * 用户服务实现类 8 | * 9 | * @author 程序员鱼皮 10 | * @learn 编程宝典 11 | * @from 编程导航知识星球 12 | */ 13 | public class UserServiceImpl implements UserService { 14 | 15 | public User getUser(User user) { 16 | System.out.println("用户名:" + user.getName()); 17 | return user; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /example-provider/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | rpc.name=yurpc 2 | rpc.version=2.0 3 | rpc.mock=false 4 | rpc.serializer=jdk 5 | rpc.serverPort=8080 6 | rpc.serverHost=localhost -------------------------------------------------------------------------------- /example-springboot-consumer/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /example-springboot-consumer/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | com.yupi 6 | example-springboot-consumer 7 | 0.0.1-SNAPSHOT 8 | example-springboot-consumer 9 | example-springboot-consumer 10 | 11 | 1.8 12 | UTF-8 13 | UTF-8 14 | 2.6.13 15 | 16 | 17 | 18 | com.yupi 19 | yu-rpc-spring-boot-starter 20 | 0.0.1-SNAPSHOT 21 | 22 | 23 | com.yupi 24 | example-common 25 | 1.0-SNAPSHOT 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter 31 | 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter-test 36 | test 37 | 38 | 39 | 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-dependencies 44 | ${spring-boot.version} 45 | pom 46 | import 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | org.apache.maven.plugins 55 | maven-compiler-plugin 56 | 3.8.1 57 | 58 | 1.8 59 | 1.8 60 | UTF-8 61 | 62 | 63 | 64 | org.springframework.boot 65 | spring-boot-maven-plugin 66 | ${spring-boot.version} 67 | 68 | com.yupi.examplespringbootconsumer.ExampleSpringbootConsumerApplication 69 | true 70 | 71 | 72 | 73 | repackage 74 | 75 | repackage 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /example-springboot-consumer/src/main/java/com/yupi/examplespringbootconsumer/ExampleServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.yupi.examplespringbootconsumer; 2 | 3 | import com.yupi.example.common.model.User; 4 | import com.yupi.example.common.service.UserService; 5 | import com.yupi.yurpc.springboot.starter.annotation.RpcReference; 6 | import org.springframework.stereotype.Service; 7 | 8 | /** 9 | * 示例服务实现类 10 | * 11 | * @author 程序员鱼皮 12 | * @learn 编程宝典 13 | * @from 编程导航知识星球 14 | */ 15 | @Service 16 | public class ExampleServiceImpl { 17 | 18 | /** 19 | * 使用 Rpc 框架注入 20 | */ 21 | @RpcReference 22 | private UserService userService; 23 | 24 | /** 25 | * 测试方法 26 | */ 27 | public void test() { 28 | User user = new User(); 29 | user.setName("yupi"); 30 | User resultUser = userService.getUser(user); 31 | System.out.println(resultUser.getName()); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /example-springboot-consumer/src/main/java/com/yupi/examplespringbootconsumer/ExampleSpringbootConsumerApplication.java: -------------------------------------------------------------------------------- 1 | package com.yupi.examplespringbootconsumer; 2 | 3 | import com.yupi.yurpc.springboot.starter.annotation.EnableRpc; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | 7 | /** 8 | * 示例 Spring Boot 服务消费者应用 9 | * 10 | * @author 程序员鱼皮 11 | * @learn 编程宝典 12 | * @from 编程导航知识星球 13 | */ 14 | @SpringBootApplication 15 | @EnableRpc(needServer = false) 16 | public class ExampleSpringbootConsumerApplication { 17 | 18 | public static void main(String[] args) { 19 | SpringApplication.run(ExampleSpringbootConsumerApplication.class, args); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /example-springboot-consumer/src/test/java/com/yupi/examplespringbootconsumer/ExampleServiceImplTest.java: -------------------------------------------------------------------------------- 1 | package com.yupi.examplespringbootconsumer; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | import javax.annotation.Resource; 7 | 8 | /** 9 | * 单元测试 10 | * 11 | * @author 程序员鱼皮 12 | * @learn 编程宝典 13 | * @from 编程导航知识星球 14 | */ 15 | @SpringBootTest 16 | class ExampleServiceImplTest { 17 | 18 | @Resource 19 | private ExampleServiceImpl exampleService; 20 | 21 | @Test 22 | void test1() { 23 | exampleService.test(); 24 | } 25 | } -------------------------------------------------------------------------------- /example-springboot-provider/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /example-springboot-provider/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | com.yupi 6 | example-springboot-provider 7 | 0.0.1-SNAPSHOT 8 | example-springboot-provider 9 | example-springboot-provider 10 | 11 | 1.8 12 | UTF-8 13 | UTF-8 14 | 2.6.13 15 | 16 | 17 | 18 | com.yupi 19 | yu-rpc-spring-boot-starter 20 | 0.0.1-SNAPSHOT 21 | 22 | 23 | com.yupi 24 | example-common 25 | 1.0-SNAPSHOT 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter 31 | 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter-test 36 | test 37 | 38 | 39 | 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-dependencies 44 | ${spring-boot.version} 45 | pom 46 | import 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | org.apache.maven.plugins 55 | maven-compiler-plugin 56 | 3.8.1 57 | 58 | 1.8 59 | 1.8 60 | UTF-8 61 | 62 | 63 | 64 | org.springframework.boot 65 | spring-boot-maven-plugin 66 | ${spring-boot.version} 67 | 68 | com.yupi.examplespringbootprovider.ExampleSpringbootProviderApplication 69 | true 70 | 71 | 72 | 73 | repackage 74 | 75 | repackage 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /example-springboot-provider/src/main/java/com/yupi/examplespringbootprovider/ExampleSpringbootProviderApplication.java: -------------------------------------------------------------------------------- 1 | package com.yupi.examplespringbootprovider; 2 | 3 | import com.yupi.yurpc.springboot.starter.annotation.EnableRpc; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | 7 | /** 8 | * 示例 Spring Boot 服务提供者应用 9 | * 10 | * @author 程序员鱼皮 11 | * @learn 编程宝典 12 | * @from 编程导航知识星球 13 | */ 14 | @SpringBootApplication 15 | @EnableRpc 16 | public class ExampleSpringbootProviderApplication { 17 | 18 | public static void main(String[] args) { 19 | SpringApplication.run(ExampleSpringbootProviderApplication.class, args); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /example-springboot-provider/src/main/java/com/yupi/examplespringbootprovider/UserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.yupi.examplespringbootprovider; 2 | 3 | import com.yupi.example.common.model.User; 4 | import com.yupi.example.common.service.UserService; 5 | import com.yupi.yurpc.springboot.starter.annotation.RpcService; 6 | import org.springframework.stereotype.Service; 7 | 8 | /** 9 | * 用户服务实现类 10 | * 11 | * @author 程序员鱼皮 12 | * @learn 编程宝典 13 | * @from 编程导航知识星球 14 | */ 15 | @Service 16 | @RpcService 17 | public class UserServiceImpl implements UserService { 18 | 19 | public User getUser(User user) { 20 | System.out.println("用户名:" + user.getName()); 21 | return user; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /yu-rpc-core/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | !**/src/main/**/target/ 4 | !**/src/test/**/target/ 5 | 6 | ### IntelliJ IDEA ### 7 | .idea/modules.xml 8 | .idea/jarRepositories.xml 9 | .idea/compiler.xml 10 | .idea/libraries/ 11 | *.iws 12 | *.iml 13 | *.ipr 14 | 15 | ### Eclipse ### 16 | .apt_generated 17 | .classpath 18 | .factorypath 19 | .project 20 | .settings 21 | .springBeans 22 | .sts4-cache 23 | 24 | ### NetBeans ### 25 | /nbproject/private/ 26 | /nbbuild/ 27 | /dist/ 28 | /nbdist/ 29 | /.nb-gradle/ 30 | build/ 31 | !**/src/main/**/build/ 32 | !**/src/test/**/build/ 33 | 34 | ### VS Code ### 35 | .vscode/ 36 | 37 | ### Mac OS ### 38 | .DS_Store -------------------------------------------------------------------------------- /yu-rpc-core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.yupi 8 | yu-rpc-core 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | 14 | io.vertx 15 | vertx-core 16 | 4.5.1 17 | 18 | 19 | 20 | cn.hutool 21 | hutool-all 22 | 5.8.16 23 | 24 | 25 | 26 | ch.qos.logback 27 | logback-classic 28 | 1.3.12 29 | 30 | 31 | 32 | 33 | com.caucho 34 | hessian 35 | 4.0.66 36 | 37 | 38 | 39 | com.esotericsoftware 40 | kryo 41 | 5.6.0 42 | 43 | 44 | 45 | 46 | io.etcd 47 | jetcd-core 48 | 0.7.7 49 | 50 | 51 | 52 | org.apache.curator 53 | curator-x-discovery 54 | 5.6.0 55 | 56 | 57 | 58 | com.github.rholder 59 | guava-retrying 60 | 2.0.0 61 | 62 | 63 | 64 | org.projectlombok 65 | lombok 66 | 1.18.30 67 | provided 68 | 69 | 70 | junit 71 | junit 72 | RELEASE 73 | test 74 | 75 | 76 | 77 | 78 | 79 | 80 | org.apache.maven.plugins 81 | maven-compiler-plugin 82 | 83 | 8 84 | 8 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/RpcApplication.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc; 2 | 3 | import com.yupi.yurpc.config.RegistryConfig; 4 | import com.yupi.yurpc.config.RpcConfig; 5 | import com.yupi.yurpc.constant.RpcConstant; 6 | import com.yupi.yurpc.registry.Registry; 7 | import com.yupi.yurpc.registry.RegistryFactory; 8 | import com.yupi.yurpc.utils.ConfigUtils; 9 | import lombok.extern.slf4j.Slf4j; 10 | 11 | /** 12 | * RPC 框架应用 13 | * 相当于 holder,存放了项目全局用到的变量。双检锁单例模式实现 14 | * 15 | * @author 程序员鱼皮 16 | * @learn 程序员鱼皮的编程宝典 17 | * @from 编程导航知识星球 18 | */ 19 | @Slf4j 20 | public class RpcApplication { 21 | 22 | private static volatile RpcConfig rpcConfig; 23 | 24 | /** 25 | * 框架初始化,支持传入自定义配置 26 | * 27 | * @param newRpcConfig 28 | */ 29 | public static void init(RpcConfig newRpcConfig) { 30 | rpcConfig = newRpcConfig; 31 | log.info("rpc init, config = {}", newRpcConfig.toString()); 32 | // 注册中心初始化 33 | RegistryConfig registryConfig = rpcConfig.getRegistryConfig(); 34 | Registry registry = RegistryFactory.getInstance(registryConfig.getRegistry()); 35 | registry.init(registryConfig); 36 | log.info("registry init, config = {}", registryConfig); 37 | // 创建并注册 Shutdown Hook,JVM 退出时执行操作 38 | Runtime.getRuntime().addShutdownHook(new Thread(registry::destroy)); 39 | } 40 | 41 | /** 42 | * 初始化 43 | */ 44 | public static void init() { 45 | RpcConfig newRpcConfig; 46 | try { 47 | newRpcConfig = ConfigUtils.loadConfig(RpcConfig.class, RpcConstant.DEFAULT_CONFIG_PREFIX); 48 | } catch (Exception e) { 49 | // 配置加载失败,使用默认值 50 | newRpcConfig = new RpcConfig(); 51 | } 52 | init(newRpcConfig); 53 | } 54 | 55 | 56 | /** 57 | * 获取配置 58 | * 59 | * @return 60 | */ 61 | public static RpcConfig getRpcConfig() { 62 | if (rpcConfig == null) { 63 | synchronized (RpcApplication.class) { 64 | if (rpcConfig == null) { 65 | init(); 66 | } 67 | } 68 | } 69 | return rpcConfig; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/bootstrap/ConsumerBootstrap.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.bootstrap; 2 | 3 | import com.yupi.yurpc.RpcApplication; 4 | 5 | /** 6 | * 服务消费者启动类(初始化) 7 | * 8 | * @author 程序员鱼皮 9 | * @learn 鱼皮的编程宝典 10 | * @from 编程导航学习圈 11 | */ 12 | public class ConsumerBootstrap { 13 | 14 | /** 15 | * 初始化 16 | */ 17 | public static void init() { 18 | // RPC 框架初始化(配置和注册中心) 19 | RpcApplication.init(); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/bootstrap/ProviderBootstrap.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.bootstrap; 2 | 3 | import com.yupi.yurpc.RpcApplication; 4 | import com.yupi.yurpc.config.RegistryConfig; 5 | import com.yupi.yurpc.config.RpcConfig; 6 | import com.yupi.yurpc.model.ServiceMetaInfo; 7 | import com.yupi.yurpc.model.ServiceRegisterInfo; 8 | import com.yupi.yurpc.registry.LocalRegistry; 9 | import com.yupi.yurpc.registry.Registry; 10 | import com.yupi.yurpc.registry.RegistryFactory; 11 | import com.yupi.yurpc.server.tcp.VertxTcpServer; 12 | 13 | import java.util.List; 14 | 15 | /** 16 | * 服务提供者启动类(初始化) 17 | * 18 | * @author 程序员鱼皮 19 | * @learn 鱼皮的编程宝典 20 | * @from 编程导航学习圈 21 | */ 22 | public class ProviderBootstrap { 23 | 24 | /** 25 | * 初始化 26 | */ 27 | public static void init(List> serviceRegisterInfoList) { 28 | // RPC 框架初始化(配置和注册中心) 29 | RpcApplication.init(); 30 | // 全局配置 31 | final RpcConfig rpcConfig = RpcApplication.getRpcConfig(); 32 | 33 | // 注册服务 34 | for (ServiceRegisterInfo serviceRegisterInfo : serviceRegisterInfoList) { 35 | String serviceName = serviceRegisterInfo.getServiceName(); 36 | // 本地注册 37 | LocalRegistry.register(serviceName, serviceRegisterInfo.getImplClass()); 38 | 39 | // 注册服务到注册中心 40 | RegistryConfig registryConfig = rpcConfig.getRegistryConfig(); 41 | Registry registry = RegistryFactory.getInstance(registryConfig.getRegistry()); 42 | ServiceMetaInfo serviceMetaInfo = new ServiceMetaInfo(); 43 | serviceMetaInfo.setServiceName(serviceName); 44 | serviceMetaInfo.setServiceHost(rpcConfig.getServerHost()); 45 | serviceMetaInfo.setServicePort(rpcConfig.getServerPort()); 46 | try { 47 | registry.register(serviceMetaInfo); 48 | } catch (Exception e) { 49 | throw new RuntimeException(serviceName + " 服务注册失败", e); 50 | } 51 | } 52 | 53 | // 启动服务器 54 | VertxTcpServer vertxTcpServer = new VertxTcpServer(); 55 | vertxTcpServer.doStart(rpcConfig.getServerPort()); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/config/RegistryConfig.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.config; 2 | 3 | import com.yupi.yurpc.registry.RegistryKeys; 4 | import lombok.Data; 5 | 6 | /** 7 | * RPC 框架注册中心配置 8 | * 9 | * @author 程序员鱼皮 10 | * @learn 鱼皮的编程宝典 11 | * @from 编程导航学习圈 12 | */ 13 | @Data 14 | public class RegistryConfig { 15 | 16 | /** 17 | * 注册中心类别 18 | */ 19 | private String registry = RegistryKeys.ETCD; 20 | 21 | /** 22 | * 注册中心地址 23 | */ 24 | private String address = "http://localhost:2380"; 25 | 26 | /** 27 | * 用户名 28 | */ 29 | private String username; 30 | 31 | /** 32 | * 密码 33 | */ 34 | private String password; 35 | 36 | /** 37 | * 超时时间(单位毫秒) 38 | */ 39 | private Long timeout = 10000L; 40 | } 41 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/config/RpcConfig.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.config; 2 | 3 | import com.yupi.yurpc.fault.retry.RetryStrategyKeys; 4 | import com.yupi.yurpc.fault.tolerant.TolerantStrategyKeys; 5 | import com.yupi.yurpc.loadbalancer.LoadBalancerKeys; 6 | import com.yupi.yurpc.serializer.SerializerKeys; 7 | import lombok.Data; 8 | 9 | /** 10 | * RPC 框架全局配置 11 | * 12 | * @author 程序员鱼皮 13 | * @learn 鱼皮的编程宝典 14 | * @from 编程导航学习圈 15 | */ 16 | @Data 17 | public class RpcConfig { 18 | 19 | /** 20 | * 名称 21 | */ 22 | private String name = "yu-rpc"; 23 | 24 | /** 25 | * 版本号 26 | */ 27 | private String version = "1.0"; 28 | 29 | /** 30 | * 服务器主机名 31 | */ 32 | private String serverHost = "localhost"; 33 | 34 | /** 35 | * 服务器端口号 36 | */ 37 | private Integer serverPort = 8080; 38 | 39 | /** 40 | * 序列化器 41 | */ 42 | private String serializer = SerializerKeys.JDK; 43 | 44 | /** 45 | * 负载均衡器 46 | */ 47 | private String loadBalancer = LoadBalancerKeys.ROUND_ROBIN; 48 | 49 | /** 50 | * 重试策略 51 | */ 52 | private String retryStrategy = RetryStrategyKeys.NO; 53 | 54 | /** 55 | * 容错策略 56 | */ 57 | private String tolerantStrategy = TolerantStrategyKeys.FAIL_FAST; 58 | 59 | /** 60 | * 模拟调用 61 | */ 62 | private boolean mock = false; 63 | 64 | /** 65 | * 注册中心配置 66 | */ 67 | private RegistryConfig registryConfig = new RegistryConfig(); 68 | } 69 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/constant/RpcConstant.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.constant; 2 | 3 | /** 4 | * RPC 相关常量 5 | * 6 | * @author 程序员鱼皮 7 | * @learn 鱼皮的编程宝典 8 | * @from 编程导航学习圈 9 | */ 10 | public interface RpcConstant { 11 | 12 | /** 13 | * 默认配置文件加载前缀 14 | */ 15 | String DEFAULT_CONFIG_PREFIX = "rpc"; 16 | 17 | /** 18 | * 默认服务版本 19 | */ 20 | String DEFAULT_SERVICE_VERSION = "1.0"; 21 | 22 | } 23 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/exception/RpcException.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.exception; 2 | 3 | /** 4 | * 自定义异常类 5 | * 6 | * @author 程序员鱼皮 7 | * @learn 鱼皮的编程宝典 8 | * @from 编程导航学习圈 9 | */ 10 | public class RpcException extends RuntimeException { 11 | 12 | public RpcException(String message) { 13 | super(message); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/fault/retry/FixedIntervalRetryStrategy.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.fault.retry; 2 | 3 | import com.github.rholder.retry.*; 4 | import com.yupi.yurpc.model.RpcResponse; 5 | import lombok.extern.slf4j.Slf4j; 6 | 7 | import java.util.concurrent.Callable; 8 | import java.util.concurrent.ExecutionException; 9 | import java.util.concurrent.TimeUnit; 10 | 11 | /** 12 | * 固定时间间隔 - 重试策略 13 | * 14 | * @author 程序员鱼皮 15 | * @learn 鱼皮的编程宝典 16 | * @from 编程导航学习圈 17 | */ 18 | @Slf4j 19 | public class FixedIntervalRetryStrategy implements RetryStrategy { 20 | 21 | /** 22 | * 重试 23 | * 24 | * @param callable 25 | * @return 26 | * @throws ExecutionException 27 | * @throws RetryException 28 | */ 29 | public RpcResponse doRetry(Callable callable) throws ExecutionException, RetryException { 30 | Retryer retryer = RetryerBuilder.newBuilder() 31 | .retryIfExceptionOfType(Exception.class) 32 | .withWaitStrategy(WaitStrategies.fixedWait(3L, TimeUnit.SECONDS)) 33 | .withStopStrategy(StopStrategies.stopAfterAttempt(3)) 34 | .withRetryListener(new RetryListener() { 35 | @Override 36 | public void onRetry(Attempt attempt) { 37 | log.info("重试次数 {}", attempt.getAttemptNumber()); 38 | } 39 | }) 40 | .build(); 41 | return retryer.call(callable); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/fault/retry/NoRetryStrategy.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.fault.retry; 2 | 3 | import com.yupi.yurpc.model.RpcResponse; 4 | import lombok.extern.slf4j.Slf4j; 5 | 6 | import java.util.concurrent.Callable; 7 | 8 | /** 9 | * 不重试 - 重试策略 10 | * 11 | * @author 程序员鱼皮 12 | * @learn 鱼皮的编程宝典 13 | * @from 编程导航学习圈 14 | */ 15 | @Slf4j 16 | public class NoRetryStrategy implements RetryStrategy { 17 | 18 | /** 19 | * 重试 20 | * 21 | * @param callable 22 | * @return 23 | * @throws Exception 24 | */ 25 | public RpcResponse doRetry(Callable callable) throws Exception { 26 | return callable.call(); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/fault/retry/RetryStrategy.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.fault.retry; 2 | 3 | import com.yupi.yurpc.model.RpcResponse; 4 | 5 | import java.util.concurrent.Callable; 6 | 7 | /** 8 | * 重试策略 9 | * 10 | * @author 程序员鱼皮 11 | * @learn 鱼皮的编程宝典 12 | * @from 编程导航学习圈 13 | */ 14 | public interface RetryStrategy { 15 | 16 | /** 17 | * 重试 18 | * 19 | * @param callable 20 | * @return 21 | * @throws Exception 22 | */ 23 | RpcResponse doRetry(Callable callable) throws Exception; 24 | } 25 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/fault/retry/RetryStrategyFactory.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.fault.retry; 2 | 3 | import com.yupi.yurpc.spi.SpiLoader; 4 | 5 | /** 6 | * 重试策略工厂(用于获取重试器对象) 7 | * 8 | * @author 程序员鱼皮 9 | * @learn 编程宝典 10 | * @from 编程导航知识星球 11 | */ 12 | public class RetryStrategyFactory { 13 | 14 | static { 15 | SpiLoader.load(RetryStrategy.class); 16 | } 17 | 18 | /** 19 | * 默认重试器 20 | */ 21 | private static final RetryStrategy DEFAULT_RETRY_STRATEGY = new NoRetryStrategy(); 22 | 23 | /** 24 | * 获取实例 25 | * 26 | * @param key 27 | * @return 28 | */ 29 | public static RetryStrategy getInstance(String key) { 30 | return SpiLoader.getInstance(RetryStrategy.class, key); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/fault/retry/RetryStrategyKeys.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.fault.retry; 2 | 3 | /** 4 | * 重试策略键名常量 5 | * 6 | * @author 程序员鱼皮 7 | * @learn 鱼皮的编程宝典 8 | * @from 编程导航学习圈 9 | */ 10 | public interface RetryStrategyKeys { 11 | 12 | /** 13 | * 不重试 14 | */ 15 | String NO = "no"; 16 | 17 | /** 18 | * 固定时间间隔 19 | */ 20 | String FIXED_INTERVAL = "fixedInterval"; 21 | 22 | } 23 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/fault/tolerant/FailBackTolerantStrategy.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.fault.tolerant; 2 | 3 | import com.yupi.yurpc.model.RpcResponse; 4 | import lombok.extern.slf4j.Slf4j; 5 | 6 | import java.util.Map; 7 | 8 | /** 9 | * 降级到其他服务 - 容错策略 10 | * 11 | * @author 程序员鱼皮 12 | * @learn 鱼皮的编程宝典 13 | * @from 编程导航学习圈 14 | */ 15 | @Slf4j 16 | public class FailBackTolerantStrategy implements TolerantStrategy { 17 | 18 | @Override 19 | public RpcResponse doTolerant(Map context, Exception e) { 20 | // todo 可自行扩展,获取降级的服务并调用 21 | return null; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/fault/tolerant/FailFastTolerantStrategy.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.fault.tolerant; 2 | 3 | import com.yupi.yurpc.model.RpcResponse; 4 | 5 | import java.util.Map; 6 | 7 | /** 8 | * 快速失败 - 容错策略(立刻通知外层调用方) 9 | * 10 | * @author 程序员鱼皮 11 | * @learn 鱼皮的编程宝典 12 | * @from 编程导航学习圈 13 | */ 14 | public class FailFastTolerantStrategy implements TolerantStrategy { 15 | 16 | @Override 17 | public RpcResponse doTolerant(Map context, Exception e) { 18 | throw new RuntimeException("服务报错", e); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/fault/tolerant/FailOverTolerantStrategy.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.fault.tolerant; 2 | 3 | import com.yupi.yurpc.model.RpcResponse; 4 | import lombok.extern.slf4j.Slf4j; 5 | 6 | import java.util.Map; 7 | 8 | /** 9 | * 转移到其他服务节点 - 容错策略 10 | * 11 | * @author 程序员鱼皮 12 | * @learn 鱼皮的编程宝典 13 | * @from 编程导航学习圈 14 | */ 15 | @Slf4j 16 | public class FailOverTolerantStrategy implements TolerantStrategy { 17 | 18 | @Override 19 | public RpcResponse doTolerant(Map context, Exception e) { 20 | // todo 可自行扩展,获取其他服务节点并调用 21 | return null; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/fault/tolerant/FailSafeTolerantStrategy.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.fault.tolerant; 2 | 3 | import com.yupi.yurpc.model.RpcResponse; 4 | import lombok.extern.slf4j.Slf4j; 5 | 6 | import java.util.Map; 7 | 8 | /** 9 | * 静默处理异常 - 容错策略 10 | * 11 | * @author 程序员鱼皮 12 | * @learn 鱼皮的编程宝典 13 | * @from 编程导航学习圈 14 | */ 15 | @Slf4j 16 | public class FailSafeTolerantStrategy implements TolerantStrategy { 17 | 18 | @Override 19 | public RpcResponse doTolerant(Map context, Exception e) { 20 | log.info("静默处理异常", e); 21 | return new RpcResponse(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/fault/tolerant/TolerantStrategy.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.fault.tolerant; 2 | 3 | import com.yupi.yurpc.model.RpcResponse; 4 | 5 | import java.util.Map; 6 | 7 | /** 8 | * 容错策略 9 | * 10 | * @author 程序员鱼皮 11 | * @learn 鱼皮的编程宝典 12 | * @from 编程导航学习圈 13 | */ 14 | public interface TolerantStrategy { 15 | 16 | /** 17 | * 容错 18 | * 19 | * @param context 上下文,用于传递数据 20 | * @param e 异常 21 | * @return 22 | */ 23 | RpcResponse doTolerant(Map context, Exception e); 24 | } 25 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/fault/tolerant/TolerantStrategyFactory.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.fault.tolerant; 2 | 3 | import com.yupi.yurpc.spi.SpiLoader; 4 | 5 | /** 6 | * 容错策略工厂(工厂模式,用于获取容错策略对象) 7 | * 8 | * @author 程序员鱼皮 9 | * @learn 鱼皮的编程宝典 10 | * @from 编程导航学习圈 11 | */ 12 | public class TolerantStrategyFactory { 13 | 14 | static { 15 | SpiLoader.load(TolerantStrategy.class); 16 | } 17 | 18 | /** 19 | * 默认容错策略 20 | */ 21 | private static final TolerantStrategy DEFAULT_RETRY_STRATEGY = new FailFastTolerantStrategy(); 22 | 23 | /** 24 | * 获取实例 25 | * 26 | * @param key 27 | * @return 28 | */ 29 | public static TolerantStrategy getInstance(String key) { 30 | return SpiLoader.getInstance(TolerantStrategy.class, key); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/fault/tolerant/TolerantStrategyKeys.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.fault.tolerant; 2 | 3 | /** 4 | * 容错策略键名常量 5 | * 6 | * @author 程序员鱼皮 7 | * @learn 鱼皮的编程宝典 8 | * @from 编程导航学习圈 9 | */ 10 | public interface TolerantStrategyKeys { 11 | 12 | /** 13 | * 故障恢复 14 | */ 15 | String FAIL_BACK = "failBack"; 16 | 17 | /** 18 | * 快速失败 19 | */ 20 | String FAIL_FAST = "failFast"; 21 | 22 | /** 23 | * 故障转移 24 | */ 25 | String FAIL_OVER = "failOver"; 26 | 27 | /** 28 | * 静默处理 29 | */ 30 | String FAIL_SAFE = "failSafe"; 31 | 32 | } 33 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/loadbalancer/ConsistentHashLoadBalancer.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.loadbalancer; 2 | 3 | import com.yupi.yurpc.model.ServiceMetaInfo; 4 | 5 | import java.util.List; 6 | import java.util.Map; 7 | import java.util.TreeMap; 8 | 9 | /** 10 | * 一致性哈希负载均衡器 11 | * 12 | * @author 程序员鱼皮 13 | * @learn 鱼皮的编程宝典 14 | * @from 编程导航学习圈 15 | */ 16 | public class ConsistentHashLoadBalancer implements LoadBalancer { 17 | 18 | /** 19 | * 一致性 Hash 环,存放虚拟节点 20 | */ 21 | private final TreeMap virtualNodes = new TreeMap<>(); 22 | 23 | /** 24 | * 虚拟节点数 25 | */ 26 | private static final int VIRTUAL_NODE_NUM = 100; 27 | 28 | @Override 29 | public ServiceMetaInfo select(Map requestParams, List serviceMetaInfoList) { 30 | if (serviceMetaInfoList.isEmpty()) { 31 | return null; 32 | } 33 | 34 | // 构建虚拟节点环 35 | for (ServiceMetaInfo serviceMetaInfo : serviceMetaInfoList) { 36 | for (int i = 0; i < VIRTUAL_NODE_NUM; i++) { 37 | int hash = getHash(serviceMetaInfo.getServiceAddress() + "#" + i); 38 | virtualNodes.put(hash, serviceMetaInfo); 39 | } 40 | } 41 | 42 | // 获取调用请求的 hash 值 43 | int hash = getHash(requestParams); 44 | 45 | // 选择最接近且大于等于调用请求 hash 值的虚拟节点 46 | Map.Entry entry = virtualNodes.ceilingEntry(hash); 47 | if (entry == null) { 48 | // 如果没有大于等于调用请求 hash 值的虚拟节点,则返回环首部的节点 49 | entry = virtualNodes.firstEntry(); 50 | } 51 | return entry.getValue(); 52 | } 53 | 54 | 55 | /** 56 | * Hash 算法,可自行实现 57 | * 58 | * @param key 59 | * @return 60 | */ 61 | private int getHash(Object key) { 62 | return key.hashCode(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/loadbalancer/LoadBalancer.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.loadbalancer; 2 | 3 | import com.yupi.yurpc.model.ServiceMetaInfo; 4 | 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | /** 9 | * 负载均衡器(消费端使用) 10 | * 11 | * @author 程序员鱼皮 12 | * @learn 鱼皮的编程宝典 13 | * @from 编程导航学习圈 14 | */ 15 | public interface LoadBalancer { 16 | 17 | /** 18 | * 选择服务调用 19 | * 20 | * @param requestParams 请求参数 21 | * @param serviceMetaInfoList 可用服务列表 22 | * @return 23 | */ 24 | ServiceMetaInfo select(Map requestParams, List serviceMetaInfoList); 25 | } 26 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/loadbalancer/LoadBalancerFactory.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.loadbalancer; 2 | 3 | import com.yupi.yurpc.spi.SpiLoader; 4 | 5 | /** 6 | * 负载均衡器工厂(工厂模式,用于获取负载均衡器对象) 7 | * 8 | * @author 程序员鱼皮 9 | * @learn 编程宝典 10 | * @from 编程导航知识星球 11 | */ 12 | public class LoadBalancerFactory { 13 | 14 | static { 15 | SpiLoader.load(LoadBalancer.class); 16 | } 17 | 18 | /** 19 | * 默认负载均衡器 20 | */ 21 | private static final LoadBalancer DEFAULT_LOAD_BALANCER = new RoundRobinLoadBalancer(); 22 | 23 | /** 24 | * 获取实例 25 | * 26 | * @param key 27 | * @return 28 | */ 29 | public static LoadBalancer getInstance(String key) { 30 | return SpiLoader.getInstance(LoadBalancer.class, key); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/loadbalancer/LoadBalancerKeys.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.loadbalancer; 2 | 3 | /** 4 | * 负载均衡器键名常量 5 | * 6 | * @author 程序员鱼皮 7 | * @learn 鱼皮的编程宝典 8 | * @from 编程导航学习圈 9 | */ 10 | public interface LoadBalancerKeys { 11 | 12 | /** 13 | * 轮询 14 | */ 15 | String ROUND_ROBIN = "roundRobin"; 16 | 17 | String RANDOM = "random"; 18 | 19 | String CONSISTENT_HASH = "consistentHash"; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/loadbalancer/RandomLoadBalancer.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.loadbalancer; 2 | 3 | import com.yupi.yurpc.model.ServiceMetaInfo; 4 | 5 | import java.util.List; 6 | import java.util.Map; 7 | import java.util.Random; 8 | 9 | /** 10 | * 随机负载均衡器 11 | * 12 | * @author 程序员鱼皮 13 | * @learn 鱼皮的编程宝典 14 | * @from 编程导航学习圈 15 | */ 16 | public class RandomLoadBalancer implements LoadBalancer { 17 | 18 | private final Random random = new Random(); 19 | 20 | @Override 21 | public ServiceMetaInfo select(Map requestParams, List serviceMetaInfoList) { 22 | int size = serviceMetaInfoList.size(); 23 | if (size == 0) { 24 | return null; 25 | } 26 | // 只有 1 个服务,不用随机 27 | if (size == 1) { 28 | return serviceMetaInfoList.get(0); 29 | } 30 | return serviceMetaInfoList.get(random.nextInt(size)); 31 | } 32 | } -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/loadbalancer/RoundRobinLoadBalancer.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.loadbalancer; 2 | 3 | import com.yupi.yurpc.model.ServiceMetaInfo; 4 | 5 | import java.util.List; 6 | import java.util.Map; 7 | import java.util.concurrent.atomic.AtomicInteger; 8 | 9 | /** 10 | * 轮询负载均衡器 11 | * 12 | * @author 程序员鱼皮 13 | * @learn 鱼皮的编程宝典 14 | * @from 编程导航学习圈 15 | */ 16 | public class RoundRobinLoadBalancer implements LoadBalancer { 17 | 18 | /** 19 | * 当前轮询的下标 20 | */ 21 | private final AtomicInteger currentIndex = new AtomicInteger(0); 22 | 23 | @Override 24 | public ServiceMetaInfo select(Map requestParams, List serviceMetaInfoList) { 25 | if (serviceMetaInfoList.isEmpty()) { 26 | return null; 27 | } 28 | // 只有一个服务,无需轮询 29 | int size = serviceMetaInfoList.size(); 30 | if (size == 1) { 31 | return serviceMetaInfoList.get(0); 32 | } 33 | // 取模算法轮询 34 | int index = currentIndex.getAndIncrement() % size; 35 | return serviceMetaInfoList.get(index); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/model/RpcRequest.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.model; 2 | 3 | import com.yupi.yurpc.constant.RpcConstant; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | import java.io.Serializable; 10 | 11 | /** 12 | * RPC 请求 13 | * 14 | * @author 程序员鱼皮 15 | * @learn 编程宝典 16 | * @from 编程导航知识星球 17 | */ 18 | @Data 19 | @Builder 20 | @AllArgsConstructor 21 | @NoArgsConstructor 22 | public class RpcRequest implements Serializable { 23 | 24 | /** 25 | * 服务名称 26 | */ 27 | private String serviceName; 28 | 29 | /** 30 | * 方法名称 31 | */ 32 | private String methodName; 33 | 34 | /** 35 | * 服务版本 36 | */ 37 | private String serviceVersion = RpcConstant.DEFAULT_SERVICE_VERSION; 38 | 39 | /** 40 | * 参数类型列表 41 | */ 42 | private Class[] parameterTypes; 43 | 44 | /** 45 | * 参数列表 46 | */ 47 | private Object[] args; 48 | 49 | } 50 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/model/RpcResponse.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.io.Serializable; 9 | 10 | /** 11 | * RPC 响应 12 | * 13 | * @author 程序员鱼皮 14 | * @learn 编程宝典 15 | * @from 编程导航知识星球 16 | */ 17 | @Data 18 | @Builder 19 | @AllArgsConstructor 20 | @NoArgsConstructor 21 | public class RpcResponse implements Serializable { 22 | 23 | /** 24 | * 响应数据 25 | */ 26 | private Object data; 27 | 28 | /** 29 | * 响应数据类型(预留) 30 | */ 31 | private Class dataType; 32 | 33 | /** 34 | * 响应信息 35 | */ 36 | private String message; 37 | 38 | /** 39 | * 异常信息 40 | */ 41 | private Exception exception; 42 | 43 | } 44 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/model/ServiceMetaInfo.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.model; 2 | 3 | import cn.hutool.core.util.StrUtil; 4 | import com.yupi.yurpc.constant.RpcConstant; 5 | import lombok.Data; 6 | 7 | /** 8 | * 服务元信息(注册信息) 9 | * 10 | * @author 程序员鱼皮 11 | * @from 编程导航学习圈 12 | * @learn 鱼皮的编程宝典 13 | */ 14 | @Data 15 | public class ServiceMetaInfo { 16 | 17 | /** 18 | * 服务名称 19 | */ 20 | private String serviceName; 21 | 22 | /** 23 | * 服务版本号 24 | */ 25 | private String serviceVersion = RpcConstant.DEFAULT_SERVICE_VERSION; 26 | 27 | /** 28 | * 服务域名 29 | */ 30 | private String serviceHost; 31 | 32 | /** 33 | * 服务端口号 34 | */ 35 | private Integer servicePort; 36 | 37 | /** 38 | * 服务分组(暂未实现) 39 | */ 40 | private String serviceGroup = "default"; 41 | 42 | /** 43 | * 获取服务键名 44 | * 45 | * @return 46 | */ 47 | public String getServiceKey() { 48 | // 后续可扩展服务分组 49 | // return String.format("%s:%s:%s", serviceName, serviceVersion, serviceGroup); 50 | return String.format("%s:%s", serviceName, serviceVersion); 51 | } 52 | 53 | /** 54 | * 获取服务注册节点键名 55 | * 56 | * @return 57 | */ 58 | public String getServiceNodeKey() { 59 | return String.format("%s/%s:%s", getServiceKey(), serviceHost, servicePort); 60 | } 61 | 62 | /** 63 | * 获取完整服务地址 64 | * 65 | * @return 66 | */ 67 | public String getServiceAddress() { 68 | if (!StrUtil.contains(serviceHost, "http")) { 69 | return String.format("http://%s:%s", serviceHost, servicePort); 70 | } 71 | return String.format("%s:%s", serviceHost, servicePort); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/model/ServiceRegisterInfo.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * 服务注册信息类 9 | * 10 | * @author 程序员鱼皮 11 | * @from 编程导航学习圈 12 | * @learn 鱼皮的编程宝典 13 | */ 14 | @Data 15 | @AllArgsConstructor 16 | @NoArgsConstructor 17 | public class ServiceRegisterInfo { 18 | 19 | /** 20 | * 服务名称 21 | */ 22 | private String serviceName; 23 | 24 | /** 25 | * 实现类 26 | */ 27 | private Class implClass; 28 | } 29 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/protocol/ProtocolConstant.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.protocol; 2 | 3 | /** 4 | * 协议常量 5 | * 6 | * @author 程序员鱼皮 7 | * @from 编程导航学习圈 8 | * @learn 鱼皮的编程宝典 9 | */ 10 | public interface ProtocolConstant { 11 | 12 | /** 13 | * 消息头长度 14 | */ 15 | int MESSAGE_HEADER_LENGTH = 17; 16 | 17 | /** 18 | * 协议魔数 19 | */ 20 | byte PROTOCOL_MAGIC = 0x1; 21 | 22 | /** 23 | * 协议版本号 24 | */ 25 | byte PROTOCOL_VERSION = 0x1; 26 | } 27 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/protocol/ProtocolMessage.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.protocol; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * 协议消息结构 9 | * 10 | * @author 程序员鱼皮 11 | * @from 编程导航学习圈 12 | * @learn 鱼皮的编程宝典 13 | */ 14 | @Data 15 | @AllArgsConstructor 16 | @NoArgsConstructor 17 | public class ProtocolMessage { 18 | 19 | /** 20 | * 消息头 21 | */ 22 | private Header header; 23 | 24 | /** 25 | * 消息体(请求或响应对象) 26 | */ 27 | private T body; 28 | 29 | /** 30 | * 协议消息头 31 | */ 32 | @Data 33 | public static class Header { 34 | 35 | /** 36 | * 魔数,保证安全性 37 | */ 38 | private byte magic; 39 | 40 | /** 41 | * 版本号 42 | */ 43 | private byte version; 44 | 45 | /** 46 | * 序列化器 47 | */ 48 | private byte serializer; 49 | 50 | /** 51 | * 消息类型(请求 / 响应) 52 | */ 53 | private byte type; 54 | 55 | /** 56 | * 状态 57 | */ 58 | private byte status; 59 | 60 | /** 61 | * 请求 id 62 | */ 63 | private long requestId; 64 | 65 | /** 66 | * 消息体长度 67 | */ 68 | private int bodyLength; 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/protocol/ProtocolMessageDecoder.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.protocol; 2 | 3 | import com.yupi.yurpc.model.RpcRequest; 4 | import com.yupi.yurpc.model.RpcResponse; 5 | import com.yupi.yurpc.serializer.Serializer; 6 | import com.yupi.yurpc.serializer.SerializerFactory; 7 | import io.vertx.core.buffer.Buffer; 8 | 9 | import java.io.IOException; 10 | 11 | /** 12 | * 协议消息解码器 13 | * 14 | * @author 程序员鱼皮 15 | * @from 编程导航学习圈 16 | * @learn 鱼皮的编程宝典 17 | */ 18 | public class ProtocolMessageDecoder { 19 | 20 | /** 21 | * 解码 22 | * 23 | * @param buffer 24 | * @return 25 | * @throws IOException 26 | */ 27 | public static ProtocolMessage decode(Buffer buffer) throws IOException { 28 | // 分别从指定位置读出 Buffer 29 | ProtocolMessage.Header header = new ProtocolMessage.Header(); 30 | byte magic = buffer.getByte(0); 31 | // 校验魔数 32 | if (magic != ProtocolConstant.PROTOCOL_MAGIC) { 33 | throw new RuntimeException("消息 magic 非法"); 34 | } 35 | header.setMagic(magic); 36 | header.setVersion(buffer.getByte(1)); 37 | header.setSerializer(buffer.getByte(2)); 38 | header.setType(buffer.getByte(3)); 39 | header.setStatus(buffer.getByte(4)); 40 | header.setRequestId(buffer.getLong(5)); 41 | header.setBodyLength(buffer.getInt(13)); 42 | // 解决粘包问题,只读指定长度的数据 43 | byte[] bodyBytes = buffer.getBytes(17, 17 + header.getBodyLength()); 44 | // 解析消息体 45 | ProtocolMessageSerializerEnum serializerEnum = ProtocolMessageSerializerEnum.getEnumByKey(header.getSerializer()); 46 | if (serializerEnum == null) { 47 | throw new RuntimeException("序列化消息的协议不存在"); 48 | } 49 | Serializer serializer = SerializerFactory.getInstance(serializerEnum.getValue()); 50 | ProtocolMessageTypeEnum messageTypeEnum = ProtocolMessageTypeEnum.getEnumByKey(header.getType()); 51 | if (messageTypeEnum == null) { 52 | throw new RuntimeException("序列化消息的类型不存在"); 53 | } 54 | switch (messageTypeEnum) { 55 | case REQUEST: 56 | RpcRequest request = serializer.deserialize(bodyBytes, RpcRequest.class); 57 | return new ProtocolMessage<>(header, request); 58 | case RESPONSE: 59 | RpcResponse response = serializer.deserialize(bodyBytes, RpcResponse.class); 60 | return new ProtocolMessage<>(header, response); 61 | case HEART_BEAT: 62 | case OTHERS: 63 | default: 64 | throw new RuntimeException("暂不支持该消息类型"); 65 | } 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/protocol/ProtocolMessageEncoder.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.protocol; 2 | 3 | import com.yupi.yurpc.serializer.Serializer; 4 | import com.yupi.yurpc.serializer.SerializerFactory; 5 | import io.vertx.core.buffer.Buffer; 6 | 7 | import java.io.IOException; 8 | 9 | /** 10 | * 协议消息编码器 11 | * 12 | * @author 程序员鱼皮 13 | * @from 编程导航学习圈 14 | * @learn 鱼皮的编程宝典 15 | */ 16 | public class ProtocolMessageEncoder { 17 | 18 | /** 19 | * 编码 20 | * 21 | * @param protocolMessage 22 | * @return 23 | * @throws IOException 24 | */ 25 | public static Buffer encode(ProtocolMessage protocolMessage) throws IOException { 26 | if (protocolMessage == null || protocolMessage.getHeader() == null) { 27 | return Buffer.buffer(); 28 | } 29 | ProtocolMessage.Header header = protocolMessage.getHeader(); 30 | // 依次向缓冲区写入字节 31 | Buffer buffer = Buffer.buffer(); 32 | buffer.appendByte(header.getMagic()); 33 | buffer.appendByte(header.getVersion()); 34 | buffer.appendByte(header.getSerializer()); 35 | buffer.appendByte(header.getType()); 36 | buffer.appendByte(header.getStatus()); 37 | buffer.appendLong(header.getRequestId()); 38 | // 获取序列化器 39 | ProtocolMessageSerializerEnum serializerEnum = ProtocolMessageSerializerEnum.getEnumByKey(header.getSerializer()); 40 | if (serializerEnum == null) { 41 | throw new RuntimeException("序列化协议不存在"); 42 | } 43 | Serializer serializer = SerializerFactory.getInstance(serializerEnum.getValue()); 44 | byte[] bodyBytes = serializer.serialize(protocolMessage.getBody()); 45 | // 写入 body 长度和数据 46 | buffer.appendInt(bodyBytes.length); 47 | buffer.appendBytes(bodyBytes); 48 | return buffer; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/protocol/ProtocolMessageSerializerEnum.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.protocol; 2 | 3 | import cn.hutool.core.util.ObjectUtil; 4 | import lombok.Getter; 5 | 6 | import java.util.Arrays; 7 | import java.util.List; 8 | import java.util.stream.Collectors; 9 | 10 | /** 11 | * 协议消息的序列化器枚举 12 | * 13 | * @author 程序员鱼皮 14 | * @from 编程导航学习圈 15 | * @learn 鱼皮的编程宝典 16 | */ 17 | @Getter 18 | public enum ProtocolMessageSerializerEnum { 19 | 20 | JDK(0, "jdk"), 21 | JSON(1, "json"), 22 | KRYO(2, "kryo"), 23 | HESSIAN(3, "hessian"); 24 | 25 | private final int key; 26 | 27 | private final String value; 28 | 29 | ProtocolMessageSerializerEnum(int key, String value) { 30 | this.key = key; 31 | this.value = value; 32 | } 33 | 34 | /** 35 | * 获取值列表 36 | * 37 | * @return 38 | */ 39 | public static List getValues() { 40 | return Arrays.stream(values()).map(item -> item.value).collect(Collectors.toList()); 41 | } 42 | 43 | /** 44 | * 根据 key 获取枚举 45 | * 46 | * @param key 47 | * @return 48 | */ 49 | public static ProtocolMessageSerializerEnum getEnumByKey(int key) { 50 | for (ProtocolMessageSerializerEnum anEnum : ProtocolMessageSerializerEnum.values()) { 51 | if (anEnum.key == key) { 52 | return anEnum; 53 | } 54 | } 55 | return null; 56 | } 57 | 58 | 59 | /** 60 | * 根据 value 获取枚举 61 | * 62 | * @param value 63 | * @return 64 | */ 65 | public static ProtocolMessageSerializerEnum getEnumByValue(String value) { 66 | if (ObjectUtil.isEmpty(value)) { 67 | return null; 68 | } 69 | for (ProtocolMessageSerializerEnum anEnum : ProtocolMessageSerializerEnum.values()) { 70 | if (anEnum.value.equals(value)) { 71 | return anEnum; 72 | } 73 | } 74 | return null; 75 | } 76 | } -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/protocol/ProtocolMessageStatusEnum.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.protocol; 2 | 3 | import lombok.Getter; 4 | 5 | /** 6 | * 协议消息的状态枚举 7 | * 8 | * @author 程序员鱼皮 9 | * @from 编程导航学习圈 10 | * @learn 鱼皮的编程宝典 11 | */ 12 | @Getter 13 | public enum ProtocolMessageStatusEnum { 14 | 15 | OK("ok", 20), 16 | BAD_REQUEST("badRequest", 40), 17 | BAD_RESPONSE("badResponse", 50); 18 | 19 | private final String text; 20 | 21 | private final int value; 22 | 23 | ProtocolMessageStatusEnum(String text, int value) { 24 | this.text = text; 25 | this.value = value; 26 | } 27 | 28 | /** 29 | * 根据 value 获取枚举 30 | * 31 | * @param value 32 | * @return 33 | */ 34 | public static ProtocolMessageStatusEnum getEnumByValue(int value) { 35 | for (ProtocolMessageStatusEnum anEnum : ProtocolMessageStatusEnum.values()) { 36 | if (anEnum.value == value) { 37 | return anEnum; 38 | } 39 | } 40 | return null; 41 | } 42 | } -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/protocol/ProtocolMessageTypeEnum.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.protocol; 2 | 3 | import lombok.Getter; 4 | 5 | /** 6 | * 协议消息的类型枚举 7 | * 8 | * @author 程序员鱼皮 9 | * @from 编程导航学习圈 10 | * @learn 鱼皮的编程宝典 11 | */ 12 | @Getter 13 | public enum ProtocolMessageTypeEnum { 14 | 15 | REQUEST(0), 16 | RESPONSE(1), 17 | HEART_BEAT(2), 18 | OTHERS(3); 19 | 20 | private final int key; 21 | 22 | ProtocolMessageTypeEnum(int key) { 23 | this.key = key; 24 | } 25 | 26 | /** 27 | * 根据 key 获取枚举 28 | * 29 | * @param key 30 | * @return 31 | */ 32 | public static ProtocolMessageTypeEnum getEnumByKey(int key) { 33 | for (ProtocolMessageTypeEnum anEnum : ProtocolMessageTypeEnum.values()) { 34 | if (anEnum.key == key) { 35 | return anEnum; 36 | } 37 | } 38 | return null; 39 | } 40 | } -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/proxy/MockServiceProxy.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.proxy; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | 5 | import java.lang.reflect.InvocationHandler; 6 | import java.lang.reflect.Method; 7 | 8 | /** 9 | * Mock 服务代理(JDK 动态代理) 10 | * 11 | * @author 程序员鱼皮 12 | * @learn 编程宝典 13 | * @from 编程导航知识星球 14 | */ 15 | @Slf4j 16 | public class MockServiceProxy implements InvocationHandler { 17 | 18 | /** 19 | * 调用代理 20 | * 21 | * @return 22 | * @throws Throwable 23 | */ 24 | @Override 25 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 26 | // 根据方法的返回值类型,生成特定的默认值对象 27 | Class methodReturnType = method.getReturnType(); 28 | log.info("mock invoke {}", method.getName()); 29 | return getDefaultObject(methodReturnType); 30 | } 31 | 32 | /** 33 | * 生成指定类型的默认值对象(可自行完善默认值逻辑) 34 | * 35 | * @param type 36 | * @return 37 | */ 38 | private Object getDefaultObject(Class type) { 39 | // 基本类型 40 | if (type.isPrimitive()) { 41 | if (type == boolean.class) { 42 | return false; 43 | } else if (type == short.class) { 44 | return (short) 0; 45 | } else if (type == int.class) { 46 | return 0; 47 | } else if (type == long.class) { 48 | return 0L; 49 | } 50 | } 51 | // 对象类型 52 | return null; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/proxy/ServiceProxy.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.proxy; 2 | 3 | import cn.hutool.core.collection.CollUtil; 4 | import cn.hutool.http.HttpRequest; 5 | import cn.hutool.http.HttpResponse; 6 | import com.yupi.yurpc.RpcApplication; 7 | import com.yupi.yurpc.config.RpcConfig; 8 | import com.yupi.yurpc.constant.RpcConstant; 9 | import com.yupi.yurpc.fault.retry.RetryStrategy; 10 | import com.yupi.yurpc.fault.retry.RetryStrategyFactory; 11 | import com.yupi.yurpc.fault.tolerant.TolerantStrategy; 12 | import com.yupi.yurpc.fault.tolerant.TolerantStrategyFactory; 13 | import com.yupi.yurpc.loadbalancer.LoadBalancer; 14 | import com.yupi.yurpc.loadbalancer.LoadBalancerFactory; 15 | import com.yupi.yurpc.model.RpcRequest; 16 | import com.yupi.yurpc.model.RpcResponse; 17 | import com.yupi.yurpc.model.ServiceMetaInfo; 18 | import com.yupi.yurpc.registry.Registry; 19 | import com.yupi.yurpc.registry.RegistryFactory; 20 | import com.yupi.yurpc.serializer.Serializer; 21 | import com.yupi.yurpc.serializer.SerializerFactory; 22 | import com.yupi.yurpc.server.tcp.VertxTcpClient; 23 | 24 | import java.io.IOException; 25 | import java.lang.reflect.InvocationHandler; 26 | import java.lang.reflect.Method; 27 | import java.util.HashMap; 28 | import java.util.List; 29 | import java.util.Map; 30 | 31 | /** 32 | * 服务代理(JDK 动态代理) 33 | * 34 | * @author 程序员鱼皮 35 | * @learn 编程宝典 36 | * @from 编程导航知识星球 37 | */ 38 | public class ServiceProxy implements InvocationHandler { 39 | 40 | /** 41 | * 调用代理 42 | * 43 | * @return 44 | * @throws Throwable 45 | */ 46 | @Override 47 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 48 | // 构造请求 49 | // fixme https://github.com/liyupi/yu-rpc/issues/7 50 | String serviceName = method.getDeclaringClass().getName(); 51 | RpcRequest rpcRequest = RpcRequest.builder() 52 | .serviceName(serviceName) 53 | .methodName(method.getName()) 54 | .parameterTypes(method.getParameterTypes()) 55 | .args(args) 56 | .build(); 57 | 58 | // 从注册中心获取服务提供者请求地址 59 | RpcConfig rpcConfig = RpcApplication.getRpcConfig(); 60 | Registry registry = RegistryFactory.getInstance(rpcConfig.getRegistryConfig().getRegistry()); 61 | ServiceMetaInfo serviceMetaInfo = new ServiceMetaInfo(); 62 | serviceMetaInfo.setServiceName(serviceName); 63 | serviceMetaInfo.setServiceVersion(RpcConstant.DEFAULT_SERVICE_VERSION); 64 | List serviceMetaInfoList = registry.serviceDiscovery(serviceMetaInfo.getServiceKey()); 65 | if (CollUtil.isEmpty(serviceMetaInfoList)) { 66 | throw new RuntimeException("暂无服务地址"); 67 | } 68 | 69 | // 负载均衡 70 | LoadBalancer loadBalancer = LoadBalancerFactory.getInstance(rpcConfig.getLoadBalancer()); 71 | // 将调用方法名(请求路径)作为负载均衡参数 72 | Map requestParams = new HashMap<>(); 73 | requestParams.put("methodName", rpcRequest.getMethodName()); 74 | ServiceMetaInfo selectedServiceMetaInfo = loadBalancer.select(requestParams, serviceMetaInfoList); 75 | // // http 请求 76 | // // 指定序列化器 77 | // Serializer serializer = SerializerFactory.getInstance(RpcApplication.getRpcConfig().getSerializer()); 78 | // byte[] bodyBytes = serializer.serialize(rpcRequest); 79 | // RpcResponse rpcResponse = doHttpRequest(selectedServiceMetaInfo, bodyBytes, serializer); 80 | // rpc 请求 81 | // 使用重试机制 82 | RpcResponse rpcResponse; 83 | try { 84 | RetryStrategy retryStrategy = RetryStrategyFactory.getInstance(rpcConfig.getRetryStrategy()); 85 | rpcResponse = retryStrategy.doRetry(() -> 86 | VertxTcpClient.doRequest(rpcRequest, selectedServiceMetaInfo) 87 | ); 88 | } catch (Exception e) { 89 | // 容错机制 90 | TolerantStrategy tolerantStrategy = TolerantStrategyFactory.getInstance(rpcConfig.getTolerantStrategy()); 91 | rpcResponse = tolerantStrategy.doTolerant(null, e); 92 | } 93 | return rpcResponse.getData(); 94 | } 95 | 96 | /** 97 | * 发送 HTTP 请求 98 | * 99 | * @param selectedServiceMetaInfo 100 | * @param bodyBytes 101 | * @return 102 | * @throws IOException 103 | */ 104 | private static RpcResponse doHttpRequest(ServiceMetaInfo selectedServiceMetaInfo, byte[] bodyBytes) throws IOException { 105 | final Serializer serializer = SerializerFactory.getInstance(RpcApplication.getRpcConfig().getSerializer()); 106 | // 发送 HTTP 请求 107 | try (HttpResponse httpResponse = HttpRequest.post(selectedServiceMetaInfo.getServiceAddress()) 108 | .body(bodyBytes) 109 | .execute()) { 110 | byte[] result = httpResponse.bodyBytes(); 111 | // 反序列化 112 | RpcResponse rpcResponse = serializer.deserialize(result, RpcResponse.class); 113 | return rpcResponse; 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/proxy/ServiceProxyFactory.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.proxy; 2 | 3 | import com.yupi.yurpc.RpcApplication; 4 | 5 | import java.lang.reflect.Proxy; 6 | 7 | /** 8 | * 服务代理工厂(工厂模式,用于创建代理对象) 9 | * 10 | * @author 程序员鱼皮 11 | * @learn 编程宝典 12 | * @from 编程导航知识星球 13 | */ 14 | public class ServiceProxyFactory { 15 | 16 | /** 17 | * 根据服务类获取代理对象 18 | * 19 | * @param serviceClass 20 | * @param 21 | * @return 22 | */ 23 | public static T getProxy(Class serviceClass) { 24 | if (RpcApplication.getRpcConfig().isMock()) { 25 | return getMockProxy(serviceClass); 26 | } 27 | 28 | return (T) Proxy.newProxyInstance( 29 | serviceClass.getClassLoader(), 30 | new Class[]{serviceClass}, 31 | new ServiceProxy()); 32 | } 33 | 34 | /** 35 | * 根据服务类获取 Mock 代理对象 36 | * 37 | * @param serviceClass 38 | * @param 39 | * @return 40 | */ 41 | public static T getMockProxy(Class serviceClass) { 42 | return (T) Proxy.newProxyInstance( 43 | serviceClass.getClassLoader(), 44 | new Class[]{serviceClass}, 45 | new MockServiceProxy()); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/registry/EtcdRegistry.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.registry; 2 | 3 | import cn.hutool.core.collection.CollUtil; 4 | import cn.hutool.core.collection.ConcurrentHashSet; 5 | import cn.hutool.cron.CronUtil; 6 | import cn.hutool.cron.task.Task; 7 | import cn.hutool.json.JSONUtil; 8 | import com.yupi.yurpc.config.RegistryConfig; 9 | import com.yupi.yurpc.model.ServiceMetaInfo; 10 | import io.etcd.jetcd.*; 11 | import io.etcd.jetcd.options.GetOption; 12 | import io.etcd.jetcd.options.PutOption; 13 | import io.etcd.jetcd.watch.WatchEvent; 14 | 15 | import java.nio.charset.StandardCharsets; 16 | import java.time.Duration; 17 | import java.util.HashSet; 18 | import java.util.List; 19 | import java.util.Set; 20 | import java.util.stream.Collectors; 21 | 22 | /** 23 | * Etcd 注册中心 24 | * 25 | * @author coder_yupi 26 | * @from 编程导航学习圈 27 | * @learn yupi 的编程宝典 28 | */ 29 | public class EtcdRegistry implements Registry { 30 | 31 | private Client client; 32 | 33 | private KV kvClient; 34 | 35 | /** 36 | * 本机注册的节点 key 集合(用于维护续期) 37 | */ 38 | private final Set localRegisterNodeKeySet = new HashSet<>(); 39 | 40 | /** 41 | * 注册中心服务缓存(只支持单个服务缓存,已废弃,请使用下方的 RegistryServiceMultiCache) 42 | */ 43 | @Deprecated 44 | private final RegistryServiceCache registryServiceCache = new RegistryServiceCache(); 45 | 46 | /** 47 | * 注册中心服务缓存(支持多个服务键) 48 | */ 49 | private final RegistryServiceMultiCache registryServiceMultiCache = new RegistryServiceMultiCache(); 50 | 51 | /** 52 | * 正在监听的 key 集合 53 | */ 54 | private final Set watchingKeySet = new ConcurrentHashSet<>(); 55 | 56 | /** 57 | * 根节点 58 | */ 59 | private static final String ETCD_ROOT_PATH = "/rpc/"; 60 | 61 | @Override 62 | public void init(RegistryConfig registryConfig) { 63 | client = Client.builder() 64 | .endpoints(registryConfig.getAddress()) 65 | .connectTimeout(Duration.ofMillis(registryConfig.getTimeout())) 66 | .build(); 67 | kvClient = client.getKVClient(); 68 | heartBeat(); 69 | } 70 | 71 | @Override 72 | public void register(ServiceMetaInfo serviceMetaInfo) throws Exception { 73 | // 创建 Lease 和 KV 客户端 74 | Lease leaseClient = client.getLeaseClient(); 75 | 76 | // 创建一个 30 秒的租约 77 | long leaseId = leaseClient.grant(30).get().getID(); 78 | 79 | // 设置要存储的键值对 80 | String registerKey = ETCD_ROOT_PATH + serviceMetaInfo.getServiceNodeKey(); 81 | ByteSequence key = ByteSequence.from(registerKey, StandardCharsets.UTF_8); 82 | ByteSequence value = ByteSequence.from(JSONUtil.toJsonStr(serviceMetaInfo), StandardCharsets.UTF_8); 83 | 84 | // 将键值对与租约关联起来,并设置过期时间 85 | PutOption putOption = PutOption.builder().withLeaseId(leaseId).build(); 86 | kvClient.put(key, value, putOption).get(); 87 | // 添加节点信息到本地缓存 88 | localRegisterNodeKeySet.add(registerKey); 89 | } 90 | 91 | @Override 92 | public void unRegister(ServiceMetaInfo serviceMetaInfo) { 93 | String registerKey = ETCD_ROOT_PATH + serviceMetaInfo.getServiceNodeKey(); 94 | kvClient.delete(ByteSequence.from(registerKey, StandardCharsets.UTF_8)); 95 | // 也要从本地缓存移除 96 | localRegisterNodeKeySet.remove(registerKey); 97 | } 98 | 99 | @Override 100 | public List serviceDiscovery(String serviceKey) { 101 | // 优先从缓存获取服务 102 | // 原教程代码,不支持多个服务同时缓存 103 | // List cachedServiceMetaInfoList = registryServiceCache.readCache(); 104 | // 优化后的代码,支持多个服务同时缓存 105 | List cachedServiceMetaInfoList = registryServiceMultiCache.readCache(serviceKey); 106 | if (cachedServiceMetaInfoList != null) { 107 | return cachedServiceMetaInfoList; 108 | } 109 | 110 | // 前缀搜索,结尾一定要加 '/' 111 | String searchPrefix = ETCD_ROOT_PATH + serviceKey + "/"; 112 | 113 | try { 114 | // 前缀查询 115 | GetOption getOption = GetOption.builder().isPrefix(true).build(); 116 | List keyValues = kvClient.get( 117 | ByteSequence.from(searchPrefix, StandardCharsets.UTF_8), 118 | getOption) 119 | .get() 120 | .getKvs(); 121 | // 解析服务信息 122 | List serviceMetaInfoList = keyValues.stream() 123 | .map(keyValue -> { 124 | String key = keyValue.getKey().toString(StandardCharsets.UTF_8); 125 | // 监听 key 的变化 126 | watch(key); 127 | String value = keyValue.getValue().toString(StandardCharsets.UTF_8); 128 | return JSONUtil.toBean(value, ServiceMetaInfo.class); 129 | }) 130 | .collect(Collectors.toList()); 131 | // 写入服务缓存 132 | // 原教程代码,不支持多个服务同时缓存 133 | // registryServiceCache.writeCache(serviceMetaInfoList); 134 | // 优化后的代码,支持多个服务同时缓存 135 | registryServiceMultiCache.writeCache(serviceKey, serviceMetaInfoList); 136 | return serviceMetaInfoList; 137 | } catch (Exception e) { 138 | throw new RuntimeException("获取服务列表失败", e); 139 | } 140 | } 141 | 142 | @Override 143 | public void heartBeat() { 144 | // 10 秒续签一次 145 | CronUtil.schedule("*/10 * * * * *", new Task() { 146 | @Override 147 | public void execute() { 148 | // 遍历本节点所有的 key 149 | for (String key : localRegisterNodeKeySet) { 150 | try { 151 | List keyValues = kvClient.get(ByteSequence.from(key, StandardCharsets.UTF_8)) 152 | .get() 153 | .getKvs(); 154 | // 该节点已过期(需要重启节点才能重新注册) 155 | if (CollUtil.isEmpty(keyValues)) { 156 | continue; 157 | } 158 | // 节点未过期,重新注册(相当于续签) 159 | KeyValue keyValue = keyValues.get(0); 160 | String value = keyValue.getValue().toString(StandardCharsets.UTF_8); 161 | ServiceMetaInfo serviceMetaInfo = JSONUtil.toBean(value, ServiceMetaInfo.class); 162 | register(serviceMetaInfo); 163 | } catch (Exception e) { 164 | throw new RuntimeException(key + "续签失败", e); 165 | } 166 | } 167 | } 168 | }); 169 | 170 | // 支持秒级别定时任务 171 | CronUtil.setMatchSecond(true); 172 | CronUtil.start(); 173 | } 174 | 175 | /** 176 | * 监听(消费端) 177 | * 178 | * @param serviceNodeKey 179 | */ 180 | @Override 181 | public void watch(String serviceNodeKey) { 182 | Watch watchClient = client.getWatchClient(); 183 | // 之前未被监听,开启监听 184 | boolean newWatch = watchingKeySet.add(serviceNodeKey); 185 | if (newWatch) { 186 | watchClient.watch(ByteSequence.from(serviceNodeKey, StandardCharsets.UTF_8), response -> { 187 | for (WatchEvent event : response.getEvents()) { 188 | switch (event.getEventType()) { 189 | // key 删除时触发 190 | case DELETE: 191 | // 清理注册服务缓存 192 | // 原教程代码,不支持多个服务同时缓存 193 | // registryServiceCache.clearCache(); 194 | // 优化后的代码,支持多个服务同时缓存 195 | // fixme 这里需要改为 serviceKey,而不是 serviceNodeKey 196 | registryServiceMultiCache.clearCache(serviceNodeKey); 197 | break; 198 | case PUT: 199 | default: 200 | break; 201 | } 202 | } 203 | }); 204 | } 205 | } 206 | 207 | @Override 208 | public void destroy() { 209 | System.out.println("当前节点下线"); 210 | // 下线节点 211 | // 遍历本节点所有的 key 212 | for (String key : localRegisterNodeKeySet) { 213 | try { 214 | kvClient.delete(ByteSequence.from(key, StandardCharsets.UTF_8)).get(); 215 | } catch (Exception e) { 216 | throw new RuntimeException(key + "节点下线失败"); 217 | } 218 | } 219 | 220 | // 释放资源 221 | if (kvClient != null) { 222 | kvClient.close(); 223 | } 224 | if (client != null) { 225 | client.close(); 226 | } 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/registry/LocalRegistry.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.registry; 2 | 3 | import java.util.Map; 4 | import java.util.concurrent.ConcurrentHashMap; 5 | 6 | /** 7 | * 本地注册中心 8 | * 9 | * @author 程序员鱼皮 10 | * @learn 编程宝典 11 | * @from 编程导航知识星球 12 | */ 13 | public class LocalRegistry { 14 | 15 | /** 16 | * 注册信息存储 17 | */ 18 | private static final Map> map = new ConcurrentHashMap<>(); 19 | 20 | /** 21 | * 注册服务 22 | * 23 | * @param serviceName 24 | * @param implClass 25 | */ 26 | public static void register(String serviceName, Class implClass) { 27 | map.put(serviceName, implClass); 28 | } 29 | 30 | /** 31 | * 获取服务 32 | * 33 | * @param serviceName 34 | * @return 35 | */ 36 | public static Class get(String serviceName) { 37 | return map.get(serviceName); 38 | } 39 | 40 | /** 41 | * 删除服务 42 | * 43 | * @param serviceName 44 | */ 45 | public static void remove(String serviceName) { 46 | map.remove(serviceName); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/registry/Registry.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.registry; 2 | 3 | import com.yupi.yurpc.config.RegistryConfig; 4 | import com.yupi.yurpc.model.ServiceMetaInfo; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * 注册中心 10 | * 11 | * @author 程序员鱼皮 12 | * @learn 编程宝典 13 | * @from 编程导航知识星球 14 | */ 15 | public interface Registry { 16 | 17 | /** 18 | * 初始化 19 | * 20 | * @param registryConfig 21 | */ 22 | void init(RegistryConfig registryConfig); 23 | 24 | /** 25 | * 注册服务(服务端) 26 | * 27 | * @param serviceMetaInfo 28 | */ 29 | void register(ServiceMetaInfo serviceMetaInfo) throws Exception; 30 | 31 | /** 32 | * 注销服务(服务端) 33 | * 34 | * @param serviceMetaInfo 35 | */ 36 | void unRegister(ServiceMetaInfo serviceMetaInfo); 37 | 38 | /** 39 | * 服务发现(获取某服务的所有节点,消费端) 40 | * 41 | * @param serviceKey 服务键名 42 | * @return 43 | */ 44 | List serviceDiscovery(String serviceKey); 45 | 46 | /** 47 | * 心跳检测(服务端) 48 | */ 49 | void heartBeat(); 50 | 51 | /** 52 | * 监听(消费端) 53 | * 54 | * @param serviceNodeKey 55 | */ 56 | void watch(String serviceNodeKey); 57 | 58 | /** 59 | * 服务销毁 60 | */ 61 | void destroy(); 62 | } 63 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/registry/RegistryFactory.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.registry; 2 | 3 | import com.yupi.yurpc.spi.SpiLoader; 4 | 5 | /** 6 | * 注册中心工厂(用于获取注册中心对象) 7 | * 8 | * @author 程序员鱼皮 9 | * @learn 编程宝典 10 | * @from 编程导航知识星球 11 | */ 12 | public class RegistryFactory { 13 | 14 | // SPI 动态加载 15 | static { 16 | SpiLoader.load(Registry.class); 17 | } 18 | 19 | /** 20 | * 默认注册中心 21 | */ 22 | private static final Registry DEFAULT_REGISTRY = new EtcdRegistry(); 23 | 24 | /** 25 | * 获取实例 26 | * 27 | * @param key 28 | * @return 29 | */ 30 | public static Registry getInstance(String key) { 31 | return SpiLoader.getInstance(Registry.class, key); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/registry/RegistryKeys.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.registry; 2 | 3 | /** 4 | * 注册中心键名常量 5 | * 6 | * @from 编程导航学习圈 7 | * @learn yupi 的编程宝典 8 | * @author coder_yupi 9 | */ 10 | public interface RegistryKeys { 11 | 12 | String ETCD = "etcd"; 13 | 14 | String ZOOKEEPER = "zookeeper"; 15 | 16 | } 17 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/registry/RegistryServiceCache.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.registry; 2 | 3 | import com.yupi.yurpc.model.ServiceMetaInfo; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * 注册中心服务本地缓存 9 | * 10 | * @author coder_yupi 11 | * @from 编程导航学习圈 12 | * @learn yupi 的编程宝典 13 | */ 14 | public class RegistryServiceCache { 15 | 16 | /** 17 | * 服务缓存 18 | */ 19 | List serviceCache; 20 | 21 | /** 22 | * 写缓存 23 | * 24 | * @param newServiceCache 25 | * @return 26 | */ 27 | void writeCache(List newServiceCache) { 28 | this.serviceCache = newServiceCache; 29 | } 30 | 31 | /** 32 | * 读缓存 33 | * 34 | * @return 35 | */ 36 | List readCache() { 37 | return this.serviceCache; 38 | } 39 | 40 | /** 41 | * 清空缓存 42 | */ 43 | void clearCache() { 44 | this.serviceCache = null; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/registry/RegistryServiceMultiCache.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.registry; 2 | 3 | import com.yupi.yurpc.model.ServiceMetaInfo; 4 | 5 | import java.util.List; 6 | import java.util.Map; 7 | import java.util.concurrent.ConcurrentHashMap; 8 | 9 | /** 10 | * 注册中心服务本地缓存(支持多个服务) 11 | * 12 | * @author coder_yupi 13 | * @from 编程导航学习圈 14 | * @learn yupi 的编程宝典 15 | */ 16 | public class RegistryServiceMultiCache { 17 | 18 | /** 19 | * 服务缓存 20 | */ 21 | Map> serviceCache = new ConcurrentHashMap<>(); 22 | 23 | /** 24 | * 写缓存 25 | * 26 | * @param serviceKey 服务键名 27 | * @param newServiceCache 更新后的缓存列表 28 | * @return 29 | */ 30 | void writeCache(String serviceKey, List newServiceCache) { 31 | this.serviceCache.put(serviceKey, newServiceCache); 32 | } 33 | 34 | /** 35 | * 读缓存 36 | * 37 | * @param serviceKey 38 | * @return 39 | */ 40 | List readCache(String serviceKey) { 41 | return this.serviceCache.get(serviceKey); 42 | } 43 | 44 | /** 45 | * 清空缓存 46 | */ 47 | void clearCache(String serviceKey) { 48 | this.serviceCache.remove(serviceKey); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/registry/ZooKeeperRegistry.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.registry; 2 | 3 | import cn.hutool.core.collection.ConcurrentHashSet; 4 | import com.yupi.yurpc.config.RegistryConfig; 5 | import com.yupi.yurpc.model.ServiceMetaInfo; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.apache.curator.framework.CuratorFramework; 8 | import org.apache.curator.framework.CuratorFrameworkFactory; 9 | import org.apache.curator.framework.recipes.cache.CuratorCache; 10 | import org.apache.curator.framework.recipes.cache.CuratorCacheListener; 11 | import org.apache.curator.retry.ExponentialBackoffRetry; 12 | import org.apache.curator.x.discovery.ServiceDiscovery; 13 | import org.apache.curator.x.discovery.ServiceDiscoveryBuilder; 14 | import org.apache.curator.x.discovery.ServiceInstance; 15 | import org.apache.curator.x.discovery.details.JsonInstanceSerializer; 16 | 17 | import java.util.Collection; 18 | import java.util.HashSet; 19 | import java.util.List; 20 | import java.util.Set; 21 | import java.util.stream.Collectors; 22 | 23 | /** 24 | * zookeeper 注册中心 25 | * 操作文档:Apache Curator 26 | * 代码示例:DiscoveryExample.java 27 | * 监听 key 示例:CuratorCacheExample.java 28 | * 29 | * @author coder_yupi 30 | * @from 编程导航学习圈 31 | * @learn yupi 的编程宝典 32 | */ 33 | @Slf4j 34 | public class ZooKeeperRegistry implements Registry { 35 | 36 | private CuratorFramework client; 37 | 38 | private ServiceDiscovery serviceDiscovery; 39 | 40 | /** 41 | * 本机注册的节点 key 集合(用于维护续期) 42 | */ 43 | private final Set localRegisterNodeKeySet = new HashSet<>(); 44 | 45 | /** 46 | * 注册中心服务缓存 47 | */ 48 | private final RegistryServiceCache registryServiceCache = new RegistryServiceCache(); 49 | 50 | /** 51 | * 正在监听的 key 集合 52 | */ 53 | private final Set watchingKeySet = new ConcurrentHashSet<>(); 54 | 55 | /** 56 | * 根节点 57 | */ 58 | private static final String ZK_ROOT_PATH = "/rpc/zk"; 59 | 60 | 61 | @Override 62 | public void init(RegistryConfig registryConfig) { 63 | // 构建 client 实例 64 | client = CuratorFrameworkFactory 65 | .builder() 66 | .connectString(registryConfig.getAddress()) 67 | .retryPolicy(new ExponentialBackoffRetry(Math.toIntExact(registryConfig.getTimeout()), 3)) 68 | .build(); 69 | 70 | // 构建 serviceDiscovery 实例 71 | serviceDiscovery = ServiceDiscoveryBuilder.builder(ServiceMetaInfo.class) 72 | .client(client) 73 | .basePath(ZK_ROOT_PATH) 74 | .serializer(new JsonInstanceSerializer<>(ServiceMetaInfo.class)) 75 | .build(); 76 | 77 | try { 78 | // 启动 client 和 serviceDiscovery 79 | client.start(); 80 | serviceDiscovery.start(); 81 | } catch (Exception e) { 82 | throw new RuntimeException(e); 83 | } 84 | } 85 | 86 | @Override 87 | public void register(ServiceMetaInfo serviceMetaInfo) throws Exception { 88 | // 注册到 zk 里 89 | serviceDiscovery.registerService(buildServiceInstance(serviceMetaInfo)); 90 | 91 | // 添加节点信息到本地缓存 92 | String registerKey = ZK_ROOT_PATH + "/" + serviceMetaInfo.getServiceNodeKey(); 93 | localRegisterNodeKeySet.add(registerKey); 94 | } 95 | 96 | @Override 97 | public void unRegister(ServiceMetaInfo serviceMetaInfo) { 98 | try { 99 | serviceDiscovery.unregisterService(buildServiceInstance(serviceMetaInfo)); 100 | } catch (Exception e) { 101 | throw new RuntimeException(e); 102 | } 103 | // 从本地缓存移除 104 | String registerKey = ZK_ROOT_PATH + "/" + serviceMetaInfo.getServiceNodeKey(); 105 | localRegisterNodeKeySet.remove(registerKey); 106 | } 107 | 108 | @Override 109 | public List serviceDiscovery(String serviceKey) { 110 | // 优先从缓存获取服务 111 | List cachedServiceMetaInfoList = registryServiceCache.readCache(); 112 | if (cachedServiceMetaInfoList != null) { 113 | return cachedServiceMetaInfoList; 114 | } 115 | 116 | try { 117 | // 查询服务信息 118 | Collection> serviceInstanceList = serviceDiscovery.queryForInstances(serviceKey); 119 | 120 | // 解析服务信息 121 | List serviceMetaInfoList = serviceInstanceList.stream() 122 | .map(ServiceInstance::getPayload) 123 | .collect(Collectors.toList()); 124 | 125 | // 写入服务缓存 126 | registryServiceCache.writeCache(serviceMetaInfoList); 127 | return serviceMetaInfoList; 128 | } catch (Exception e) { 129 | throw new RuntimeException("获取服务列表失败", e); 130 | } 131 | } 132 | 133 | @Override 134 | public void heartBeat() { 135 | // 不需要心跳机制,建立了临时节点,如果服务器故障,则临时节点直接丢失 136 | } 137 | 138 | /** 139 | * 监听(消费端) 140 | * 141 | * @param serviceNodeKey 服务节点 key 142 | */ 143 | @Override 144 | public void watch(String serviceNodeKey) { 145 | String watchKey = ZK_ROOT_PATH + "/" + serviceNodeKey; 146 | boolean newWatch = watchingKeySet.add(watchKey); 147 | if (newWatch) { 148 | CuratorCache curatorCache = CuratorCache.build(client, watchKey); 149 | curatorCache.start(); 150 | curatorCache.listenable().addListener( 151 | CuratorCacheListener 152 | .builder() 153 | .forDeletes(childData -> registryServiceCache.clearCache()) 154 | .forChanges(((oldNode, node) -> registryServiceCache.clearCache())) 155 | .build() 156 | ); 157 | } 158 | } 159 | 160 | @Override 161 | public void destroy() { 162 | log.info("当前节点下线"); 163 | // 下线节点(这一步可以不做,因为都是临时节点,服务下线,自然就被删掉了) 164 | for (String key : localRegisterNodeKeySet) { 165 | try { 166 | client.delete().guaranteed().forPath(key); 167 | } catch (Exception e) { 168 | throw new RuntimeException(key + "节点下线失败"); 169 | } 170 | } 171 | 172 | // 释放资源 173 | if (client != null) { 174 | client.close(); 175 | } 176 | } 177 | 178 | private ServiceInstance buildServiceInstance(ServiceMetaInfo serviceMetaInfo) { 179 | String serviceAddress = serviceMetaInfo.getServiceHost() + ":" + serviceMetaInfo.getServicePort(); 180 | try { 181 | return ServiceInstance 182 | .builder() 183 | .id(serviceAddress) 184 | .name(serviceMetaInfo.getServiceKey()) 185 | .address(serviceAddress) 186 | .payload(serviceMetaInfo) 187 | .build(); 188 | } catch (Exception e) { 189 | throw new RuntimeException(e); 190 | } 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/serializer/HessianSerializer.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.serializer; 2 | 3 | import com.caucho.hessian.io.HessianInput; 4 | import com.caucho.hessian.io.HessianOutput; 5 | 6 | import java.io.ByteArrayInputStream; 7 | import java.io.ByteArrayOutputStream; 8 | import java.io.IOException; 9 | 10 | /** 11 | * Hessian 序列化器 12 | * 13 | * @author 程序员鱼皮 14 | * @learn 编程宝典 15 | * @from 编程导航知识星球 16 | */ 17 | public class HessianSerializer implements Serializer { 18 | @Override 19 | public byte[] serialize(T object) throws IOException { 20 | ByteArrayOutputStream bos = new ByteArrayOutputStream(); 21 | HessianOutput ho = new HessianOutput(bos); 22 | ho.writeObject(object); 23 | return bos.toByteArray(); 24 | } 25 | 26 | @Override 27 | public T deserialize(byte[] bytes, Class tClass) throws IOException { 28 | ByteArrayInputStream bis = new ByteArrayInputStream(bytes); 29 | HessianInput hi = new HessianInput(bis); 30 | return (T) hi.readObject(tClass); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/serializer/JdkSerializer.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.serializer; 2 | 3 | import java.io.*; 4 | 5 | /** 6 | * JDK 序列化器 7 | * 8 | * @author 程序员鱼皮 9 | * @learn 编程宝典 10 | * @from 编程导航知识星球 11 | */ 12 | public class JdkSerializer implements Serializer { 13 | 14 | /** 15 | * 序列化 16 | * 17 | * @param object 18 | * @param 19 | * @return 20 | * @throws IOException 21 | */ 22 | @Override 23 | public byte[] serialize(T object) throws IOException { 24 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 25 | ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream); 26 | objectOutputStream.writeObject(object); 27 | objectOutputStream.close(); 28 | return outputStream.toByteArray(); 29 | } 30 | 31 | /** 32 | * 反序列化 33 | * 34 | * @param bytes 35 | * @param type 36 | * @param 37 | * @return 38 | * @throws IOException 39 | */ 40 | @Override 41 | public T deserialize(byte[] bytes, Class type) throws IOException { 42 | ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); 43 | ObjectInputStream objectInputStream = new ObjectInputStream(inputStream); 44 | try { 45 | return (T) objectInputStream.readObject(); 46 | } catch (ClassNotFoundException e) { 47 | throw new RuntimeException(e); 48 | } finally { 49 | objectInputStream.close(); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/serializer/JsonSerializer.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.serializer; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.yupi.yurpc.model.RpcRequest; 5 | import com.yupi.yurpc.model.RpcResponse; 6 | 7 | import java.io.IOException; 8 | 9 | /** 10 | * Json 序列化器 11 | * 12 | * @author 程序员鱼皮 13 | * @learn 编程宝典 14 | * @from 编程导航知识星球 15 | */ 16 | public class JsonSerializer implements Serializer { 17 | private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); 18 | 19 | @Override 20 | public byte[] serialize(T obj) throws IOException { 21 | return OBJECT_MAPPER.writeValueAsBytes(obj); 22 | } 23 | 24 | @Override 25 | public T deserialize(byte[] bytes, Class classType) throws IOException { 26 | T obj = OBJECT_MAPPER.readValue(bytes, classType); 27 | if (obj instanceof RpcRequest) { 28 | return handleRequest((RpcRequest) obj, classType); 29 | } 30 | if (obj instanceof RpcResponse) { 31 | return handleResponse((RpcResponse) obj, classType); 32 | } 33 | return obj; 34 | } 35 | 36 | /** 37 | * 由于 Object 的原始对象会被擦除,导致反序列化时会被作为 LinkedHashMap 无法转换成原始对象,因此这里做了特殊处理 38 | * 39 | * @param rpcRequest rpc 请求 40 | * @param type 类型 41 | * @return {@link T} 42 | * @throws IOException IO异常 43 | */ 44 | private T handleRequest(RpcRequest rpcRequest, Class type) throws IOException { 45 | Class[] parameterTypes = rpcRequest.getParameterTypes(); 46 | Object[] args = rpcRequest.getArgs(); 47 | 48 | // 循环处理每个参数的类型 49 | for (int i = 0; i < parameterTypes.length; i++) { 50 | Class clazz = parameterTypes[i]; 51 | // 如果类型不同,则重新处理一下类型 52 | if (!clazz.isAssignableFrom(args[i].getClass())) { 53 | byte[] argBytes = OBJECT_MAPPER.writeValueAsBytes(args[i]); 54 | args[i] = OBJECT_MAPPER.readValue(argBytes, clazz); 55 | } 56 | } 57 | return type.cast(rpcRequest); 58 | } 59 | 60 | /** 61 | * 由于 Object 的原始对象会被擦除,导致反序列化时会被作为 LinkedHashMap 无法转换成原始对象,因此这里做了特殊处理 62 | * 63 | * @param rpcResponse rpc 响应 64 | * @param type 类型 65 | * @return {@link T} 66 | * @throws IOException IO异常 67 | */ 68 | private T handleResponse(RpcResponse rpcResponse, Class type) throws IOException { 69 | // 处理响应数据 70 | byte[] dataBytes = OBJECT_MAPPER.writeValueAsBytes(rpcResponse.getData()); 71 | rpcResponse.setData(OBJECT_MAPPER.readValue(dataBytes, rpcResponse.getDataType())); 72 | return type.cast(rpcResponse); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/serializer/KryoSerializer.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.serializer; 2 | 3 | import com.esotericsoftware.kryo.Kryo; 4 | import com.esotericsoftware.kryo.io.Input; 5 | import com.esotericsoftware.kryo.io.Output; 6 | 7 | import java.io.ByteArrayInputStream; 8 | import java.io.ByteArrayOutputStream; 9 | 10 | /** 11 | * Kryo 序列化器 12 | * 13 | * @author 程序员鱼皮 14 | * @learn 编程宝典 15 | * @from 编程导航知识星球 16 | */ 17 | public class KryoSerializer implements Serializer { 18 | /** 19 | * kryo 线程不安全,使用 ThreadLocal 保证每个线程只有一个 Kryo 20 | */ 21 | private static final ThreadLocal KRYO_THREAD_LOCAL = ThreadLocal.withInitial(() -> { 22 | Kryo kryo = new Kryo(); 23 | // 设置动态动态序列化和反序列化类,不提前注册所有类(可能有安全问题) 24 | kryo.setRegistrationRequired(false); 25 | return kryo; 26 | }); 27 | 28 | @Override 29 | public byte[] serialize(T obj) { 30 | ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 31 | Output output = new Output(byteArrayOutputStream); 32 | KRYO_THREAD_LOCAL.get().writeObject(output, obj); 33 | output.close(); 34 | return byteArrayOutputStream.toByteArray(); 35 | } 36 | 37 | @Override 38 | public T deserialize(byte[] bytes, Class classType) { 39 | ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); 40 | Input input = new Input(byteArrayInputStream); 41 | T result = KRYO_THREAD_LOCAL.get().readObject(input, classType); 42 | input.close(); 43 | return result; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/serializer/Serializer.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.serializer; 2 | 3 | import java.io.IOException; 4 | 5 | /** 6 | * 序列化器接口 7 | * 8 | * @author 程序员鱼皮 9 | * @learn 编程宝典 10 | * @from 编程导航知识星球 11 | */ 12 | public interface Serializer { 13 | 14 | /** 15 | * 序列化 16 | * 17 | * @param object 18 | * @param 19 | * @return 20 | * @throws IOException 21 | */ 22 | byte[] serialize(T object) throws IOException; 23 | 24 | /** 25 | * 反序列化 26 | * 27 | * @param bytes 28 | * @param tClass 29 | * @param 30 | * @return 31 | * @throws IOException 32 | */ 33 | T deserialize(byte[] bytes, Class tClass) throws IOException; 34 | } 35 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/serializer/SerializerFactory.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.serializer; 2 | 3 | import com.yupi.yurpc.spi.SpiLoader; 4 | 5 | /** 6 | * 序列化器工厂(工厂模式,用于获取序列化器对象) 7 | * 8 | * @author 程序员鱼皮 9 | * @learn 编程宝典 10 | * @from 编程导航知识星球 11 | */ 12 | public class SerializerFactory { 13 | 14 | static { 15 | SpiLoader.load(Serializer.class); 16 | } 17 | 18 | /** 19 | * 默认序列化器 20 | */ 21 | private static final Serializer DEFAULT_SERIALIZER = new JdkSerializer(); 22 | 23 | /** 24 | * 获取实例 25 | * 26 | * @param key 27 | * @return 28 | */ 29 | public static Serializer getInstance(String key) { 30 | return SpiLoader.getInstance(Serializer.class, key); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/serializer/SerializerKeys.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.serializer; 2 | 3 | /** 4 | * 序列化器键名常量 5 | * 6 | * @author 程序员鱼皮 7 | * @learn 编程宝典 8 | * @from 编程导航知识星球 9 | */ 10 | public interface SerializerKeys { 11 | 12 | String JDK = "jdk"; 13 | String JSON = "json"; 14 | String KRYO = "kryo"; 15 | String HESSIAN = "hessian"; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/server/HttpServer.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.server; 2 | 3 | /** 4 | * HTTP 服务器接口 5 | * 6 | * @author 程序员鱼皮 7 | * @learn 编程宝典 8 | * @from 编程导航知识星球 9 | */ 10 | public interface HttpServer { 11 | 12 | /** 13 | * 启动服务器 14 | * 15 | * @param port 16 | */ 17 | void doStart(int port); 18 | } 19 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/server/HttpServerHandler.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.server; 2 | 3 | import com.yupi.yurpc.RpcApplication; 4 | import com.yupi.yurpc.model.RpcRequest; 5 | import com.yupi.yurpc.model.RpcResponse; 6 | import com.yupi.yurpc.registry.LocalRegistry; 7 | import com.yupi.yurpc.serializer.Serializer; 8 | import com.yupi.yurpc.serializer.SerializerFactory; 9 | import io.vertx.core.Handler; 10 | import io.vertx.core.buffer.Buffer; 11 | import io.vertx.core.http.HttpServerRequest; 12 | import io.vertx.core.http.HttpServerResponse; 13 | 14 | import java.io.IOException; 15 | import java.lang.reflect.Method; 16 | 17 | /** 18 | * HTTP 请求处理器 19 | * 20 | * @author 程序员鱼皮 21 | * @learn 编程宝典 22 | * @from 编程导航知识星球 23 | */ 24 | public class HttpServerHandler implements Handler { 25 | 26 | @Override 27 | public void handle(HttpServerRequest request) { 28 | // 指定序列化器 29 | final Serializer serializer = SerializerFactory.getInstance(RpcApplication.getRpcConfig().getSerializer()); 30 | 31 | // 记录日志 32 | System.out.println("Received request: " + request.method() + " " + request.uri()); 33 | 34 | // 异步处理 HTTP 请求 35 | Serializer finalSerializer = serializer; 36 | request.bodyHandler(body -> { 37 | byte[] bytes = body.getBytes(); 38 | RpcRequest rpcRequest = null; 39 | try { 40 | rpcRequest = finalSerializer.deserialize(bytes, RpcRequest.class); 41 | } catch (Exception e) { 42 | e.printStackTrace(); 43 | } 44 | 45 | // 构造响应结果对象 46 | RpcResponse rpcResponse = new RpcResponse(); 47 | // 如果请求为 null,直接返回 48 | if (rpcRequest == null) { 49 | rpcResponse.setMessage("rpcRequest is null"); 50 | doResponse(request, rpcResponse, finalSerializer); 51 | return; 52 | } 53 | 54 | try { 55 | // 获取要调用的服务实现类,通过反射调用 56 | Class implClass = LocalRegistry.get(rpcRequest.getServiceName()); 57 | Method method = implClass.getMethod(rpcRequest.getMethodName(), rpcRequest.getParameterTypes()); 58 | Object result = method.invoke(implClass.newInstance(), rpcRequest.getArgs()); 59 | // 封装返回结果 60 | rpcResponse.setData(result); 61 | rpcResponse.setDataType(method.getReturnType()); 62 | rpcResponse.setMessage("ok"); 63 | } catch (Exception e) { 64 | e.printStackTrace(); 65 | rpcResponse.setMessage(e.getMessage()); 66 | rpcResponse.setException(e); 67 | } 68 | // 响应 69 | doResponse(request, rpcResponse, finalSerializer); 70 | }); 71 | } 72 | 73 | /** 74 | * 响应 75 | * 76 | * @param request 77 | * @param rpcResponse 78 | * @param serializer 79 | */ 80 | void doResponse(HttpServerRequest request, RpcResponse rpcResponse, Serializer serializer) { 81 | HttpServerResponse httpServerResponse = request.response() 82 | .putHeader("content-type", "application/json"); 83 | try { 84 | // 序列化 85 | byte[] serialized = serializer.serialize(rpcResponse); 86 | httpServerResponse.end(Buffer.buffer(serialized)); 87 | } catch (IOException e) { 88 | e.printStackTrace(); 89 | httpServerResponse.end(Buffer.buffer()); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/server/VertxHttpServer.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.server; 2 | 3 | import io.vertx.core.Vertx; 4 | 5 | /** 6 | * Vertx HTTP 服务器 7 | * 8 | * @author 程序员鱼皮 9 | * @learn 编程宝典 10 | * @from 编程导航知识星球 11 | */ 12 | public class VertxHttpServer implements HttpServer { 13 | 14 | /** 15 | * 启动服务器 16 | * 17 | * @param port 18 | */ 19 | public void doStart(int port) { 20 | // 创建 Vert.x 实例 21 | Vertx vertx = Vertx.vertx(); 22 | 23 | // 创建 HTTP 服务器 24 | io.vertx.core.http.HttpServer server = vertx.createHttpServer(); 25 | 26 | // 处理请求 27 | server.requestHandler(new HttpServerHandler()); 28 | 29 | // 启动 HTTP 服务器并监听指定端口 30 | server.listen(port, result -> { 31 | if (result.succeeded()) { 32 | System.out.println("Server is now listening on port " + port); 33 | } else { 34 | System.err.println("Failed to start server: " + result.cause()); 35 | } 36 | }); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/server/tcp/TcpBufferHandlerWrapper.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.server.tcp; 2 | 3 | import com.yupi.yurpc.protocol.ProtocolConstant; 4 | import io.vertx.core.Handler; 5 | import io.vertx.core.buffer.Buffer; 6 | import io.vertx.core.parsetools.RecordParser; 7 | 8 | /** 9 | * TCP 消息处理器包装 10 | * 装饰者模式,使用 recordParser 对原有的 buffer 处理能力进行增强 11 | * 12 | * @author 程序员鱼皮 13 | * @learn 编程宝典 14 | * @from 编程导航知识星球 15 | */ 16 | public class TcpBufferHandlerWrapper implements Handler { 17 | 18 | /** 19 | * 解析器,用于解决半包、粘包问题 20 | */ 21 | private final RecordParser recordParser; 22 | 23 | public TcpBufferHandlerWrapper(Handler bufferHandler) { 24 | recordParser = initRecordParser(bufferHandler); 25 | } 26 | 27 | @Override 28 | public void handle(Buffer buffer) { 29 | recordParser.handle(buffer); 30 | } 31 | 32 | /** 33 | * 初始化解析器 34 | * 35 | * @param bufferHandler 36 | * @return 37 | */ 38 | private RecordParser initRecordParser(Handler bufferHandler) { 39 | // 构造 parser 40 | RecordParser parser = RecordParser.newFixed(ProtocolConstant.MESSAGE_HEADER_LENGTH); 41 | 42 | parser.setOutput(new Handler() { 43 | // 初始化 44 | int size = -1; 45 | // 一次完整的读取(头 + 体) 46 | Buffer resultBuffer = Buffer.buffer(); 47 | 48 | @Override 49 | public void handle(Buffer buffer) { 50 | // 1. 每次循环,首先读取消息头 51 | if (-1 == size) { 52 | // 读取消息体长度 53 | size = buffer.getInt(13); 54 | parser.fixedSizeMode(size); 55 | // 写入头信息到结果 56 | resultBuffer.appendBuffer(buffer); 57 | } else { 58 | // 2. 然后读取消息体 59 | // 写入体信息到结果 60 | resultBuffer.appendBuffer(buffer); 61 | // 已拼接为完整 Buffer,执行处理 62 | bufferHandler.handle(resultBuffer); 63 | // 重置一轮 64 | parser.fixedSizeMode(ProtocolConstant.MESSAGE_HEADER_LENGTH); 65 | size = -1; 66 | resultBuffer = Buffer.buffer(); 67 | } 68 | } 69 | }); 70 | 71 | return parser; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/server/tcp/TcpServerHandler.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.server.tcp; 2 | 3 | import com.yupi.yurpc.model.RpcRequest; 4 | import com.yupi.yurpc.model.RpcResponse; 5 | import com.yupi.yurpc.protocol.*; 6 | import com.yupi.yurpc.registry.LocalRegistry; 7 | import io.vertx.core.Handler; 8 | import io.vertx.core.buffer.Buffer; 9 | import io.vertx.core.net.NetSocket; 10 | 11 | import java.io.IOException; 12 | import java.lang.reflect.Method; 13 | 14 | /** 15 | * TCP 请求处理器 16 | * 17 | * @author 程序员鱼皮 18 | * @learn 编程宝典 19 | * @from 编程导航知识星球 20 | */ 21 | public class TcpServerHandler implements Handler { 22 | 23 | /** 24 | * 处理请求 25 | * 26 | * @param socket the event to handle 27 | */ 28 | @Override 29 | public void handle(NetSocket socket) { 30 | TcpBufferHandlerWrapper bufferHandlerWrapper = new TcpBufferHandlerWrapper(buffer -> { 31 | // 接受请求,解码 32 | ProtocolMessage protocolMessage; 33 | try { 34 | protocolMessage = (ProtocolMessage) ProtocolMessageDecoder.decode(buffer); 35 | } catch (IOException e) { 36 | throw new RuntimeException("协议消息解码错误"); 37 | } 38 | RpcRequest rpcRequest = protocolMessage.getBody(); 39 | ProtocolMessage.Header header = protocolMessage.getHeader(); 40 | 41 | // 处理请求 42 | // 构造响应结果对象 43 | RpcResponse rpcResponse = new RpcResponse(); 44 | try { 45 | // 获取要调用的服务实现类,通过反射调用 46 | Class implClass = LocalRegistry.get(rpcRequest.getServiceName()); 47 | Method method = implClass.getMethod(rpcRequest.getMethodName(), rpcRequest.getParameterTypes()); 48 | Object result = method.invoke(implClass.newInstance(), rpcRequest.getArgs()); 49 | // 封装返回结果 50 | rpcResponse.setData(result); 51 | rpcResponse.setDataType(method.getReturnType()); 52 | rpcResponse.setMessage("ok"); 53 | } catch (Exception e) { 54 | e.printStackTrace(); 55 | rpcResponse.setMessage(e.getMessage()); 56 | rpcResponse.setException(e); 57 | } 58 | 59 | // 发送响应,编码 60 | header.setType((byte) ProtocolMessageTypeEnum.RESPONSE.getKey()); 61 | header.setStatus((byte) ProtocolMessageStatusEnum.OK.getValue()); 62 | ProtocolMessage responseProtocolMessage = new ProtocolMessage<>(header, rpcResponse); 63 | try { 64 | Buffer encode = ProtocolMessageEncoder.encode(responseProtocolMessage); 65 | socket.write(encode); 66 | } catch (IOException e) { 67 | throw new RuntimeException("协议消息编码错误"); 68 | } 69 | }); 70 | socket.handler(bufferHandlerWrapper); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/server/tcp/VertxTcpClient.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.server.tcp; 2 | 3 | import cn.hutool.core.util.IdUtil; 4 | import com.yupi.yurpc.RpcApplication; 5 | import com.yupi.yurpc.model.RpcRequest; 6 | import com.yupi.yurpc.model.RpcResponse; 7 | import com.yupi.yurpc.model.ServiceMetaInfo; 8 | import com.yupi.yurpc.protocol.*; 9 | import io.vertx.core.Vertx; 10 | import io.vertx.core.buffer.Buffer; 11 | import io.vertx.core.net.NetClient; 12 | import io.vertx.core.net.NetSocket; 13 | 14 | import java.io.IOException; 15 | import java.util.concurrent.CompletableFuture; 16 | import java.util.concurrent.ExecutionException; 17 | 18 | /** 19 | * Vertx TCP 请求客户端 20 | * 21 | * @author 程序员鱼皮 22 | * @learn 程序员鱼皮的编程宝典 23 | * @from 编程导航知识星球 24 | */ 25 | public class VertxTcpClient { 26 | 27 | /** 28 | * 发送请求 29 | * 30 | * @param rpcRequest 31 | * @param serviceMetaInfo 32 | * @return 33 | * @throws InterruptedException 34 | * @throws ExecutionException 35 | */ 36 | public static RpcResponse doRequest(RpcRequest rpcRequest, ServiceMetaInfo serviceMetaInfo) throws InterruptedException, ExecutionException { 37 | // 发送 TCP 请求 38 | Vertx vertx = Vertx.vertx(); 39 | NetClient netClient = vertx.createNetClient(); 40 | CompletableFuture responseFuture = new CompletableFuture<>(); 41 | netClient.connect(serviceMetaInfo.getServicePort(), serviceMetaInfo.getServiceHost(), 42 | result -> { 43 | if (!result.succeeded()) { 44 | System.err.println("Failed to connect to TCP server"); 45 | return; 46 | } 47 | NetSocket socket = result.result(); 48 | // 发送数据 49 | // 构造消息 50 | ProtocolMessage protocolMessage = new ProtocolMessage<>(); 51 | ProtocolMessage.Header header = new ProtocolMessage.Header(); 52 | header.setMagic(ProtocolConstant.PROTOCOL_MAGIC); 53 | header.setVersion(ProtocolConstant.PROTOCOL_VERSION); 54 | header.setSerializer((byte) ProtocolMessageSerializerEnum.getEnumByValue(RpcApplication.getRpcConfig().getSerializer()).getKey()); 55 | header.setType((byte) ProtocolMessageTypeEnum.REQUEST.getKey()); 56 | // 生成全局请求 ID 57 | header.setRequestId(IdUtil.getSnowflakeNextId()); 58 | protocolMessage.setHeader(header); 59 | protocolMessage.setBody(rpcRequest); 60 | 61 | // 编码请求 62 | try { 63 | Buffer encodeBuffer = ProtocolMessageEncoder.encode(protocolMessage); 64 | socket.write(encodeBuffer); 65 | } catch (IOException e) { 66 | throw new RuntimeException("协议消息编码错误"); 67 | } 68 | 69 | // 接收响应 70 | TcpBufferHandlerWrapper bufferHandlerWrapper = new TcpBufferHandlerWrapper( 71 | buffer -> { 72 | try { 73 | ProtocolMessage rpcResponseProtocolMessage = 74 | (ProtocolMessage) ProtocolMessageDecoder.decode(buffer); 75 | responseFuture.complete(rpcResponseProtocolMessage.getBody()); 76 | } catch (IOException e) { 77 | throw new RuntimeException("协议消息解码错误"); 78 | } 79 | } 80 | ); 81 | socket.handler(bufferHandlerWrapper); 82 | 83 | }); 84 | 85 | RpcResponse rpcResponse = responseFuture.get(); 86 | // 记得关闭连接 87 | netClient.close(); 88 | return rpcResponse; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/server/tcp/VertxTcpServer.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.server.tcp; 2 | 3 | import io.vertx.core.Vertx; 4 | import io.vertx.core.net.NetServer; 5 | import lombok.extern.slf4j.Slf4j; 6 | 7 | /** 8 | * Vertx TCP 服务器 9 | * 10 | * @author 程序员鱼皮 11 | * @learn 程序员鱼皮的编程宝典 12 | * @from 编程导航知识星球 13 | */ 14 | @Slf4j 15 | public class VertxTcpServer { 16 | 17 | public void doStart(int port) { 18 | // 创建 Vert.x 实例 19 | Vertx vertx = Vertx.vertx(); 20 | 21 | // 创建 TCP 服务器 22 | NetServer server = vertx.createNetServer(); 23 | 24 | // 处理请求 25 | server.connectHandler(new TcpServerHandler()); 26 | 27 | // 启动 TCP 服务器并监听指定端口 28 | server.listen(port, result -> { 29 | if (result.succeeded()) { 30 | log.info("TCP server started on port " + port); 31 | } else { 32 | log.info("Failed to start TCP server: " + result.cause()); 33 | } 34 | }); 35 | } 36 | 37 | public static void main(String[] args) { 38 | new VertxTcpServer().doStart(8888); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/spi/SpiLoader.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.spi; 2 | 3 | import cn.hutool.core.io.resource.ResourceUtil; 4 | import com.yupi.yurpc.serializer.Serializer; 5 | import lombok.extern.slf4j.Slf4j; 6 | 7 | import java.io.BufferedReader; 8 | import java.io.IOException; 9 | import java.io.InputStreamReader; 10 | import java.net.URL; 11 | import java.util.Arrays; 12 | import java.util.HashMap; 13 | import java.util.List; 14 | import java.util.Map; 15 | import java.util.concurrent.ConcurrentHashMap; 16 | 17 | /** 18 | * SPI 加载器 19 | * 自定义实现,支持键值对映射 20 | * 21 | * @author 程序员鱼皮 22 | * @learn 程序员鱼皮的编程宝典 23 | * @from 编程导航知识星球 24 | */ 25 | @Slf4j 26 | public class SpiLoader { 27 | 28 | /** 29 | * 存储已加载的类:接口名 =>(key => 实现类) 30 | */ 31 | private static final Map>> loaderMap = new ConcurrentHashMap<>(); 32 | 33 | /** 34 | * 对象实例缓存(避免重复 new),类路径 => 对象实例,单例模式 35 | */ 36 | private static final Map instanceCache = new ConcurrentHashMap<>(); 37 | 38 | /** 39 | * 系统 SPI 目录 40 | */ 41 | private static final String RPC_SYSTEM_SPI_DIR = "META-INF/rpc/system/"; 42 | 43 | /** 44 | * 用户自定义 SPI 目录 45 | */ 46 | private static final String RPC_CUSTOM_SPI_DIR = "META-INF/rpc/custom/"; 47 | 48 | /** 49 | * 扫描路径 50 | */ 51 | private static final String[] SCAN_DIRS = new String[]{RPC_SYSTEM_SPI_DIR, RPC_CUSTOM_SPI_DIR}; 52 | 53 | /** 54 | * 动态加载的类列表 55 | */ 56 | private static final List> LOAD_CLASS_LIST = Arrays.asList(Serializer.class); 57 | 58 | /** 59 | * 加载所有类型 60 | */ 61 | public static void loadAll() { 62 | log.info("加载所有 SPI"); 63 | for (Class aClass : LOAD_CLASS_LIST) { 64 | load(aClass); 65 | } 66 | } 67 | 68 | /** 69 | * 获取某个接口的实例 70 | * 71 | * @param tClass 72 | * @param key 73 | * @param 74 | * @return 75 | */ 76 | public static T getInstance(Class tClass, String key) { 77 | String tClassName = tClass.getName(); 78 | Map> keyClassMap = loaderMap.get(tClassName); 79 | if (keyClassMap == null) { 80 | throw new RuntimeException(String.format("SpiLoader 未加载 %s 类型", tClassName)); 81 | } 82 | if (!keyClassMap.containsKey(key)) { 83 | throw new RuntimeException(String.format("SpiLoader 的 %s 不存在 key=%s 的类型", tClassName, key)); 84 | } 85 | // 获取到要加载的实现类型 86 | Class implClass = keyClassMap.get(key); 87 | // 从实例缓存中加载指定类型的实例 88 | String implClassName = implClass.getName(); 89 | if (!instanceCache.containsKey(implClassName)) { 90 | try { 91 | instanceCache.put(implClassName, implClass.newInstance()); 92 | } catch (InstantiationException | IllegalAccessException e) { 93 | String errorMsg = String.format("%s 类实例化失败", implClassName); 94 | throw new RuntimeException(errorMsg, e); 95 | } 96 | } 97 | return (T) instanceCache.get(implClassName); 98 | } 99 | 100 | /** 101 | * 加载某个类型 102 | * 103 | * @param loadClass 104 | * @throws IOException 105 | */ 106 | public static Map> load(Class loadClass) { 107 | log.info("加载类型为 {} 的 SPI", loadClass.getName()); 108 | // 扫描路径,用户自定义的 SPI 优先级高于系统 SPI 109 | Map> keyClassMap = new HashMap<>(); 110 | for (String scanDir : SCAN_DIRS) { 111 | List resources = ResourceUtil.getResources(scanDir + loadClass.getName()); 112 | // 读取每个资源文件 113 | for (URL resource : resources) { 114 | try { 115 | InputStreamReader inputStreamReader = new InputStreamReader(resource.openStream()); 116 | BufferedReader bufferedReader = new BufferedReader(inputStreamReader); 117 | String line; 118 | while ((line = bufferedReader.readLine()) != null) { 119 | String[] strArray = line.split("="); 120 | if (strArray.length > 1) { 121 | String key = strArray[0]; 122 | String className = strArray[1]; 123 | keyClassMap.put(key, Class.forName(className)); 124 | } 125 | } 126 | } catch (Exception e) { 127 | log.error("spi resource load error", e); 128 | } 129 | } 130 | } 131 | loaderMap.put(loadClass.getName(), keyClassMap); 132 | return keyClassMap; 133 | } 134 | 135 | public static void main(String[] args) throws IOException, ClassNotFoundException { 136 | loadAll(); 137 | System.out.println(loaderMap); 138 | Serializer serializer = getInstance(Serializer.class, "e"); 139 | System.out.println(serializer); 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/java/com/yupi/yurpc/utils/ConfigUtils.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.utils; 2 | 3 | import cn.hutool.core.util.StrUtil; 4 | import cn.hutool.setting.dialect.Props; 5 | 6 | /** 7 | * 配置工具类 8 | * 9 | * @author 程序员鱼皮 10 | * @learn 程序员鱼皮的编程宝典 11 | * @from 编程导航知识星球 12 | */ 13 | public class ConfigUtils { 14 | 15 | /** 16 | * 加载配置对象 17 | * 18 | * @param tClass 19 | * @param prefix 20 | * @param 21 | * @return 22 | */ 23 | public static T loadConfig(Class tClass, String prefix) { 24 | return loadConfig(tClass, prefix, ""); 25 | } 26 | 27 | /** 28 | * 加载配置对象,支持区分环境 29 | * 30 | * @param tClass 31 | * @param prefix 32 | * @param environment 33 | * @param 34 | * @return 35 | */ 36 | public static T loadConfig(Class tClass, String prefix, String environment) { 37 | StringBuilder configFileBuilder = new StringBuilder("application"); 38 | if (StrUtil.isNotBlank(environment)) { 39 | configFileBuilder.append("-").append(environment); 40 | } 41 | configFileBuilder.append(".properties"); 42 | Props props = new Props(configFileBuilder.toString()); 43 | return props.toBean(tClass, prefix); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /yu-rpc-core/src/main/resources/META-INF/rpc/custom/com.yupi.yurpc.serializer.Serializer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liyupi/yu-rpc/d6865886a56999859b106847fd8edb0d284b0faa/yu-rpc-core/src/main/resources/META-INF/rpc/custom/com.yupi.yurpc.serializer.Serializer -------------------------------------------------------------------------------- /yu-rpc-core/src/main/resources/META-INF/rpc/system/com.yupi.yurpc.fault.retry.RetryStrategy: -------------------------------------------------------------------------------- 1 | no=com.yupi.yurpc.fault.retry.NoRetryStrategy 2 | fixedInterval=com.yupi.yurpc.fault.retry.FixedIntervalRetryStrategy -------------------------------------------------------------------------------- /yu-rpc-core/src/main/resources/META-INF/rpc/system/com.yupi.yurpc.fault.tolerant.TolerantStrategy: -------------------------------------------------------------------------------- 1 | failBack=com.yupi.yurpc.fault.tolerant.FailBackTolerantStrategy 2 | failFast=com.yupi.yurpc.fault.tolerant.FailFastTolerantStrategy 3 | failOver=com.yupi.yurpc.fault.tolerant.FailOverTolerantStrategy 4 | failSafe=com.yupi.yurpc.fault.tolerant.FailSafeTolerantStrategy -------------------------------------------------------------------------------- /yu-rpc-core/src/main/resources/META-INF/rpc/system/com.yupi.yurpc.loadbalancer.LoadBalancer: -------------------------------------------------------------------------------- 1 | roundRobin=com.yupi.yurpc.loadbalancer.RoundRobinLoadBalancer 2 | random=com.yupi.yurpc.loadbalancer.RandomLoadBalancer 3 | consistentHash=com.yupi.yurpc.loadbalancer.ConsistentHashLoadBalancer -------------------------------------------------------------------------------- /yu-rpc-core/src/main/resources/META-INF/rpc/system/com.yupi.yurpc.registry.Registry: -------------------------------------------------------------------------------- 1 | etcd=com.yupi.yurpc.registry.EtcdRegistry 2 | zookeeper=com.yupi.yurpc.registry.ZooKeeperRegistry -------------------------------------------------------------------------------- /yu-rpc-core/src/main/resources/META-INF/rpc/system/com.yupi.yurpc.serializer.Serializer: -------------------------------------------------------------------------------- 1 | jdk=com.yupi.yurpc.serializer.JdkSerializer 2 | hessian=com.yupi.yurpc.serializer.HessianSerializer 3 | json=com.yupi.yurpc.serializer.JsonSerializer 4 | kryo=com.yupi.yurpc.serializer.KryoSerializer -------------------------------------------------------------------------------- /yu-rpc-core/src/main/resources/META-INF/services/com.yupi.yurpc.serializer.Serializer: -------------------------------------------------------------------------------- 1 | com.yupi.yurpc.serializer.JdkSerializer -------------------------------------------------------------------------------- /yu-rpc-core/src/test/java/com/yupi/yurpc/fault/retry/RetryStrategyTest.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.fault.retry; 2 | 3 | import com.yupi.yurpc.model.RpcResponse; 4 | import org.junit.Test; 5 | 6 | /** 7 | * 重试策略测试 8 | * 9 | * @author 程序员鱼皮 10 | * @learn 程序员鱼皮的编程宝典 11 | * @from 编程导航知识星球 12 | */ 13 | public class RetryStrategyTest { 14 | 15 | RetryStrategy retryStrategy = new NoRetryStrategy(); 16 | 17 | @Test 18 | public void doRetry() { 19 | try { 20 | RpcResponse rpcResponse = retryStrategy.doRetry(() -> { 21 | System.out.println("测试重试"); 22 | throw new RuntimeException("模拟重试失败"); 23 | }); 24 | System.out.println(rpcResponse); 25 | } catch (Exception e) { 26 | System.out.println("重试多次失败"); 27 | e.printStackTrace(); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /yu-rpc-core/src/test/java/com/yupi/yurpc/loadbalancer/LoadBalancerTest.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.loadbalancer; 2 | 3 | import com.yupi.yurpc.model.ServiceMetaInfo; 4 | import org.junit.Assert; 5 | import org.junit.Test; 6 | 7 | import java.util.Arrays; 8 | import java.util.HashMap; 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | /** 13 | * 负载均衡器测试 14 | * 15 | * @author 程序员鱼皮 16 | * @learn 程序员鱼皮的编程宝典 17 | * @from 编程导航知识星球 18 | */ 19 | public class LoadBalancerTest { 20 | 21 | final LoadBalancer loadBalancer = new ConsistentHashLoadBalancer(); 22 | 23 | @Test 24 | public void select() { 25 | // 请求参数 26 | Map requestParams = new HashMap<>(); 27 | requestParams.put("methodName", "apple"); 28 | // 服务列表 29 | ServiceMetaInfo serviceMetaInfo1 = new ServiceMetaInfo(); 30 | serviceMetaInfo1.setServiceName("myService"); 31 | serviceMetaInfo1.setServiceVersion("1.0"); 32 | serviceMetaInfo1.setServiceHost("localhost"); 33 | serviceMetaInfo1.setServicePort(1234); 34 | ServiceMetaInfo serviceMetaInfo2 = new ServiceMetaInfo(); 35 | serviceMetaInfo2.setServiceName("myService"); 36 | serviceMetaInfo2.setServiceVersion("1.0"); 37 | serviceMetaInfo2.setServiceHost("yupi.icu"); 38 | serviceMetaInfo2.setServicePort(80); 39 | List serviceMetaInfoList = Arrays.asList(serviceMetaInfo1, serviceMetaInfo2); 40 | // 连续调用 3 次 41 | ServiceMetaInfo serviceMetaInfo = loadBalancer.select(requestParams, serviceMetaInfoList); 42 | System.out.println(serviceMetaInfo); 43 | Assert.assertNotNull(serviceMetaInfo); 44 | serviceMetaInfo = loadBalancer.select(requestParams, serviceMetaInfoList); 45 | System.out.println(serviceMetaInfo); 46 | Assert.assertNotNull(serviceMetaInfo); 47 | serviceMetaInfo = loadBalancer.select(requestParams, serviceMetaInfoList); 48 | System.out.println(serviceMetaInfo); 49 | Assert.assertNotNull(serviceMetaInfo); 50 | } 51 | } -------------------------------------------------------------------------------- /yu-rpc-core/src/test/java/com/yupi/yurpc/protocol/ProtocolMessageTest.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.protocol; 2 | 3 | import cn.hutool.core.util.IdUtil; 4 | import com.yupi.yurpc.constant.RpcConstant; 5 | import com.yupi.yurpc.model.RpcRequest; 6 | import io.vertx.core.buffer.Buffer; 7 | import org.junit.Assert; 8 | import org.junit.Test; 9 | 10 | import java.io.IOException; 11 | 12 | /** 13 | * 自定义协议消息测试 14 | * 15 | * @author 程序员鱼皮 16 | * @learn 程序员鱼皮的编程宝典 17 | * @from 编程导航知识星球 18 | */ 19 | public class ProtocolMessageTest { 20 | 21 | @Test 22 | public void testEncodeAndDecode() throws IOException { 23 | // 构造消息 24 | ProtocolMessage protocolMessage = new ProtocolMessage<>(); 25 | ProtocolMessage.Header header = new ProtocolMessage.Header(); 26 | header.setMagic(ProtocolConstant.PROTOCOL_MAGIC); 27 | header.setVersion(ProtocolConstant.PROTOCOL_VERSION); 28 | header.setSerializer((byte) ProtocolMessageSerializerEnum.JDK.getKey()); 29 | header.setType((byte) ProtocolMessageTypeEnum.REQUEST.getKey()); 30 | header.setStatus((byte) ProtocolMessageStatusEnum.OK.getValue()); 31 | header.setRequestId(IdUtil.getSnowflakeNextId()); 32 | header.setBodyLength(0); 33 | RpcRequest rpcRequest = new RpcRequest(); 34 | rpcRequest.setServiceName("myService"); 35 | rpcRequest.setMethodName("myMethod"); 36 | rpcRequest.setServiceVersion(RpcConstant.DEFAULT_SERVICE_VERSION); 37 | rpcRequest.setParameterTypes(new Class[]{String.class}); 38 | rpcRequest.setArgs(new Object[]{"aaa", "bbb"}); 39 | protocolMessage.setHeader(header); 40 | protocolMessage.setBody(rpcRequest); 41 | 42 | Buffer encodeBuffer = ProtocolMessageEncoder.encode(protocolMessage); 43 | ProtocolMessage message = ProtocolMessageDecoder.decode(encodeBuffer); 44 | Assert.assertNotNull(message); 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /yu-rpc-core/src/test/java/com/yupi/yurpc/registry/RegistryTest.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.registry; 2 | 3 | import com.yupi.yurpc.config.RegistryConfig; 4 | import com.yupi.yurpc.model.ServiceMetaInfo; 5 | import org.junit.Assert; 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * 注册中心测试 13 | * 14 | * @author 程序员鱼皮 15 | * @learn 程序员鱼皮的编程宝典 16 | * @from 编程导航知识星球 17 | */ 18 | public class RegistryTest { 19 | 20 | final Registry registry = new EtcdRegistry(); 21 | 22 | @Before 23 | public void init() { 24 | RegistryConfig registryConfig = new RegistryConfig(); 25 | registryConfig.setAddress("http://localhost:2379"); 26 | registry.init(registryConfig); 27 | } 28 | 29 | @Test 30 | public void register() throws Exception { 31 | ServiceMetaInfo serviceMetaInfo = new ServiceMetaInfo(); 32 | serviceMetaInfo.setServiceName("myService"); 33 | serviceMetaInfo.setServiceVersion("1.0"); 34 | serviceMetaInfo.setServiceHost("localhost"); 35 | serviceMetaInfo.setServicePort(1234); 36 | registry.register(serviceMetaInfo); 37 | serviceMetaInfo = new ServiceMetaInfo(); 38 | serviceMetaInfo.setServiceName("myService"); 39 | serviceMetaInfo.setServiceVersion("1.0"); 40 | serviceMetaInfo.setServiceHost("localhost"); 41 | serviceMetaInfo.setServicePort(1235); 42 | registry.register(serviceMetaInfo); 43 | serviceMetaInfo = new ServiceMetaInfo(); 44 | serviceMetaInfo.setServiceName("myService"); 45 | serviceMetaInfo.setServiceVersion("2.0"); 46 | serviceMetaInfo.setServiceHost("localhost"); 47 | serviceMetaInfo.setServicePort(1234); 48 | registry.register(serviceMetaInfo); 49 | } 50 | 51 | @Test 52 | public void unRegister() { 53 | ServiceMetaInfo serviceMetaInfo = new ServiceMetaInfo(); 54 | serviceMetaInfo.setServiceName("myService"); 55 | serviceMetaInfo.setServiceVersion("1.0"); 56 | serviceMetaInfo.setServiceHost("localhost"); 57 | serviceMetaInfo.setServicePort(1234); 58 | registry.unRegister(serviceMetaInfo); 59 | } 60 | 61 | @Test 62 | public void serviceDiscovery() { 63 | ServiceMetaInfo serviceMetaInfo = new ServiceMetaInfo(); 64 | serviceMetaInfo.setServiceName("myService"); 65 | serviceMetaInfo.setServiceVersion("1.0"); 66 | String serviceKey = serviceMetaInfo.getServiceKey(); 67 | List serviceMetaInfoList = registry.serviceDiscovery(serviceKey); 68 | Assert.assertNotNull(serviceMetaInfoList); 69 | } 70 | 71 | @Test 72 | public void heartBeat() throws Exception { 73 | // init 方法中已经执行心跳检测了 74 | register(); 75 | // 阻塞 1 分钟 76 | Thread.sleep(60 * 1000L); 77 | } 78 | } -------------------------------------------------------------------------------- /yu-rpc-easy/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | !**/src/main/**/target/ 4 | !**/src/test/**/target/ 5 | 6 | ### IntelliJ IDEA ### 7 | .idea/modules.xml 8 | .idea/jarRepositories.xml 9 | .idea/compiler.xml 10 | .idea/libraries/ 11 | *.iws 12 | *.iml 13 | *.ipr 14 | 15 | ### Eclipse ### 16 | .apt_generated 17 | .classpath 18 | .factorypath 19 | .project 20 | .settings 21 | .springBeans 22 | .sts4-cache 23 | 24 | ### NetBeans ### 25 | /nbproject/private/ 26 | /nbbuild/ 27 | /dist/ 28 | /nbdist/ 29 | /.nb-gradle/ 30 | build/ 31 | !**/src/main/**/build/ 32 | !**/src/test/**/build/ 33 | 34 | ### VS Code ### 35 | .vscode/ 36 | 37 | ### Mac OS ### 38 | .DS_Store -------------------------------------------------------------------------------- /yu-rpc-easy/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.yupi 8 | yu-rpc-easy 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | 14 | io.vertx 15 | vertx-core 16 | 4.5.1 17 | 18 | 19 | 20 | cn.hutool 21 | hutool-all 22 | 5.8.16 23 | 24 | 25 | 26 | org.projectlombok 27 | lombok 28 | 1.18.30 29 | provided 30 | 31 | 32 | 33 | 34 | 35 | 36 | org.apache.maven.plugins 37 | maven-compiler-plugin 38 | 39 | 8 40 | 8 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /yu-rpc-easy/src/main/java/com/yupi/yurpc/model/RpcRequest.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.io.Serializable; 9 | 10 | /** 11 | * RPC 请求 12 | * 13 | * @author 程序员鱼皮 14 | * @learn 编程宝典 15 | * @from 编程导航知识星球 16 | */ 17 | @Data 18 | @Builder 19 | @AllArgsConstructor 20 | @NoArgsConstructor 21 | public class RpcRequest implements Serializable { 22 | 23 | /** 24 | * 服务名称 25 | */ 26 | private String serviceName; 27 | 28 | /** 29 | * 方法名称 30 | */ 31 | private String methodName; 32 | 33 | /** 34 | * 参数类型列表 35 | */ 36 | private Class[] parameterTypes; 37 | 38 | /** 39 | * 参数列表 40 | */ 41 | private Object[] args; 42 | 43 | } 44 | -------------------------------------------------------------------------------- /yu-rpc-easy/src/main/java/com/yupi/yurpc/model/RpcResponse.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.io.Serializable; 9 | 10 | /** 11 | * RPC 响应 12 | * 13 | * @author 程序员鱼皮 14 | * @learn 编程宝典 15 | * @from 编程导航知识星球 16 | */ 17 | @Data 18 | @Builder 19 | @AllArgsConstructor 20 | @NoArgsConstructor 21 | public class RpcResponse implements Serializable { 22 | 23 | /** 24 | * 响应数据 25 | */ 26 | private Object data; 27 | 28 | /** 29 | * 响应数据类型(预留) 30 | */ 31 | private Class dataType; 32 | 33 | /** 34 | * 响应信息 35 | */ 36 | private String message; 37 | 38 | /** 39 | * 异常信息 40 | */ 41 | private Exception exception; 42 | 43 | } 44 | -------------------------------------------------------------------------------- /yu-rpc-easy/src/main/java/com/yupi/yurpc/proxy/ServiceProxy.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.proxy; 2 | 3 | import cn.hutool.http.HttpRequest; 4 | import cn.hutool.http.HttpResponse; 5 | import com.yupi.yurpc.model.RpcRequest; 6 | import com.yupi.yurpc.model.RpcResponse; 7 | import com.yupi.yurpc.serializer.JdkSerializer; 8 | import com.yupi.yurpc.serializer.Serializer; 9 | 10 | import java.io.IOException; 11 | import java.lang.reflect.InvocationHandler; 12 | import java.lang.reflect.Method; 13 | 14 | /** 15 | * 服务代理(JDK 动态代理) 16 | * 17 | * @author 程序员鱼皮 18 | * @learn 编程宝典 19 | * @from 编程导航知识星球 20 | */ 21 | public class ServiceProxy implements InvocationHandler { 22 | 23 | /** 24 | * 调用代理 25 | * 26 | * @return 27 | * @throws Throwable 28 | */ 29 | @Override 30 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 31 | // 指定序列化器 32 | Serializer serializer = new JdkSerializer(); 33 | 34 | // 构造请求 35 | RpcRequest rpcRequest = RpcRequest.builder() 36 | .serviceName(method.getDeclaringClass().getName()) 37 | .methodName(method.getName()) 38 | .parameterTypes(method.getParameterTypes()) 39 | .args(args) 40 | .build(); 41 | try { 42 | // 序列化 43 | byte[] bodyBytes = serializer.serialize(rpcRequest); 44 | // 发送请求 45 | // todo 注意,这里地址被硬编码了(需要使用注册中心和服务发现机制解决) 46 | try (HttpResponse httpResponse = HttpRequest.post("http://localhost:8080") 47 | .body(bodyBytes) 48 | .execute()) { 49 | byte[] result = httpResponse.bodyBytes(); 50 | // 反序列化 51 | RpcResponse rpcResponse = serializer.deserialize(result, RpcResponse.class); 52 | return rpcResponse.getData(); 53 | } 54 | } catch (IOException e) { 55 | e.printStackTrace(); 56 | } 57 | 58 | return null; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /yu-rpc-easy/src/main/java/com/yupi/yurpc/proxy/ServiceProxyFactory.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.proxy; 2 | 3 | import java.lang.reflect.Proxy; 4 | 5 | /** 6 | * 服务代理工厂(用于创建代理对象) 7 | * 8 | * @author 程序员鱼皮 9 | * @learn 编程宝典 10 | * @from 编程导航知识星球 11 | */ 12 | public class ServiceProxyFactory { 13 | 14 | /** 15 | * 根据服务类获取代理对象 16 | * 17 | * @param serviceClass 18 | * @param 19 | * @return 20 | */ 21 | public static T getProxy(Class serviceClass) { 22 | return (T) Proxy.newProxyInstance( 23 | serviceClass.getClassLoader(), 24 | new Class[]{serviceClass}, 25 | new ServiceProxy()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /yu-rpc-easy/src/main/java/com/yupi/yurpc/registry/LocalRegistry.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.registry; 2 | 3 | import java.util.Map; 4 | import java.util.concurrent.ConcurrentHashMap; 5 | 6 | /** 7 | * 本地注册中心 8 | * 9 | * @author 程序员鱼皮 10 | * @learn 编程宝典 11 | * @from 编程导航知识星球 12 | */ 13 | public class LocalRegistry { 14 | 15 | /** 16 | * 注册信息存储 17 | */ 18 | private static final Map> map = new ConcurrentHashMap<>(); 19 | 20 | /** 21 | * 注册服务 22 | * 23 | * @param serviceName 24 | * @param implClass 25 | */ 26 | public static void register(String serviceName, Class implClass) { 27 | map.put(serviceName, implClass); 28 | } 29 | 30 | /** 31 | * 获取服务 32 | * 33 | * @param serviceName 34 | * @return 35 | */ 36 | public static Class get(String serviceName) { 37 | return map.get(serviceName); 38 | } 39 | 40 | /** 41 | * 删除服务 42 | * 43 | * @param serviceName 44 | */ 45 | public static void remove(String serviceName) { 46 | map.remove(serviceName); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /yu-rpc-easy/src/main/java/com/yupi/yurpc/serializer/JdkSerializer.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.serializer; 2 | 3 | import java.io.*; 4 | 5 | /** 6 | * JDK 序列化器 7 | * 8 | * @author 程序员鱼皮 9 | * @learn 编程宝典 10 | * @from 编程导航知识星球 11 | */ 12 | public class JdkSerializer implements Serializer { 13 | 14 | /** 15 | * 序列化 16 | * 17 | * @param object 18 | * @param 19 | * @return 20 | * @throws IOException 21 | */ 22 | @Override 23 | public byte[] serialize(T object) throws IOException { 24 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 25 | ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream); 26 | objectOutputStream.writeObject(object); 27 | objectOutputStream.close(); 28 | return outputStream.toByteArray(); 29 | } 30 | 31 | /** 32 | * 反序列化 33 | * 34 | * @param bytes 35 | * @param type 36 | * @param 37 | * @return 38 | * @throws IOException 39 | */ 40 | @Override 41 | public T deserialize(byte[] bytes, Class type) throws IOException { 42 | ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); 43 | ObjectInputStream objectInputStream = new ObjectInputStream(inputStream); 44 | try { 45 | return (T) objectInputStream.readObject(); 46 | } catch (ClassNotFoundException e) { 47 | throw new RuntimeException(e); 48 | } finally { 49 | objectInputStream.close(); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /yu-rpc-easy/src/main/java/com/yupi/yurpc/serializer/Serializer.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.serializer; 2 | 3 | import java.io.IOException; 4 | 5 | /** 6 | * 序列化器接口 7 | * 8 | * @author 程序员鱼皮 9 | * @learn 编程宝典 10 | * @from 编程导航知识星球 11 | */ 12 | public interface Serializer { 13 | 14 | /** 15 | * 序列化 16 | * 17 | * @param object 18 | * @param 19 | * @return 20 | * @throws IOException 21 | */ 22 | byte[] serialize(T object) throws IOException; 23 | 24 | /** 25 | * 反序列化 26 | * 27 | * @param bytes 28 | * @param type 29 | * @param 30 | * @return 31 | * @throws IOException 32 | */ 33 | T deserialize(byte[] bytes, Class type) throws IOException; 34 | } 35 | -------------------------------------------------------------------------------- /yu-rpc-easy/src/main/java/com/yupi/yurpc/server/HttpServer.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.server; 2 | 3 | /** 4 | * HTTP 服务器接口 5 | * 6 | * @author 程序员鱼皮 7 | * @learn 编程宝典 8 | * @from 编程导航知识星球 9 | */ 10 | public interface HttpServer { 11 | 12 | /** 13 | * 启动服务器 14 | * 15 | * @param port 16 | */ 17 | void doStart(int port); 18 | } 19 | -------------------------------------------------------------------------------- /yu-rpc-easy/src/main/java/com/yupi/yurpc/server/HttpServerHandler.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.server; 2 | 3 | import com.yupi.yurpc.model.RpcRequest; 4 | import com.yupi.yurpc.model.RpcResponse; 5 | import com.yupi.yurpc.registry.LocalRegistry; 6 | import com.yupi.yurpc.serializer.JdkSerializer; 7 | import com.yupi.yurpc.serializer.Serializer; 8 | import io.vertx.core.Handler; 9 | import io.vertx.core.buffer.Buffer; 10 | import io.vertx.core.http.HttpServerRequest; 11 | import io.vertx.core.http.HttpServerResponse; 12 | 13 | import java.io.IOException; 14 | import java.lang.reflect.Method; 15 | 16 | /** 17 | * HTTP 请求处理 18 | * 19 | * @author 程序员鱼皮 20 | * @learn 编程宝典 21 | * @from 编程导航知识星球 22 | */ 23 | public class HttpServerHandler implements Handler { 24 | 25 | @Override 26 | public void handle(HttpServerRequest request) { 27 | // 指定序列化器 28 | final Serializer serializer = new JdkSerializer(); 29 | 30 | // 记录日志 31 | System.out.println("Received request: " + request.method() + " " + request.uri()); 32 | 33 | // 异步处理 HTTP 请求 34 | request.bodyHandler(body -> { 35 | byte[] bytes = body.getBytes(); 36 | RpcRequest rpcRequest = null; 37 | try { 38 | rpcRequest = serializer.deserialize(bytes, RpcRequest.class); 39 | } catch (Exception e) { 40 | e.printStackTrace(); 41 | } 42 | 43 | // 构造响应结果对象 44 | RpcResponse rpcResponse = new RpcResponse(); 45 | // 如果请求为 null,直接返回 46 | if (rpcRequest == null) { 47 | rpcResponse.setMessage("rpcRequest is null"); 48 | doResponse(request, rpcResponse, serializer); 49 | return; 50 | } 51 | 52 | try { 53 | // 获取要调用的服务实现类,通过反射调用 54 | Class implClass = LocalRegistry.get(rpcRequest.getServiceName()); 55 | Method method = implClass.getMethod(rpcRequest.getMethodName(), rpcRequest.getParameterTypes()); 56 | Object result = method.invoke(implClass.newInstance(), rpcRequest.getArgs()); 57 | // 封装返回结果 58 | rpcResponse.setData(result); 59 | rpcResponse.setDataType(method.getReturnType()); 60 | rpcResponse.setMessage("ok"); 61 | } catch (Exception e) { 62 | e.printStackTrace(); 63 | rpcResponse.setMessage(e.getMessage()); 64 | rpcResponse.setException(e); 65 | } 66 | // 响应 67 | doResponse(request, rpcResponse, serializer); 68 | }); 69 | } 70 | 71 | /** 72 | * 响应 73 | * 74 | * @param request 75 | * @param rpcResponse 76 | * @param serializer 77 | */ 78 | void doResponse(HttpServerRequest request, RpcResponse rpcResponse, Serializer serializer) { 79 | HttpServerResponse httpServerResponse = request.response() 80 | .putHeader("content-type", "application/json"); 81 | try { 82 | // 序列化 83 | byte[] serialized = serializer.serialize(rpcResponse); 84 | httpServerResponse.end(Buffer.buffer(serialized)); 85 | } catch (IOException e) { 86 | e.printStackTrace(); 87 | httpServerResponse.end(Buffer.buffer()); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /yu-rpc-easy/src/main/java/com/yupi/yurpc/server/VertxHttpServer.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.server; 2 | 3 | import io.vertx.core.Vertx; 4 | 5 | /** 6 | * Vertx HTTP 服务器 7 | * 8 | * @author 程序员鱼皮 9 | * @learn 编程宝典 10 | * @from 编程导航知识星球 11 | */ 12 | public class VertxHttpServer implements HttpServer { 13 | 14 | /** 15 | * 启动服务器 16 | * 17 | * @param port 18 | */ 19 | public void doStart(int port) { 20 | // 创建 Vert.x 实例 21 | Vertx vertx = Vertx.vertx(); 22 | 23 | // 创建 HTTP 服务器 24 | io.vertx.core.http.HttpServer server = vertx.createHttpServer(); 25 | 26 | // 监听端口并处理请求 27 | server.requestHandler(new HttpServerHandler()); 28 | 29 | // 启动 HTTP 服务器并监听指定端口 30 | server.listen(port, result -> { 31 | if (result.succeeded()) { 32 | System.out.println("Server is now listening on port " + port); 33 | } else { 34 | System.err.println("Failed to start server: " + result.cause()); 35 | } 36 | }); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /yu-rpc-spring-boot-starter/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /yu-rpc-spring-boot-starter/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | com.yupi 6 | yu-rpc-spring-boot-starter 7 | 0.0.1-SNAPSHOT 8 | yu-rpc-spring-boot-starter 9 | yu-rpc-spring-boot-starter 10 | 11 | 1.8 12 | UTF-8 13 | UTF-8 14 | 2.6.13 15 | 16 | 17 | 18 | com.yupi 19 | yu-rpc-core 20 | 1.0-SNAPSHOT 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-configuration-processor 29 | true 30 | 31 | 32 | 33 | org.projectlombok 34 | lombok 35 | provided 36 | 37 | 38 | 39 | org.springframework.boot 40 | spring-boot-starter-test 41 | test 42 | 43 | 44 | 45 | 46 | 47 | org.springframework.boot 48 | spring-boot-dependencies 49 | ${spring-boot.version} 50 | pom 51 | import 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | org.apache.maven.plugins 60 | maven-compiler-plugin 61 | 3.8.1 62 | 63 | 1.8 64 | 1.8 65 | UTF-8 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /yu-rpc-spring-boot-starter/src/main/java/com/yupi/yurpc/springboot/starter/annotation/EnableRpc.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.springboot.starter.annotation; 2 | 3 | import com.yupi.yurpc.springboot.starter.bootstrap.RpcConsumerBootstrap; 4 | import com.yupi.yurpc.springboot.starter.bootstrap.RpcInitBootstrap; 5 | import com.yupi.yurpc.springboot.starter.bootstrap.RpcProviderBootstrap; 6 | import org.springframework.context.annotation.Import; 7 | 8 | import java.lang.annotation.ElementType; 9 | import java.lang.annotation.Retention; 10 | import java.lang.annotation.RetentionPolicy; 11 | import java.lang.annotation.Target; 12 | 13 | /** 14 | * 启用 Rpc 注解 15 | * 16 | * @author 程序员鱼皮 17 | * @learn 程序员鱼皮的编程宝典 18 | * @from 编程导航知识星球 19 | */ 20 | @Target({ElementType.TYPE}) 21 | @Retention(RetentionPolicy.RUNTIME) 22 | @Import({RpcInitBootstrap.class, RpcProviderBootstrap.class, RpcConsumerBootstrap.class}) 23 | public @interface EnableRpc { 24 | 25 | /** 26 | * 需要启动 server 27 | * 28 | * @return 29 | */ 30 | boolean needServer() default true; 31 | } 32 | -------------------------------------------------------------------------------- /yu-rpc-spring-boot-starter/src/main/java/com/yupi/yurpc/springboot/starter/annotation/RpcReference.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.springboot.starter.annotation; 2 | 3 | import com.yupi.yurpc.constant.RpcConstant; 4 | import com.yupi.yurpc.fault.retry.RetryStrategyKeys; 5 | import com.yupi.yurpc.fault.tolerant.TolerantStrategyKeys; 6 | import com.yupi.yurpc.loadbalancer.LoadBalancerKeys; 7 | 8 | import java.lang.annotation.ElementType; 9 | import java.lang.annotation.Retention; 10 | import java.lang.annotation.RetentionPolicy; 11 | import java.lang.annotation.Target; 12 | 13 | /** 14 | * 服务消费者注解(用于注入服务) 15 | * 16 | * @author 程序员鱼皮 17 | * @learn 程序员鱼皮的编程宝典 18 | * @from 编程导航知识星球 19 | */ 20 | @Retention(RetentionPolicy.RUNTIME) 21 | @Target(ElementType.FIELD) 22 | public @interface RpcReference { 23 | 24 | /** 25 | * 服务接口类 26 | */ 27 | Class interfaceClass() default void.class; 28 | 29 | /** 30 | * 版本 31 | */ 32 | String serviceVersion() default RpcConstant.DEFAULT_SERVICE_VERSION; 33 | 34 | /** 35 | * 负载均衡器 36 | */ 37 | String loadBalancer() default LoadBalancerKeys.ROUND_ROBIN; 38 | 39 | /** 40 | * 重试策略 41 | */ 42 | String retryStrategy() default RetryStrategyKeys.NO; 43 | 44 | /** 45 | * 容错策略 46 | */ 47 | String tolerantStrategy() default TolerantStrategyKeys.FAIL_FAST; 48 | 49 | /** 50 | * 模拟调用 51 | */ 52 | boolean mock() default false; 53 | 54 | } 55 | -------------------------------------------------------------------------------- /yu-rpc-spring-boot-starter/src/main/java/com/yupi/yurpc/springboot/starter/annotation/RpcService.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.springboot.starter.annotation; 2 | 3 | import com.yupi.yurpc.constant.RpcConstant; 4 | import org.springframework.stereotype.Component; 5 | 6 | import java.lang.annotation.ElementType; 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.RetentionPolicy; 9 | import java.lang.annotation.Target; 10 | 11 | /** 12 | * 服务提供者注解(用于注册服务) 13 | * 14 | * @author 程序员鱼皮 15 | * @learn 程序员鱼皮的编程宝典 16 | * @from 编程导航知识星球 17 | */ 18 | @Target({ElementType.TYPE}) 19 | @Retention(RetentionPolicy.RUNTIME) 20 | @Component 21 | public @interface RpcService { 22 | 23 | /** 24 | * 服务接口类 25 | */ 26 | Class interfaceClass() default void.class; 27 | 28 | /** 29 | * 版本 30 | */ 31 | String serviceVersion() default RpcConstant.DEFAULT_SERVICE_VERSION; 32 | } 33 | -------------------------------------------------------------------------------- /yu-rpc-spring-boot-starter/src/main/java/com/yupi/yurpc/springboot/starter/bootstrap/RpcConsumerBootstrap.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.springboot.starter.bootstrap; 2 | 3 | import com.yupi.yurpc.proxy.ServiceProxyFactory; 4 | import com.yupi.yurpc.springboot.starter.annotation.RpcReference; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.beans.BeansException; 7 | import org.springframework.beans.factory.config.BeanPostProcessor; 8 | 9 | import java.lang.reflect.Field; 10 | 11 | /** 12 | * Rpc 服务消费者启动 13 | * 14 | * @author 程序员鱼皮 15 | * @learn 程序员鱼皮的编程宝典 16 | * @from 编程导航知识星球 17 | */ 18 | @Slf4j 19 | public class RpcConsumerBootstrap implements BeanPostProcessor { 20 | 21 | /** 22 | * Bean 初始化后执行,注入服务 23 | * 24 | * @param bean 25 | * @param beanName 26 | * @return 27 | * @throws BeansException 28 | */ 29 | @Override 30 | public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { 31 | Class beanClass = bean.getClass(); 32 | // 遍历对象的所有属性 33 | Field[] declaredFields = beanClass.getDeclaredFields(); 34 | for (Field field : declaredFields) { 35 | RpcReference rpcReference = field.getAnnotation(RpcReference.class); 36 | if (rpcReference != null) { 37 | // 为属性生成代理对象 38 | Class interfaceClass = rpcReference.interfaceClass(); 39 | if (interfaceClass == void.class) { 40 | interfaceClass = field.getType(); 41 | } 42 | field.setAccessible(true); 43 | Object proxyObject = ServiceProxyFactory.getProxy(interfaceClass); 44 | try { 45 | field.set(bean, proxyObject); 46 | field.setAccessible(false); 47 | } catch (IllegalAccessException e) { 48 | throw new RuntimeException("为字段注入代理对象失败", e); 49 | } 50 | } 51 | } 52 | return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /yu-rpc-spring-boot-starter/src/main/java/com/yupi/yurpc/springboot/starter/bootstrap/RpcInitBootstrap.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.springboot.starter.bootstrap; 2 | 3 | import com.yupi.yurpc.RpcApplication; 4 | import com.yupi.yurpc.config.RpcConfig; 5 | import com.yupi.yurpc.server.tcp.VertxTcpServer; 6 | import com.yupi.yurpc.springboot.starter.annotation.EnableRpc; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.beans.factory.support.BeanDefinitionRegistry; 9 | import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; 10 | import org.springframework.core.type.AnnotationMetadata; 11 | 12 | /** 13 | * Rpc 框架启动 14 | * 15 | * @author 程序员鱼皮 16 | * @learn 程序员鱼皮的编程宝典 17 | * @from 编程导航知识星球 18 | */ 19 | @Slf4j 20 | public class RpcInitBootstrap implements ImportBeanDefinitionRegistrar { 21 | 22 | /** 23 | * Spring 初始化时执行,初始化 RPC 框架 24 | * 25 | * @param importingClassMetadata 26 | * @param registry 27 | */ 28 | @Override 29 | public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { 30 | // 获取 EnableRpc 注解的属性值 31 | boolean needServer = (boolean) importingClassMetadata.getAnnotationAttributes(EnableRpc.class.getName()) 32 | .get("needServer"); 33 | 34 | // RPC 框架初始化(配置和注册中心) 35 | RpcApplication.init(); 36 | 37 | // 全局配置 38 | final RpcConfig rpcConfig = RpcApplication.getRpcConfig(); 39 | 40 | // 启动服务器 41 | if (needServer) { 42 | VertxTcpServer vertxTcpServer = new VertxTcpServer(); 43 | vertxTcpServer.doStart(rpcConfig.getServerPort()); 44 | } else { 45 | log.info("不启动 server"); 46 | } 47 | 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /yu-rpc-spring-boot-starter/src/main/java/com/yupi/yurpc/springboot/starter/bootstrap/RpcProviderBootstrap.java: -------------------------------------------------------------------------------- 1 | package com.yupi.yurpc.springboot.starter.bootstrap; 2 | 3 | import com.yupi.yurpc.RpcApplication; 4 | import com.yupi.yurpc.config.RegistryConfig; 5 | import com.yupi.yurpc.config.RpcConfig; 6 | import com.yupi.yurpc.model.ServiceMetaInfo; 7 | import com.yupi.yurpc.registry.LocalRegistry; 8 | import com.yupi.yurpc.registry.Registry; 9 | import com.yupi.yurpc.registry.RegistryFactory; 10 | import com.yupi.yurpc.springboot.starter.annotation.RpcService; 11 | import lombok.extern.slf4j.Slf4j; 12 | import org.springframework.beans.BeansException; 13 | import org.springframework.beans.factory.config.BeanPostProcessor; 14 | 15 | /** 16 | * Rpc 服务提供者启动 17 | * 18 | * @author 程序员鱼皮 19 | * @learn 程序员鱼皮的编程宝典 20 | * @from 编程导航知识星球 21 | */ 22 | @Slf4j 23 | public class RpcProviderBootstrap implements BeanPostProcessor { 24 | 25 | /** 26 | * Bean 初始化后执行,注册服务 27 | * 28 | * @param bean 29 | * @param beanName 30 | * @return 31 | * @throws BeansException 32 | */ 33 | @Override 34 | public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { 35 | Class beanClass = bean.getClass(); 36 | RpcService rpcService = beanClass.getAnnotation(RpcService.class); 37 | if (rpcService != null) { 38 | // 需要注册服务 39 | // 1. 获取服务基本信息 40 | Class interfaceClass = rpcService.interfaceClass(); 41 | // 默认值处理 42 | if (interfaceClass == void.class) { 43 | interfaceClass = beanClass.getInterfaces()[0]; 44 | } 45 | String serviceName = interfaceClass.getName(); 46 | String serviceVersion = rpcService.serviceVersion(); 47 | // 2. 注册服务 48 | // 本地注册 49 | LocalRegistry.register(serviceName, beanClass); 50 | 51 | // 全局配置 52 | final RpcConfig rpcConfig = RpcApplication.getRpcConfig(); 53 | // 注册服务到注册中心 54 | RegistryConfig registryConfig = rpcConfig.getRegistryConfig(); 55 | Registry registry = RegistryFactory.getInstance(registryConfig.getRegistry()); 56 | ServiceMetaInfo serviceMetaInfo = new ServiceMetaInfo(); 57 | serviceMetaInfo.setServiceName(serviceName); 58 | serviceMetaInfo.setServiceVersion(serviceVersion); 59 | serviceMetaInfo.setServiceHost(rpcConfig.getServerHost()); 60 | serviceMetaInfo.setServicePort(rpcConfig.getServerPort()); 61 | try { 62 | registry.register(serviceMetaInfo); 63 | } catch (Exception e) { 64 | throw new RuntimeException(serviceName + " 服务注册失败", e); 65 | } 66 | } 67 | 68 | return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName); 69 | } 70 | } 71 | --------------------------------------------------------------------------------