├── .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 | 
33 |
34 |
35 | 详细的保姆级文字教程:
36 |
37 | 
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 extends T> 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 |
--------------------------------------------------------------------------------