From e9268d21418924fc79d01b0783c4fadffddeb50b Mon Sep 17 00:00:00 2001 From: xiaobaozi-web <864508127@qq.com> Date: Fri, 6 Dec 2024 20:29:51 +0800 Subject: [PATCH 1/5] Add files via upload --- README.md | 449 +++++++++++++++++++++++++++--------------------------- 1 file changed, 227 insertions(+), 222 deletions(-) diff --git a/README.md b/README.md index 9b1567a..bec3697 100644 --- a/README.md +++ b/README.md @@ -1,222 +1,227 @@ -# RPC-Java - -[【代码随想录知识星球】](https://www.programmercarl.com/other/kstar.html)项目分享-手撕RPC框架,[项目专栏](https://t.zsxq.com/0CVPn) 有本项目的详细文档 - -* [第一章:项目背景、项目收获、简历写法、项目架构和流程图](https://t.zsxq.com/RtJmQ) -* [第二章(版本一):实现一个基本的rpc调用](https://t.zsxq.com/QImhy) -* [第三章(版本二):netty自定义编码器,解码器和序列化器,创建缓存](https://t.zsxq.com/osLak) -* [第四章(版本三):负载均衡、超时重试 &白名单](https://t.zsxq.com/zVqPE) -* [第五章(版本四):服务-限流、熔断](https://t.zsxq.com/Oj1gG0) -* [第六章:项目常见问题以及如何回答(网络传输层面,注册中心层面,算法层面,各种场景题](https://t.zsxq.com/xOAAq) - -## 启动流程 - -1.先安装并开启zookeeper - -安装参考: - -[【Zookeeper】Windows下安装Zookeeper(图文记录详细步骤,手把手包安装成功)_windows安装zk-CSDN博客](https://blog.csdn.net/tttzzzqqq2018/article/details/132093374?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522172149339116800211548359%2522%252C%2522scm%2522%253A%252220140713.130102334) - -2.运行Server包下的TestServer,再运行Client包下的TestClient - - - -# RPC概念 - -### 概念 - -1. RPC(Remote Procedure Call Protocol) 远程过程调用协议。 -2. RPC是一种通过网络从远程计算机程序上请求服务,不需要了解底层网络技术的协议。 -3. RPC主要作用就是不同的服务间方法调用就像本地调用一样便捷。 - - - -### 常用RPC技术或框架 - -应用级的服务框架:阿里的 Dubbo/Dubbox、Google gRPC、Spring Boot/Spring Cloud。 -远程通信协议:RMI、Socket、SOAP(HTTP XML)、REST(HTTP JSON)。 -通信框架:MINA 和 Netty - - - -### 为什么要有RPC? - -1. 服务化:微服务化,跨平台的服务之间远程调用; -2. 分布式系统架构:分布式服务跨机器进行远程调用; -3. 服务可重用:开发一个公共能力服务,供多个服务远程调用。 -4. 系统间交互调用:两台服务器A、B,服务器A上的应用a需要调用服务器B上的应用b提供的方法,而应用a和应用b不在一个内存空间,不能直接调用,此时,需要通过网络传输来表达需要调用的语义及传输调用的数据。 - -#### 使用场景 - -1. `大型网站`:内部涉及多个子系统,服务、接口较多。 -2. `注册发现机制`:如Nacos、Dubbo等,一般都有注册中心,服务有多个实例,调用方调用的哪个实例无感知。 -3. `安全性`:不暴露资源 -4. `服务化治理`:微服务架构、分布式架构。 - - - -### 架构图 - -![](README.assets/655c04a02b08474e985ff4bf8a561d12.png) - - - -### 核心功能概念 - -核心功能实现主要分为**服务寻址**、**序列化和反序列化**、**网络传输功能**。 - -#### 服务寻址功能 - -**Call ID映射:** - -​ 本地:在本地方法调用中,函数体是直接通过函数指针来指定的,但是在远程调用中,由于两个进程的地址空间完全不一样,函数指针不起作用。 -​ 远程:RPC中所有函数或方法都有自己的一个ID,在所有进程中都唯一。客户端在做远程过程调用时,必须附上这个ID,即客户端会查一下表,找出相应的Call ID,然后传给服务端,服务端也会查表,来确定客户端需要调用的函数,然后执行相应函数的代码。 -​ Call ID映射表一般是一个哈希表。 - - - -#### 序列化和反序列化功能 - -**概述:** - -- 序列化:将消息对象转换为二进制流。 -- 反序列化:将二进制流转换为消息对象。 - - - -**必要性**: -远程调用涉及到数据的传输,在本地调用中,只需要将数据压入栈中,然后让函数去栈中读取即可。 -但远程的数据传输,由于客户端和服务端不在同一个服务器上,涉及不同的进程,不能通过内存传递参数,此时就需要将客户端先将请求参数转成字节流(编码),传递给服务端,服务端再将字节流转为自己可读取格式(解码),这就是序列化和反序列化的过程。反之,服务端返回值也逆向经历序列化和反序列化到客户端。 - -**序列化的优势:** -将消息对象转为二进制字节流,便于网络传输。 -可跨平台、跨语言。如Python编写的客户端请求序列化参数传输到Java编写的服务端进行反序列化。 - -#### 网络传输功能 - -**作用**: - -- 客户端将Call ID和序列化后的参数字节流传输给服务端。 -- 服务端将序列化后的调用结果回传给客户端。 - -**协议**: -  主要有TCP、UDP、HTTP协议。 - -**基于TCP协议** - -​ 客户端和服务端建立Socket连接。 -​ 客户端通过Socket将需要调用的接口名称、方法名称及参数序列化后传递给服务端。 -服务端反序列化后再利用反射调用对应的方法,将结果返回给客户端。 - -**基于HTTP协议** - -​ 客户端向服务端发送请求,如GET、POST、PUT、DELETE等请求。 -​ 服务端根据不同的请求参数和请求URL进行方法调用,返回JSON或者XML数据结果。 - -**TCP和HTTP对比** - -- 基于TCP协议实现的RPC调用,由于是底层协议栈,更佳灵活的对协议字段进行定制,可减少网络开销,提高性能,实现更大的吞吐量和并发数。但**,底层复杂,实现代价高**。 -- 基于HTTP协议实现的RPC调用,已封装实现序列化,但HTTP属于应用层协议,HTTP传输**所占用的字节数比TCP更高,传输效率对比TCP较低**。 - - - - - - - -# 版本一 - -**part1** - -- 实现基本的rpc调用 -- 客户端动态代理 -- 定义统一的request和response - -**part2** - -- 引入netty框架进行信息传输 -- 自定义消息格式 - -**part3** - -- 引入zookeeper作为注册中心 - - - -![1720376325450](README.assets/1720376325450.png) - - - -# 版本二 - -**part1** - -- netty自定义编码器,解码器和序列化器 - - - -**part2** - -- 在客户端建立本地服务缓存 - -- 实现本地缓存的动态更新 - - - -![1720376518663](README.assets/1720376518663.png) - - - - - -# 版本三 - -**part1** - -- 实现客户端的负载均衡 - - - -**part2** - -- 实现客户端的容错:失败重传 -- 服务白名单 - - - -![1720376674829](README.assets/1720376674829.png) - - - - - -# 版本四 - -**part1** - -- 服务限流,降级的实现 -- 熔断器的实现 - - - - - -![1720376759165](README.assets/1720376759165.png) - - - - - -# TodoList - -- [ ] 补充其它序列化方式(如ProtoBuf,Hessian) - -- [ ] 集成Spring - -- [ ] 主动下线失败次数过多的节点 - -- [ ] 探测离线节点的状态,对恢复正常的节点重新上线 - -- [ ] 实现自适应的负载均衡器 - - +# RPC-Java + +[【代码随想录知识星球】](https://www.programmercarl.com/other/kstar.html)项目分享-手撕RPC框架,[项目专栏](https://t.zsxq.com/0CVPn) 有本项目的详细文档 + +* [第一章:项目背景、项目收获、简历写法、项目架构和流程图](https://t.zsxq.com/RtJmQ) +* [第二章(版本一):实现一个基本的rpc调用](https://t.zsxq.com/QImhy) +* [第三章(版本二):netty自定义编码器,解码器和序列化器,创建缓存](https://t.zsxq.com/osLak) +* [第四章(版本三):负载均衡、超时重试 &白名单](https://t.zsxq.com/zVqPE) +* [第五章(版本四):服务-限流、熔断](https://t.zsxq.com/Oj1gG0) +* [第六章:项目常见问题以及如何回答(网络传输层面,注册中心层面,算法层面,各种场景题](https://t.zsxq.com/xOAAq) + +## 启动流程 + +1.先安装并开启zookeeper + +安装参考: + +[【Zookeeper】Windows下安装Zookeeper(图文记录详细步骤,手把手包安装成功)_windows安装zk-CSDN博客](https://blog.csdn.net/tttzzzqqq2018/article/details/132093374?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522172149339116800211548359%2522%252C%2522scm%2522%253A%252220140713.130102334) + +2.运行Server包下的TestServer,再运行Client包下的TestClient + + + +# RPC概念 + +### 概念 + +1. RPC(Remote Procedure Call Protocol) 远程过程调用协议。 +2. RPC是一种通过网络从远程计算机程序上请求服务,不需要了解底层网络技术的协议。 +3. RPC主要作用就是不同的服务间方法调用就像本地调用一样便捷。 + + + +### 常用RPC技术或框架 + +应用级的服务框架:阿里的 Dubbo/Dubbox、Google gRPC、Spring Boot/Spring Cloud。 +远程通信协议:RMI、Socket、SOAP(HTTP XML)、REST(HTTP JSON)。 +通信框架:MINA 和 Netty + + + +### 为什么要有RPC? + +1. 服务化:微服务化,跨平台的服务之间远程调用; +2. 分布式系统架构:分布式服务跨机器进行远程调用; +3. 服务可重用:开发一个公共能力服务,供多个服务远程调用。 +4. 系统间交互调用:两台服务器A、B,服务器A上的应用a需要调用服务器B上的应用b提供的方法,而应用a和应用b不在一个内存空间,不能直接调用,此时,需要通过网络传输来表达需要调用的语义及传输调用的数据。 + +#### 使用场景 + +1. `大型网站`:内部涉及多个子系统,服务、接口较多。 +2. `注册发现机制`:如Nacos、Dubbo等,一般都有注册中心,服务有多个实例,调用方调用的哪个实例无感知。 +3. `安全性`:不暴露资源 +4. `服务化治理`:微服务架构、分布式架构。 + + + +### 架构图 + +![](README.assets/655c04a02b08474e985ff4bf8a561d12.png) + + + +### 核心功能概念 + +核心功能实现主要分为**服务寻址**、**序列化和反序列化**、**网络传输功能**。 + +#### 服务寻址功能 + +**Call ID映射:** + +​ 本地:在本地方法调用中,函数体是直接通过函数指针来指定的,但是在远程调用中,由于两个进程的地址空间完全不一样,函数指针不起作用。 +​ 远程:RPC中所有函数或方法都有自己的一个ID,在所有进程中都唯一。客户端在做远程过程调用时,必须附上这个ID,即客户端会查一下表,找出相应的Call ID,然后传给服务端,服务端也会查表,来确定客户端需要调用的函数,然后执行相应函数的代码。 +​ Call ID映射表一般是一个哈希表。 + + + +#### 序列化和反序列化功能 + +**概述:** + +- 序列化:将消息对象转换为二进制流。 +- 反序列化:将二进制流转换为消息对象。 + + + +**必要性**: +远程调用涉及到数据的传输,在本地调用中,只需要将数据压入栈中,然后让函数去栈中读取即可。 +但远程的数据传输,由于客户端和服务端不在同一个服务器上,涉及不同的进程,不能通过内存传递参数,此时就需要将客户端先将请求参数转成字节流(编码),传递给服务端,服务端再将字节流转为自己可读取格式(解码),这就是序列化和反序列化的过程。反之,服务端返回值也逆向经历序列化和反序列化到客户端。 + +**序列化的优势:** +将消息对象转为二进制字节流,便于网络传输。 +可跨平台、跨语言。如Python编写的客户端请求序列化参数传输到Java编写的服务端进行反序列化。 + +#### 网络传输功能 + +**作用**: + +- 客户端将Call ID和序列化后的参数字节流传输给服务端。 +- 服务端将序列化后的调用结果回传给客户端。 + +**协议**: +  主要有TCP、UDP、HTTP协议。 + +**基于TCP协议** + +​ 客户端和服务端建立Socket连接。 +​ 客户端通过Socket将需要调用的接口名称、方法名称及参数序列化后传递给服务端。 +服务端反序列化后再利用反射调用对应的方法,将结果返回给客户端。 + +**基于HTTP协议** + +​ 客户端向服务端发送请求,如GET、POST、PUT、DELETE等请求。 +​ 服务端根据不同的请求参数和请求URL进行方法调用,返回JSON或者XML数据结果。 + +**TCP和HTTP对比** + +- 基于TCP协议实现的RPC调用,由于是底层协议栈,更佳灵活的对协议字段进行定制,可减少网络开销,提高性能,实现更大的吞吐量和并发数。但**,底层复杂,实现代价高**。 +- 基于HTTP协议实现的RPC调用,已封装实现序列化,但HTTP属于应用层协议,HTTP传输**所占用的字节数比TCP更高,传输效率对比TCP较低**。 + + + + + + + +# 版本一 + +**part1** + +- 实现基本的rpc调用 +- 客户端动态代理 +- 定义统一的request和response + +**part2** + +- 引入netty框架进行信息传输 +- 自定义消息格式 + +**part3** + +- 引入zookeeper作为注册中心 + + + +![1720376325450](README.assets/1720376325450.png) + + + +# 版本二 + +**part1** + +- netty自定义编码器,解码器和序列化器 + + + +**part2** + +- 在客户端建立本地服务缓存 + +- 实现本地缓存的动态更新 + + + +![1720376518663](README.assets/1720376518663.png) + + + + + +# 版本三 + +**part1** + +- 实现客户端的负载均衡 + + + +**part2** + +- 实现客户端的容错:失败重传 +- 服务白名单 + + + +![1720376674829](README.assets/1720376674829.png) + + + + + +# 版本四 + +**part1** + +- 服务限流,降级的实现 +- 熔断器的实现 + + + + + +![1720376759165](README.assets/1720376759165.png) + + + +# 版本五 + +- 添加Hessian、protostuff、kryo等序列化方式 +- 优化了服务端的关闭 +- 配置顶 +- SPI机制 + +![](assets/image.png) + +# TodoList + +- [ ] 集成Spring + +- [ ] 主动下线失败次数过多的节点 + +- [ ] 探测离线节点的状态,对恢复正常的节点重新上线 + +- [ ] 实现自适应的负载均衡器 + + From 930ca3d84401c22fe5901fae5d972d52571cc297 Mon Sep 17 00:00:00 2001 From: xiaobaozi-web <864508127@qq.com> Date: Fri, 6 Dec 2024 20:50:41 +0800 Subject: [PATCH 2/5] Add files via upload From afcc7eac59645584a8c1e126318bcc09d4e50476 Mon Sep 17 00:00:00 2001 From: xiaobaozi-web <864508127@qq.com> Date: Fri, 6 Dec 2024 20:54:39 +0800 Subject: [PATCH 3/5] Add files via upload --- README.assets/image.png | Bin 0 -> 60992 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 README.assets/image.png diff --git a/README.assets/image.png b/README.assets/image.png new file mode 100644 index 0000000000000000000000000000000000000000..9b95bfaee6acb807c6567ffcc5b8ab87b9e3e841 GIT binary patch literal 60992 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 psr=)hA0x^A{9 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 V0xYT?jW|>iV6?zIPOAIp000&p{Gb2; literal 0 HcmV?d00001 From 069b1ae425a6813654684ac02ebc05ce4c084b49 Mon Sep 17 00:00:00 2001 From: shaojitong <864508127@qq.com> Date: Mon, 9 Dec 2024 10:24:09 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E7=AC=AC=E4=BA=94=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 14 +- version5/.idea/.gitignore | 8 + version5/.idea/compiler.xml | 21 +++ version5/.idea/encodings.xml | 20 +++ .../inspectionProfiles/Project_Default.xml | 8 + version5/.idea/jarRepositories.xml | 20 +++ version5/.idea/misc.xml | 19 +++ version5/.idea/uiDesigner.xml | 124 ++++++++++++++ version5/.idea/vcs.xml | 4 + version5/krpc-api/pom.xml | 20 +++ .../src/main/java/com/kama/pojo/User.java | 26 +++ .../java/com/kama/service/UserService.java | 19 +++ .../com/kama/pojo/User$UserBuilder.class | Bin 0 -> 1701 bytes .../target/classes/com/kama/pojo/User.class | Bin 0 -> 3013 bytes .../com/kama/service/UserService.class | Bin 0 -> 257 bytes .../krpc-api/target/krpc-api-1.0-SNAPSHOT.jar | Bin 0 -> 4476 bytes .../target/maven-archiver/pom.properties | 5 + .../compile/default-compile/createdFiles.lst | 3 + .../compile/default-compile/inputFiles.lst | 2 + version5/krpc-common/pom.xml | 32 ++++ .../common/exception/SerializeException.java | 17 ++ .../main/java/common/message/MessageType.java | 13 ++ .../main/java/common/message/RpcRequest.java | 31 ++++ .../main/java/common/message/RpcResponse.java | 37 +++++ .../common/serializer/mycoder/MyDecoder.java | 58 +++++++ .../common/serializer/mycoder/MyEncoder.java | 50 ++++++ .../myserializer/HessianSerializer.java | 52 ++++++ .../myserializer/JsonSerializer.java | 78 +++++++++ .../myserializer/KryoSerializer.java | 81 ++++++++++ .../myserializer/ObjectSerializer.java | 60 +++++++ .../myserializer/ProtostuffSerializer.java | 83 ++++++++++ .../serializer/myserializer/Serializer.java | 35 ++++ .../src/main/java/common/spi/SpiLoader.java | 111 +++++++++++++ .../src/main/java/common/util/ConfigUtil.java | 48 ++++++ .../common/exception/SerializeException.class | Bin 0 -> 561 bytes .../classes/common/message/MessageType.class | Bin 0 -> 1196 bytes .../RpcRequest$RpcRequestBuilder.class | Bin 0 -> 2248 bytes .../classes/common/message/RpcRequest.class | Bin 0 -> 3779 bytes .../RpcResponse$RpcResponseBuilder.class | Bin 0 -> 2153 bytes .../classes/common/message/RpcResponse.class | Bin 0 -> 4218 bytes .../common/serializer/mycoder/MyDecoder.class | Bin 0 -> 3033 bytes .../common/serializer/mycoder/MyEncoder.class | Bin 0 -> 2549 bytes .../myserializer/HessianSerializer.class | Bin 0 -> 2191 bytes .../myserializer/JsonSerializer.class | Bin 0 -> 2794 bytes .../myserializer/KryoSerializer.class | Bin 0 -> 3569 bytes .../myserializer/ObjectSerializer.class | Bin 0 -> 1942 bytes .../myserializer/ProtostuffSerializer.class | Bin 0 -> 3186 bytes .../serializer/myserializer/Serializer.class | Bin 0 -> 1296 bytes .../target/classes/common/spi/SpiLoader.class | Bin 0 -> 5133 bytes .../classes/common/util/ConfigUtil.class | Bin 0 -> 2372 bytes version5/krpc-consumer/pom.xml | 49 ++++++ .../java/com/kama/consumer/ConsumerTest.java | 66 ++++++++ .../com/kama/consumer/ConsumerTestConfig.java | 20 +++ .../src/main/resources/application.properties | 7 + .../target/classes/application.properties | 7 + .../com/kama/consumer/ConsumerTest.class | Bin 0 -> 3941 bytes .../kama/consumer/ConsumerTestConfig.class | Bin 0 -> 781 bytes .../target/krpc-consumer-1.0-SNAPSHOT.jar | Bin 0 -> 3110 bytes .../target/maven-archiver/pom.properties | 5 + .../compile/default-compile/createdFiles.lst | 2 + .../compile/default-compile/inputFiles.lst | 2 + version5/krpc-core/pom.xml | 114 +++++++++++++ .../main/java/com/kama/KRpcApplication.java | 48 ++++++ .../com/kama/client/cache/ServiceCache.java | 75 +++++++++ .../client/circuitbreaker/CircuitBreaker.java | 109 +++++++++++++ .../CircuitBreakerProvider.java | 28 ++++ .../kama/client/netty/NettyClientHandler.java | 34 ++++ .../client/netty/NettyClientInitializer.java | 40 +++++ .../com/kama/client/proxy/ClientProxy.java | 92 +++++++++++ .../com/kama/client/retry/GuavaRetry.java | 48 ++++++ .../com/kama/client/rpcclient/RpcClient.java | 17 ++ .../client/rpcclient/impl/NettyRpcClient.java | 103 ++++++++++++ .../rpcclient/impl/SimpleSocketRpcClient.java | 57 +++++++ .../client/servicecenter/ServiceCenter.java | 20 +++ .../client/servicecenter/ZKServiceCenter.java | 113 +++++++++++++ .../servicecenter/ZKWatcher/watchZK.java | 98 +++++++++++ .../servicecenter/balance/LoadBalance.java | 20 +++ .../balance/impl/ConsistencyHashBalance.java | 152 ++++++++++++++++++ .../balance/impl/RandomLoadBalance.java | 48 ++++++ .../balance/impl/RoundLoadBalance.java | 52 ++++++ .../main/java/com/kama/config/KRpcConfig.java | 37 +++++ .../java/com/kama/config/RpcConstant.java | 20 +++ .../server/netty/NettyRpcServerHandler.java | 71 ++++++++ .../server/netty/NettyServerInitializer.java | 33 ++++ .../kama/server/provider/ServiceProvider.java | 60 +++++++ .../com/kama/server/ratelimit/RateLimit.java | 15 ++ .../impl/TokenBucketRateLimitImpl.java | 60 +++++++ .../ratelimit/provider/RateLimitProvider.java | 34 ++++ .../com/kama/server/server/RpcServer.java | 16 ++ .../server/server/impl/NettyRpcServer.java | 81 ++++++++++ .../server/server/impl/SimpleRpcServer.java | 70 ++++++++ .../kama/server/server/work/WorkThread.java | 61 +++++++ .../serviceRegister/ServiceRegister.java | 16 ++ .../impl/ZKServiceRegister.java | 74 +++++++++ .../balance/ConsistencyHashBalanceTest.java | 100 ++++++++++++ .../test/balance/RandomLoadBalanceTest.java | 76 +++++++++ .../test/balance/RoundLoadBalanceTest.java | 93 +++++++++++ .../serializer/HessianSerializerTest.java | 52 ++++++ .../test/serializer/KryoSerializerTest.java | 89 ++++++++++ .../serializer/ProtostuffSerializerTest.java | 89 ++++++++++ ....common.serializer.myserializer.Serializer | 5 + ....common.serializer.myserializer.Serializer | 5 + .../classes/com/kama/KRpcApplication.class | Bin 0 -> 1617 bytes .../com/kama/client/cache/ServiceCache.class | Bin 0 -> 3168 bytes .../circuitbreaker/CircuitBreaker$1.class | Bin 0 -> 889 bytes .../circuitbreaker/CircuitBreaker.class | Bin 0 -> 3249 bytes .../CircuitBreakerProvider.class | Bin 0 -> 2116 bytes .../circuitbreaker/CircuitBreakerState.class | Bin 0 -> 1229 bytes .../client/netty/NettyClientHandler.class | Bin 0 -> 2135 bytes .../client/netty/NettyClientInitializer.class | Bin 0 -> 2184 bytes .../com/kama/client/proxy/ClientProxy.class | Bin 0 -> 4534 bytes .../com/kama/client/retry/GuavaRetry$1.class | Bin 0 -> 1343 bytes .../com/kama/client/retry/GuavaRetry.class | Bin 0 -> 4250 bytes .../com/kama/client/rpcclient/RpcClient.class | Bin 0 -> 207 bytes .../rpcclient/impl/NettyRpcClient.class | Bin 0 -> 4718 bytes .../impl/SimpleSocketRpcClient.class | Bin 0 -> 2938 bytes .../client/servicecenter/ServiceCenter.class | Bin 0 -> 260 bytes .../servicecenter/ZKServiceCenter.class | Bin 0 -> 5446 bytes .../servicecenter/ZKWatcher/watchZK$1.class | Bin 0 -> 3154 bytes .../servicecenter/ZKWatcher/watchZK.class | Bin 0 -> 2426 bytes .../servicecenter/balance/LoadBalance.class | Bin 0 -> 337 bytes .../balance/impl/ConsistencyHashBalance.class | Bin 0 -> 5355 bytes .../balance/impl/RandomLoadBalance.class | Bin 0 -> 2176 bytes .../balance/impl/RoundLoadBalance.class | Bin 0 -> 2805 bytes .../config/KRpcConfig$KRpcConfigBuilder.class | Bin 0 -> 2349 bytes .../classes/com/kama/config/KRpcConfig.class | Bin 0 -> 3648 bytes .../classes/com/kama/config/RpcConstant.class | Bin 0 -> 255 bytes .../server/netty/NettyRpcServerHandler.class | Bin 0 -> 4906 bytes .../server/netty/NettyServerInitializer.class | Bin 0 -> 1682 bytes .../server/provider/ServiceProvider.class | Bin 0 -> 2307 bytes .../com/kama/server/ratelimit/RateLimit.class | Bin 0 -> 150 bytes .../impl/TokenBucketRateLimitImpl.class | Bin 0 -> 1290 bytes .../provider/RateLimitProvider.class | Bin 0 -> 2264 bytes .../com/kama/server/server/RpcServer.class | Bin 0 -> 166 bytes .../server/server/impl/NettyRpcServer.class | Bin 0 -> 3723 bytes .../server/server/impl/SimpleRpcServer.class | Bin 0 -> 2608 bytes .../kama/server/server/work/WorkThread.class | Bin 0 -> 2959 bytes .../serviceRegister/ServiceRegister.class | Bin 0 -> 215 bytes .../impl/ZKServiceRegister.class | Bin 0 -> 4674 bytes .../balance/ConsistencyHashBalanceTest.class | Bin 0 -> 4410 bytes .../test/balance/RandomLoadBalanceTest.class | Bin 0 -> 2086 bytes .../test/balance/RoundLoadBalanceTest.class | Bin 0 -> 2529 bytes .../serializer/HessianSerializerTest.class | Bin 0 -> 1784 bytes .../test/serializer/KryoSerializerTest.class | Bin 0 -> 3493 bytes .../serializer/ProtostuffSerializerTest.class | Bin 0 -> 3525 bytes .../target/krpc-core-1.0-SNAPSHOT.jar | Bin 0 -> 78697 bytes .../target/maven-archiver/pom.properties | 5 + .../compile/default-compile/createdFiles.lst | 55 +++++++ .../compile/default-compile/inputFiles.lst | 54 +++++++ version5/krpc-provider/pom.xml | 52 ++++++ .../java/com/kama/provider/ProviderTest.java | 35 ++++ .../kama/provider/impl/UserServiceImpl.java | 41 +++++ .../src/main/resources/application.properties | 7 + .../classes/META-INF/application.properties | 0 .../target/classes/application.properties | 7 + .../com/kama/provider/ProviderTest.class | Bin 0 -> 1479 bytes .../kama/provider/impl/UserServiceImpl.class | Bin 0 -> 1930 bytes .../target/krpc-provider-1.0-SNAPSHOT.jar | Bin 0 -> 5129 bytes .../target/maven-archiver/pom.properties | 5 + .../compile/default-compile/createdFiles.lst | 3 + .../compile/default-compile/inputFiles.lst | 3 + version5/pom.xml | 97 +++++++++++ 162 files changed, 4040 insertions(+), 6 deletions(-) create mode 100644 version5/.idea/.gitignore create mode 100644 version5/.idea/compiler.xml create mode 100644 version5/.idea/encodings.xml create mode 100644 version5/.idea/inspectionProfiles/Project_Default.xml create mode 100644 version5/.idea/jarRepositories.xml create mode 100644 version5/.idea/misc.xml create mode 100644 version5/.idea/uiDesigner.xml create mode 100644 version5/.idea/vcs.xml create mode 100644 version5/krpc-api/pom.xml create mode 100644 version5/krpc-api/src/main/java/com/kama/pojo/User.java create mode 100644 version5/krpc-api/src/main/java/com/kama/service/UserService.java create mode 100644 version5/krpc-api/target/classes/com/kama/pojo/User$UserBuilder.class create mode 100644 version5/krpc-api/target/classes/com/kama/pojo/User.class create mode 100644 version5/krpc-api/target/classes/com/kama/service/UserService.class create mode 100644 version5/krpc-api/target/krpc-api-1.0-SNAPSHOT.jar create mode 100644 version5/krpc-api/target/maven-archiver/pom.properties create mode 100644 version5/krpc-api/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst create mode 100644 version5/krpc-api/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst create mode 100644 version5/krpc-common/pom.xml create mode 100644 version5/krpc-common/src/main/java/common/exception/SerializeException.java create mode 100644 version5/krpc-common/src/main/java/common/message/MessageType.java create mode 100644 version5/krpc-common/src/main/java/common/message/RpcRequest.java create mode 100644 version5/krpc-common/src/main/java/common/message/RpcResponse.java create mode 100644 version5/krpc-common/src/main/java/common/serializer/mycoder/MyDecoder.java create mode 100644 version5/krpc-common/src/main/java/common/serializer/mycoder/MyEncoder.java create mode 100644 version5/krpc-common/src/main/java/common/serializer/myserializer/HessianSerializer.java create mode 100644 version5/krpc-common/src/main/java/common/serializer/myserializer/JsonSerializer.java create mode 100644 version5/krpc-common/src/main/java/common/serializer/myserializer/KryoSerializer.java create mode 100644 version5/krpc-common/src/main/java/common/serializer/myserializer/ObjectSerializer.java create mode 100644 version5/krpc-common/src/main/java/common/serializer/myserializer/ProtostuffSerializer.java create mode 100644 version5/krpc-common/src/main/java/common/serializer/myserializer/Serializer.java create mode 100644 version5/krpc-common/src/main/java/common/spi/SpiLoader.java create mode 100644 version5/krpc-common/src/main/java/common/util/ConfigUtil.java create mode 100644 version5/krpc-common/target/classes/common/exception/SerializeException.class create mode 100644 version5/krpc-common/target/classes/common/message/MessageType.class create mode 100644 version5/krpc-common/target/classes/common/message/RpcRequest$RpcRequestBuilder.class create mode 100644 version5/krpc-common/target/classes/common/message/RpcRequest.class create mode 100644 version5/krpc-common/target/classes/common/message/RpcResponse$RpcResponseBuilder.class create mode 100644 version5/krpc-common/target/classes/common/message/RpcResponse.class create mode 100644 version5/krpc-common/target/classes/common/serializer/mycoder/MyDecoder.class create mode 100644 version5/krpc-common/target/classes/common/serializer/mycoder/MyEncoder.class create mode 100644 version5/krpc-common/target/classes/common/serializer/myserializer/HessianSerializer.class create mode 100644 version5/krpc-common/target/classes/common/serializer/myserializer/JsonSerializer.class create mode 100644 version5/krpc-common/target/classes/common/serializer/myserializer/KryoSerializer.class create mode 100644 version5/krpc-common/target/classes/common/serializer/myserializer/ObjectSerializer.class create mode 100644 version5/krpc-common/target/classes/common/serializer/myserializer/ProtostuffSerializer.class create mode 100644 version5/krpc-common/target/classes/common/serializer/myserializer/Serializer.class create mode 100644 version5/krpc-common/target/classes/common/spi/SpiLoader.class create mode 100644 version5/krpc-common/target/classes/common/util/ConfigUtil.class create mode 100644 version5/krpc-consumer/pom.xml create mode 100644 version5/krpc-consumer/src/main/java/com/kama/consumer/ConsumerTest.java create mode 100644 version5/krpc-consumer/src/main/java/com/kama/consumer/ConsumerTestConfig.java create mode 100644 version5/krpc-consumer/src/main/resources/application.properties create mode 100644 version5/krpc-consumer/target/classes/application.properties create mode 100644 version5/krpc-consumer/target/classes/com/kama/consumer/ConsumerTest.class create mode 100644 version5/krpc-consumer/target/classes/com/kama/consumer/ConsumerTestConfig.class create mode 100644 version5/krpc-consumer/target/krpc-consumer-1.0-SNAPSHOT.jar create mode 100644 version5/krpc-consumer/target/maven-archiver/pom.properties create mode 100644 version5/krpc-consumer/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst create mode 100644 version5/krpc-consumer/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst create mode 100644 version5/krpc-core/pom.xml create mode 100644 version5/krpc-core/src/main/java/com/kama/KRpcApplication.java create mode 100644 version5/krpc-core/src/main/java/com/kama/client/cache/ServiceCache.java create mode 100644 version5/krpc-core/src/main/java/com/kama/client/circuitbreaker/CircuitBreaker.java create mode 100644 version5/krpc-core/src/main/java/com/kama/client/circuitbreaker/CircuitBreakerProvider.java create mode 100644 version5/krpc-core/src/main/java/com/kama/client/netty/NettyClientHandler.java create mode 100644 version5/krpc-core/src/main/java/com/kama/client/netty/NettyClientInitializer.java create mode 100644 version5/krpc-core/src/main/java/com/kama/client/proxy/ClientProxy.java create mode 100644 version5/krpc-core/src/main/java/com/kama/client/retry/GuavaRetry.java create mode 100644 version5/krpc-core/src/main/java/com/kama/client/rpcclient/RpcClient.java create mode 100644 version5/krpc-core/src/main/java/com/kama/client/rpcclient/impl/NettyRpcClient.java create mode 100644 version5/krpc-core/src/main/java/com/kama/client/rpcclient/impl/SimpleSocketRpcClient.java create mode 100644 version5/krpc-core/src/main/java/com/kama/client/servicecenter/ServiceCenter.java create mode 100644 version5/krpc-core/src/main/java/com/kama/client/servicecenter/ZKServiceCenter.java create mode 100644 version5/krpc-core/src/main/java/com/kama/client/servicecenter/ZKWatcher/watchZK.java create mode 100644 version5/krpc-core/src/main/java/com/kama/client/servicecenter/balance/LoadBalance.java create mode 100644 version5/krpc-core/src/main/java/com/kama/client/servicecenter/balance/impl/ConsistencyHashBalance.java create mode 100644 version5/krpc-core/src/main/java/com/kama/client/servicecenter/balance/impl/RandomLoadBalance.java create mode 100644 version5/krpc-core/src/main/java/com/kama/client/servicecenter/balance/impl/RoundLoadBalance.java create mode 100644 version5/krpc-core/src/main/java/com/kama/config/KRpcConfig.java create mode 100644 version5/krpc-core/src/main/java/com/kama/config/RpcConstant.java create mode 100644 version5/krpc-core/src/main/java/com/kama/server/netty/NettyRpcServerHandler.java create mode 100644 version5/krpc-core/src/main/java/com/kama/server/netty/NettyServerInitializer.java create mode 100644 version5/krpc-core/src/main/java/com/kama/server/provider/ServiceProvider.java create mode 100644 version5/krpc-core/src/main/java/com/kama/server/ratelimit/RateLimit.java create mode 100644 version5/krpc-core/src/main/java/com/kama/server/ratelimit/impl/TokenBucketRateLimitImpl.java create mode 100644 version5/krpc-core/src/main/java/com/kama/server/ratelimit/provider/RateLimitProvider.java create mode 100644 version5/krpc-core/src/main/java/com/kama/server/server/RpcServer.java create mode 100644 version5/krpc-core/src/main/java/com/kama/server/server/impl/NettyRpcServer.java create mode 100644 version5/krpc-core/src/main/java/com/kama/server/server/impl/SimpleRpcServer.java create mode 100644 version5/krpc-core/src/main/java/com/kama/server/server/work/WorkThread.java create mode 100644 version5/krpc-core/src/main/java/com/kama/server/serviceRegister/ServiceRegister.java create mode 100644 version5/krpc-core/src/main/java/com/kama/server/serviceRegister/impl/ZKServiceRegister.java create mode 100644 version5/krpc-core/src/main/java/com/kama/test/balance/ConsistencyHashBalanceTest.java create mode 100644 version5/krpc-core/src/main/java/com/kama/test/balance/RandomLoadBalanceTest.java create mode 100644 version5/krpc-core/src/main/java/com/kama/test/balance/RoundLoadBalanceTest.java create mode 100644 version5/krpc-core/src/main/java/com/kama/test/serializer/HessianSerializerTest.java create mode 100644 version5/krpc-core/src/main/java/com/kama/test/serializer/KryoSerializerTest.java create mode 100644 version5/krpc-core/src/main/java/com/kama/test/serializer/ProtostuffSerializerTest.java create mode 100644 version5/krpc-core/src/main/resources/META-INF/serializer/com.kama.common.serializer.myserializer.Serializer create mode 100644 version5/krpc-core/target/classes/META-INF/serializer/com.kama.common.serializer.myserializer.Serializer create mode 100644 version5/krpc-core/target/classes/com/kama/KRpcApplication.class create mode 100644 version5/krpc-core/target/classes/com/kama/client/cache/ServiceCache.class create mode 100644 version5/krpc-core/target/classes/com/kama/client/circuitbreaker/CircuitBreaker$1.class create mode 100644 version5/krpc-core/target/classes/com/kama/client/circuitbreaker/CircuitBreaker.class create mode 100644 version5/krpc-core/target/classes/com/kama/client/circuitbreaker/CircuitBreakerProvider.class create mode 100644 version5/krpc-core/target/classes/com/kama/client/circuitbreaker/CircuitBreakerState.class create mode 100644 version5/krpc-core/target/classes/com/kama/client/netty/NettyClientHandler.class create mode 100644 version5/krpc-core/target/classes/com/kama/client/netty/NettyClientInitializer.class create mode 100644 version5/krpc-core/target/classes/com/kama/client/proxy/ClientProxy.class create mode 100644 version5/krpc-core/target/classes/com/kama/client/retry/GuavaRetry$1.class create mode 100644 version5/krpc-core/target/classes/com/kama/client/retry/GuavaRetry.class create mode 100644 version5/krpc-core/target/classes/com/kama/client/rpcclient/RpcClient.class create mode 100644 version5/krpc-core/target/classes/com/kama/client/rpcclient/impl/NettyRpcClient.class create mode 100644 version5/krpc-core/target/classes/com/kama/client/rpcclient/impl/SimpleSocketRpcClient.class create mode 100644 version5/krpc-core/target/classes/com/kama/client/servicecenter/ServiceCenter.class create mode 100644 version5/krpc-core/target/classes/com/kama/client/servicecenter/ZKServiceCenter.class create mode 100644 version5/krpc-core/target/classes/com/kama/client/servicecenter/ZKWatcher/watchZK$1.class create mode 100644 version5/krpc-core/target/classes/com/kama/client/servicecenter/ZKWatcher/watchZK.class create mode 100644 version5/krpc-core/target/classes/com/kama/client/servicecenter/balance/LoadBalance.class create mode 100644 version5/krpc-core/target/classes/com/kama/client/servicecenter/balance/impl/ConsistencyHashBalance.class create mode 100644 version5/krpc-core/target/classes/com/kama/client/servicecenter/balance/impl/RandomLoadBalance.class create mode 100644 version5/krpc-core/target/classes/com/kama/client/servicecenter/balance/impl/RoundLoadBalance.class create mode 100644 version5/krpc-core/target/classes/com/kama/config/KRpcConfig$KRpcConfigBuilder.class create mode 100644 version5/krpc-core/target/classes/com/kama/config/KRpcConfig.class create mode 100644 version5/krpc-core/target/classes/com/kama/config/RpcConstant.class create mode 100644 version5/krpc-core/target/classes/com/kama/server/netty/NettyRpcServerHandler.class create mode 100644 version5/krpc-core/target/classes/com/kama/server/netty/NettyServerInitializer.class create mode 100644 version5/krpc-core/target/classes/com/kama/server/provider/ServiceProvider.class create mode 100644 version5/krpc-core/target/classes/com/kama/server/ratelimit/RateLimit.class create mode 100644 version5/krpc-core/target/classes/com/kama/server/ratelimit/impl/TokenBucketRateLimitImpl.class create mode 100644 version5/krpc-core/target/classes/com/kama/server/ratelimit/provider/RateLimitProvider.class create mode 100644 version5/krpc-core/target/classes/com/kama/server/server/RpcServer.class create mode 100644 version5/krpc-core/target/classes/com/kama/server/server/impl/NettyRpcServer.class create mode 100644 version5/krpc-core/target/classes/com/kama/server/server/impl/SimpleRpcServer.class create mode 100644 version5/krpc-core/target/classes/com/kama/server/server/work/WorkThread.class create mode 100644 version5/krpc-core/target/classes/com/kama/server/serviceRegister/ServiceRegister.class create mode 100644 version5/krpc-core/target/classes/com/kama/server/serviceRegister/impl/ZKServiceRegister.class create mode 100644 version5/krpc-core/target/classes/com/kama/test/balance/ConsistencyHashBalanceTest.class create mode 100644 version5/krpc-core/target/classes/com/kama/test/balance/RandomLoadBalanceTest.class create mode 100644 version5/krpc-core/target/classes/com/kama/test/balance/RoundLoadBalanceTest.class create mode 100644 version5/krpc-core/target/classes/com/kama/test/serializer/HessianSerializerTest.class create mode 100644 version5/krpc-core/target/classes/com/kama/test/serializer/KryoSerializerTest.class create mode 100644 version5/krpc-core/target/classes/com/kama/test/serializer/ProtostuffSerializerTest.class create mode 100644 version5/krpc-core/target/krpc-core-1.0-SNAPSHOT.jar create mode 100644 version5/krpc-core/target/maven-archiver/pom.properties create mode 100644 version5/krpc-core/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst create mode 100644 version5/krpc-core/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst create mode 100644 version5/krpc-provider/pom.xml create mode 100644 version5/krpc-provider/src/main/java/com/kama/provider/ProviderTest.java create mode 100644 version5/krpc-provider/src/main/java/com/kama/provider/impl/UserServiceImpl.java create mode 100644 version5/krpc-provider/src/main/resources/application.properties create mode 100644 version5/krpc-provider/target/classes/META-INF/application.properties create mode 100644 version5/krpc-provider/target/classes/application.properties create mode 100644 version5/krpc-provider/target/classes/com/kama/provider/ProviderTest.class create mode 100644 version5/krpc-provider/target/classes/com/kama/provider/impl/UserServiceImpl.class create mode 100644 version5/krpc-provider/target/krpc-provider-1.0-SNAPSHOT.jar create mode 100644 version5/krpc-provider/target/maven-archiver/pom.properties create mode 100644 version5/krpc-provider/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst create mode 100644 version5/krpc-provider/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst create mode 100644 version5/pom.xml diff --git a/README.md b/README.md index bec3697..0259d90 100644 --- a/README.md +++ b/README.md @@ -203,20 +203,22 @@ ![1720376759165](README.assets/1720376759165.png) - - # 版本五 -- 添加Hessian、protostuff、kryo等序列化方式 -- 优化了服务端的关闭 -- 配置顶 - SPI机制 +- 配置顶 +- 新增kryo、Hessian、protostuff等序列化方式 +- 优化关闭方法 ![](assets/image.png) + + + + # TodoList -- [ ] 集成Spring +- [ ] 使用注解注册服务,消费服务 - [ ] 主动下线失败次数过多的节点 diff --git a/version5/.idea/.gitignore b/version5/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/version5/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/version5/.idea/compiler.xml b/version5/.idea/compiler.xml new file mode 100644 index 0000000..c8f73e3 --- /dev/null +++ b/version5/.idea/compiler.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/version5/.idea/encodings.xml b/version5/.idea/encodings.xml new file mode 100644 index 0000000..1da27bf --- /dev/null +++ b/version5/.idea/encodings.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/version5/.idea/inspectionProfiles/Project_Default.xml b/version5/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..c8e8ddc --- /dev/null +++ b/version5/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/version5/.idea/jarRepositories.xml b/version5/.idea/jarRepositories.xml new file mode 100644 index 0000000..5a2f139 --- /dev/null +++ b/version5/.idea/jarRepositories.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/version5/.idea/misc.xml b/version5/.idea/misc.xml new file mode 100644 index 0000000..4914f21 --- /dev/null +++ b/version5/.idea/misc.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/version5/.idea/uiDesigner.xml b/version5/.idea/uiDesigner.xml new file mode 100644 index 0000000..2b63946 --- /dev/null +++ b/version5/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/version5/.idea/vcs.xml b/version5/.idea/vcs.xml new file mode 100644 index 0000000..d843f34 --- /dev/null +++ b/version5/.idea/vcs.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/version5/krpc-api/pom.xml b/version5/krpc-api/pom.xml new file mode 100644 index 0000000..c0f741b --- /dev/null +++ b/version5/krpc-api/pom.xml @@ -0,0 +1,20 @@ + + + 4.0.0 + + com.kama + version5 + 1.0-SNAPSHOT + + + krpc-api + + + 17 + 17 + UTF-8 + + + \ No newline at end of file diff --git a/version5/krpc-api/src/main/java/com/kama/pojo/User.java b/version5/krpc-api/src/main/java/com/kama/pojo/User.java new file mode 100644 index 0000000..3713995 --- /dev/null +++ b/version5/krpc-api/src/main/java/com/kama/pojo/User.java @@ -0,0 +1,26 @@ +package com.kama.pojo; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * @ClassName User + * @Description User对象 + * @Author Tong + * @LastChangeDate 2024-12-05 0:53 + * @Version v5.0 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +public class User implements Serializable { + // 客户端和服务端共有的 + private Integer id; + private String userName; + private Boolean gender; +} diff --git a/version5/krpc-api/src/main/java/com/kama/service/UserService.java b/version5/krpc-api/src/main/java/com/kama/service/UserService.java new file mode 100644 index 0000000..388978b --- /dev/null +++ b/version5/krpc-api/src/main/java/com/kama/service/UserService.java @@ -0,0 +1,19 @@ +package com.kama.service; + + +import com.kama.pojo.User; + +/** + * @InterfaceName UserService + * @Description 接口 + * @Author Tong + * @LastChangeDate 2024-12-05 0:52 + * @Version v1.0 + */ + +public interface UserService { + // 查询 + User getUserByUserId(Integer id); + // 新增 + Integer insertUserId(User user); +} diff --git a/version5/krpc-api/target/classes/com/kama/pojo/User$UserBuilder.class b/version5/krpc-api/target/classes/com/kama/pojo/User$UserBuilder.class new file mode 100644 index 0000000000000000000000000000000000000000..261a629fcece9b13117c4a98d1c8c5a3d59a42ac GIT binary patch literal 1701 zcmbW1+fLg+5QhH^Aty`&AwcPY(l)e7ptuL<$%G3cA&^pf0i~*XwMkaN#IYlLgT$Nk zFlU|L z`p5c#Xh?>{s%e{YjbW%*+DjvabP^d2BhVOT8%{@W^A6X$&Vi$U@`PJZ-FnZon!;s> znN5a?+68sXmZBwGLxFB-$YP8k)gzZ}-VqGBi0O`WO}ph=<}^$oPr0;&?Q^5Zb;q#; zw~ZvG2+*HV29vn0>YHIOije?kxkQ%2n30t;dTB35irv+45BC|SJG?Ebj@{t$vnls! z=1Fc#kD>Pe5h78;JQ(6tr%AeGYo;x>d!4#)cX{3Nm2w)~+T*UNzQe`1+&3wQxmv#p z(S0!(RtBY6y54U&>KA5t1Gh`z&LzD*+;Cl;B!;QtpdeI7N;*Ln40*D-IB4ezW_FyO z+YlS3QZ21c=aRA_*KOg+O~<1%K1L}?PrAJOLCAfl>17kpGkA^{NxW2B{)(Y+!8Ywf zr!Dj#L~sf=ctbkw5yQ8rK%rp(-Q;%D5?-O^IPG57=+8B>a>;S`s4E7Te~s7&m(_?0 z5F)DFvUW@;|fNaK|dF#Gq~2ws5N!_dG%8Y9!*A#5;!h zK1~Ldngngs?A4}pw*y`9kY0uuU9}5-vXOg$6+NRI%G32O9OfuJCr^B*_25Rd>>vajm`7X5J&&uB~$af#Y8UMn(boT7*N4bpgv_gKZuKe;uF AmH+?% literal 0 HcmV?d00001 diff --git a/version5/krpc-api/target/classes/com/kama/pojo/User.class b/version5/krpc-api/target/classes/com/kama/pojo/User.class new file mode 100644 index 0000000000000000000000000000000000000000..1dc838daf5345cfc1df510a88ee66eb1d9c110f0 GIT binary patch literal 3013 zcma)8U31$+6g_K8mh8xF;yP&!C4|%siJjC?zKWd$Xy}I_NlVkF^aH43i$skh1zRqZ z|G+!L51=o7fEn7(loaFghLKys&ZGs(F=}>k|BuftPVq zK;5*wD&riTo{2y^k)|+?S2au+cug`7cXJ8I7?mYgYDvmt2J$#AkR;`~FxFn$Xtb{z zn8u925YbZcK!a>g7@-O>ilTv8%rUtwv$eJ4)~Ofwp_<`dmJGasHw6ywnA_Hp>(or| zs_hZvv^>-CS^~>a89KQ|(hb*%SUQnLMTtbCXn0#7>&s!gr8TQ*n+^MWbECnyw2h#6 z1SXly{>q3JScUZ!WOH!YcC6L*&W6>z9Pnh8-J020H_1)j(5QG@HZ#f&;3b#Z3U+gB zP{K3oRGWaV+CAkFrYBKJqB=X+L$xOhLBj1V*|5AAgzIg|I@M&Td3gwQ-{KF4d}F7S zjbNhlnD!^TC*nXb-8H1wJhQgFV(x}&s249)SI#h&Dl4&Rwb~7jb>T0hv*CGec0VD_msx81B+XKlITNh#?GSq)Tp)oOVwmfT0~(=ZkDT3*xKU9r3^ zx86FKz_k>vHuv+~t}lq$iH_WI9BjgZ z;7K>Cj$<{K8fL3yQJNv?5%yUPHG$*%bQqML1`0m@d?Z9CX@c|ePyZd_+9Sk&;aK1luL<6P5nkCjd6noA@6xIkeuE~V zu?X}q-lxR~04SrvCn38Yul>&He&-_to!MXqIFIx@=LR~@^R*YOTxOgy%|xO20NSs8 zac6w*u>cXqEz_6s$#fU-!62UWi}FbMCC`WWXb{iHi}J|rlRSJk1w7&uV@@)gABu&a zp(^)_e?k8VYW#ljPv|SNg@+jRA34aa&hk=L=KjV|PI-cndobp5%0s-;K}?oaLMF?J zoGLMiC&=7GI;Z9m5ztgwBWS9u=QIh@Ly(r!J0Rsg1Byq=B9QlH5a^z~Kj1W$n8ql+ z!^bR@7+Q8Y8d?!7p`cp>- zi@F#r7={(cB`i}YzLIbes~p9UqBIv6Y8=OKl2$3|B6I(YBh{DfTO^**z*oF31f_xy z&j{~6$E0BtY6sB4D7~f-mWaT*$bKuq4D-@nYnbPXvae*MZ2V10nI7W=(0|l}{Ro;& zCQMu=EW1^2lX--b{U__tz&@d3Ep)y~#T15-EzsHT6@}RcNEhZFVBaat`dozozgHO9 zuxpURZ@aRtvhssgN{Y*Tis1^@c~`imz*UZP-aqH>OWfj9h6sK_z~q71KpkHr_di){ BMo0hv literal 0 HcmV?d00001 diff --git a/version5/krpc-api/target/classes/com/kama/service/UserService.class b/version5/krpc-api/target/classes/com/kama/service/UserService.class new file mode 100644 index 0000000000000000000000000000000000000000..b95b247b554fd4f1f796e1ce58b3e1db379a6635 GIT binary patch literal 257 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 TMjrrRCBK?z0DuG*%D4XkF)NE> literal 0 HcmV?d00001 diff --git a/version5/krpc-api/target/maven-archiver/pom.properties b/version5/krpc-api/target/maven-archiver/pom.properties new file mode 100644 index 0000000..243e555 --- /dev/null +++ b/version5/krpc-api/target/maven-archiver/pom.properties @@ -0,0 +1,5 @@ +#Generated by Maven +#Thu Dec 05 15:18:25 CST 2024 +groupId=com.kama +artifactId=krpc-api +version=1.0-SNAPSHOT diff --git a/version5/krpc-api/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/version5/krpc-api/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 0000000..bf00c34 --- /dev/null +++ b/version5/krpc-api/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -0,0 +1,3 @@ +com\kama\pojo\User$UserBuilder.class +com\kama\pojo\User.class +com\kama\service\UserService.class diff --git a/version5/krpc-api/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/version5/krpc-api/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 0000000..f327769 --- /dev/null +++ b/version5/krpc-api/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1,2 @@ +D:\java_stduy\version5\krpc-api\src\main\java\com\kama\pojo\User.java +D:\java_stduy\version5\krpc-api\src\main\java\com\kama\service\UserService.java diff --git a/version5/krpc-common/pom.xml b/version5/krpc-common/pom.xml new file mode 100644 index 0000000..9434747 --- /dev/null +++ b/version5/krpc-common/pom.xml @@ -0,0 +1,32 @@ + + + 4.0.0 + + com.kama + version5 + 1.0-SNAPSHOT + + + krpc-common + + + com.kama + krpc-api + 1.0-SNAPSHOT + + + cn.hutool + hutool-all + 5.8.10 + compile + + + + 17 + 17 + UTF-8 + + + \ No newline at end of file diff --git a/version5/krpc-common/src/main/java/common/exception/SerializeException.java b/version5/krpc-common/src/main/java/common/exception/SerializeException.java new file mode 100644 index 0000000..33a75a5 --- /dev/null +++ b/version5/krpc-common/src/main/java/common/exception/SerializeException.java @@ -0,0 +1,17 @@ +package common.exception; + +/** + * @ClassName SerializeException + * @Description ToDo + * @Author Tong + * @LastChangeDate 2024-12-02 19:18 + * @Version v1.0 + */ +public class SerializeException extends RuntimeException{ + public SerializeException(String message) { + super(message); + } + public SerializeException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/version5/krpc-common/src/main/java/common/message/MessageType.java b/version5/krpc-common/src/main/java/common/message/MessageType.java new file mode 100644 index 0000000..51f44c0 --- /dev/null +++ b/version5/krpc-common/src/main/java/common/message/MessageType.java @@ -0,0 +1,13 @@ +package common.message; + +import lombok.AllArgsConstructor; + +@AllArgsConstructor +public enum MessageType { + REQUEST(0), RESPONSE(1); + private int code; + + public int getCode() { + return code; + } +} diff --git a/version5/krpc-common/src/main/java/common/message/RpcRequest.java b/version5/krpc-common/src/main/java/common/message/RpcRequest.java new file mode 100644 index 0000000..b44a931 --- /dev/null +++ b/version5/krpc-common/src/main/java/common/message/RpcRequest.java @@ -0,0 +1,31 @@ +package common.message; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * @ClassName RpcRequest + * @Description 定义请求消息格式 + * @Author Tong + * @LastChangeDate 2024-11-29 10:12 + * @Version v5.0 + */ + +@NoArgsConstructor +@AllArgsConstructor +@Data +@Builder +public class RpcRequest implements Serializable { + //接口名、方法名、参数列表参数类型 + private String interfaceName; + + private String methodName; + + private Object[] params; + + private Class[] paramsType; +} diff --git a/version5/krpc-common/src/main/java/common/message/RpcResponse.java b/version5/krpc-common/src/main/java/common/message/RpcResponse.java new file mode 100644 index 0000000..3d0fc81 --- /dev/null +++ b/version5/krpc-common/src/main/java/common/message/RpcResponse.java @@ -0,0 +1,37 @@ +package common.message; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * @ClassName RpcResponse + * @Description 定义响应消息格式 + * @Author Tong + * @LastChangeDate 2024-11-29 10:14 + * @Version v5.0 + */ +@NoArgsConstructor +@AllArgsConstructor +@Data +@Builder +public class RpcResponse implements Serializable { + //状态信息 + private int code; + private String message; + //更新:加入传输数据的类型,以便在自定义序列化器中解析 + private Class dataType; + //具体数据 + private Object data; + + public static RpcResponse sussess(Object data) { + return RpcResponse.builder().code(200).dataType(data.getClass()).data(data).build(); + } + + public static RpcResponse fail(String msg) { + return RpcResponse.builder().code(500).message(msg).build(); + } +} diff --git a/version5/krpc-common/src/main/java/common/serializer/mycoder/MyDecoder.java b/version5/krpc-common/src/main/java/common/serializer/mycoder/MyDecoder.java new file mode 100644 index 0000000..16c2d26 --- /dev/null +++ b/version5/krpc-common/src/main/java/common/serializer/mycoder/MyDecoder.java @@ -0,0 +1,58 @@ +package common.serializer.mycoder; + + +import common.exception.SerializeException; +import common.message.MessageType; +import common.serializer.myserializer.Serializer; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ByteToMessageDecoder; +import lombok.extern.slf4j.Slf4j; + +import java.util.Arrays; +import java.util.List; + +/** + * @ClassName MyDecoder + * @Description 解码器 + * @Author Tong + * @LastChangeDate 2024-11-29 10:32 + * @Version v5.0 + */ +@Slf4j +public class MyDecoder extends ByteToMessageDecoder { + @Override + protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf in, List out) throws Exception { + //检查可读字节数 + if (in.readableBytes() < 6) { // messageType + serializerType + length + return; + } + //1.读取消息类型 + short messageType = in.readShort(); + // 现在还只支持request与response请求 + if (messageType != MessageType.REQUEST.getCode() && + messageType != MessageType.RESPONSE.getCode()) { + log.warn("暂不支持此种数据, messageType: {}", messageType); + return; + } + //2.读取序列化的方式&类型 + short serializerType = in.readShort(); + Serializer serializer = Serializer.getSerializerByCode(serializerType); + if (serializer == null) { + log.error("不存在对应的序列化器, serializerType: {}", serializerType); + throw new SerializeException("不存在对应的序列化器, serializerType: " + serializerType); + } + //3.读取序列化数组长度 + int length = in.readInt(); + if (in.readableBytes() < length) { + return; // 数据不完整,等待更多数据 + } + //4.读取序列化数组 + byte[] bytes = new byte[length]; + in.readBytes(bytes); + log.debug("Received bytes: {}", Arrays.toString(bytes)); + Object deserialize = serializer.deserialize(bytes, messageType); + + out.add(deserialize); + } +} diff --git a/version5/krpc-common/src/main/java/common/serializer/mycoder/MyEncoder.java b/version5/krpc-common/src/main/java/common/serializer/mycoder/MyEncoder.java new file mode 100644 index 0000000..862796f --- /dev/null +++ b/version5/krpc-common/src/main/java/common/serializer/mycoder/MyEncoder.java @@ -0,0 +1,50 @@ +package common.serializer.mycoder; + + +import common.message.MessageType; +import common.message.RpcRequest; +import common.message.RpcResponse; +import common.serializer.myserializer.Serializer; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToByteEncoder; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +/** + * @ClassName MyEncoder + * @Description 编码器 + * @Author Tong + * @LastChangeDate 2024-11-29 10:32 + * @Version v5.0 + */ +@Slf4j +@AllArgsConstructor +public class MyEncoder extends MessageToByteEncoder { + private Serializer serializer; + + @Override + protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception { + log.debug("Encoding message of type: {}", msg.getClass()); + //1.写入消息类型 + if (msg instanceof RpcRequest) { + out.writeShort(MessageType.REQUEST.getCode()); + } else if (msg instanceof RpcResponse) { + out.writeShort(MessageType.RESPONSE.getCode()); + } else { + log.error("Unknown message type: {}", msg.getClass()); + throw new IllegalArgumentException("Unknown message type: " + msg.getClass()); + } + //2.写入序列化方式 + out.writeShort(serializer.getType()); + //得到序列化数组 + byte[] serializeBytes = serializer.serialize(msg); + if (serializeBytes == null || serializeBytes.length == 0) { + throw new IllegalArgumentException("Serialized message is empty"); + } + //3.写入长度 + out.writeInt(serializeBytes.length); + //4.写入序列化数组 + out.writeBytes(serializeBytes); + } +} diff --git a/version5/krpc-common/src/main/java/common/serializer/myserializer/HessianSerializer.java b/version5/krpc-common/src/main/java/common/serializer/myserializer/HessianSerializer.java new file mode 100644 index 0000000..c30c14d --- /dev/null +++ b/version5/krpc-common/src/main/java/common/serializer/myserializer/HessianSerializer.java @@ -0,0 +1,52 @@ +package common.serializer.myserializer; + +import com.caucho.hessian.io.HessianInput; +import com.caucho.hessian.io.HessianOutput; +import common.exception.SerializeException; + + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +/** + * @ClassName HessianSerializer + * @Description Hessian序列化 + * @Author Tong + * @LastChangeDate 2024-11-29 11:49 + * @Version v5.0 + */ +public class HessianSerializer implements Serializer { + @Override + public byte[] serialize(Object obj) { + // 使用 ByteArrayOutputStream 和 HessianOutput 来实现对象的序列化 + try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) { + HessianOutput hessianOutput = new HessianOutput(byteArrayOutputStream); + hessianOutput.writeObject(obj); // 将对象写入输出流 + return byteArrayOutputStream.toByteArray(); // 返回字节数组 + } catch (IOException e) { + throw new SerializeException("Serialization failed"); + } + } + + @Override + public Object deserialize(byte[] bytes, int messageType) { + // 使用 ByteArrayInputStream 和 HessianInput 来实现反序列化 + try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes)) { + HessianInput hessianInput = new HessianInput(byteArrayInputStream); + return hessianInput.readObject(); // 读取并返回对象 + } catch (IOException e) { + throw new SerializeException("Deserialization failed"); + } + } + + @Override + public int getType() { + return 3; + } + + @Override + public String toString() { + return "Hessian"; + } +} \ No newline at end of file diff --git a/version5/krpc-common/src/main/java/common/serializer/myserializer/JsonSerializer.java b/version5/krpc-common/src/main/java/common/serializer/myserializer/JsonSerializer.java new file mode 100644 index 0000000..fab6b97 --- /dev/null +++ b/version5/krpc-common/src/main/java/common/serializer/myserializer/JsonSerializer.java @@ -0,0 +1,78 @@ +package common.serializer.myserializer; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import common.message.RpcRequest; +import common.message.RpcResponse; + + +/** + * @ClassName JsonSerializer + * @Description json序列化 + * @Author Tong + * @LastChangeDate 2024-11-29 10:33 + * @Version v5.0 + */ +public class JsonSerializer implements Serializer { + @Override + public byte[] serialize(Object obj) { + byte[] bytes = JSONObject.toJSONBytes(obj); + return bytes; + } + + @Override + public Object deserialize(byte[] bytes, int messageType) { + Object obj = null; + // 传输的消息分为request与response + switch (messageType){ + case 0: + RpcRequest request = JSON.parseObject(bytes, RpcRequest.class); + Object[] objects = new Object[request.getParams().length]; + // 把json字串转化成对应的对象, fastjson可以读出基本数据类型,不用转化 + // 对转换后的request中的params属性逐个进行类型判断 + for(int i = 0; i < objects.length; i++){ + Class paramsType = request.getParamsType()[i]; + //判断每个对象类型是否和paramsTypes中的一致 + if (!paramsType.isAssignableFrom(request.getParams()[i].getClass())){ + //如果不一致,就行进行类型转换 + objects[i] = JSONObject.toJavaObject((JSONObject) request.getParams()[i],request.getParamsType()[i]); + }else{ + //如果一致就直接赋给objects[i] + objects[i] = request.getParams()[i]; + } + } + request.setParams(objects); + obj = request; + break; + case 1: + RpcResponse response = JSON.parseObject(bytes, RpcResponse.class); + // 如果类型为空,说明返回错误 + if(response.getDataType()==null){ + obj = RpcResponse.fail("类型为空"); + break; + } + Class dataType = response.getDataType(); + //判断转化后的response对象中的data的类型是否正确 + if(!dataType.isAssignableFrom(response.getData().getClass())){ + response.setData(JSONObject.toJavaObject((JSONObject) response.getData(),dataType)); + } + obj = response; + break; + default: + System.out.println("暂时不支持此种消息"); + throw new RuntimeException(); + } + return obj; + } + + //1 代表json序列化方式 + @Override + public int getType() { + return 1; + } + + @Override + public String toString() { + return "Json"; + } +} diff --git a/version5/krpc-common/src/main/java/common/serializer/myserializer/KryoSerializer.java b/version5/krpc-common/src/main/java/common/serializer/myserializer/KryoSerializer.java new file mode 100644 index 0000000..9c6ae18 --- /dev/null +++ b/version5/krpc-common/src/main/java/common/serializer/myserializer/KryoSerializer.java @@ -0,0 +1,81 @@ +package common.serializer.myserializer; + +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.io.Input; +import com.esotericsoftware.kryo.io.Output; + +import com.kama.pojo.User; +import common.exception.SerializeException; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; + + +/** + * @ClassName KryoSerializer + * @Description kryo序列化 + * @Author Tong + * @LastChangeDate 2024-11-29 11:29 + * @Version v5.0 + */ + +public class KryoSerializer implements Serializer { + private Kryo kryo; + + public KryoSerializer() { + this.kryo = new Kryo(); + } + + @Override + public byte[] serialize(Object obj) { + if (obj == null) { + throw new IllegalArgumentException("Cannot serialize null object"); + } + + try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + Output output = new Output(byteArrayOutputStream)) { + + kryo.writeObject(output, obj); // 使用 Kryo 写入对象 + return output.toBytes(); // 返回字节数组 + + } catch (Exception e) { + throw new SerializeException("Serialization failed"); + } + } + + @Override + public Object deserialize(byte[] bytes, int messageType) { + if (bytes == null || bytes.length == 0) { + throw new IllegalArgumentException("Cannot deserialize null or empty byte array"); + } + + try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); + Input input = new Input(byteArrayInputStream)) { + + // 根据 messageType 来反序列化不同的类 + Class clazz = getClassForMessageType(messageType); + return kryo.readObject(input, clazz); // 使用 Kryo 反序列化对象 + + } catch (Exception e) { + throw new SerializeException("Deserialization failed"); + } + } + + @Override + public int getType() { + return 2; + } + + private Class getClassForMessageType(int messageType) { + if (messageType == 1) { + return User.class; // 假设我们在此反序列化成 User 类 + } else { + throw new SerializeException("Unknown message type: " + messageType); + } + } + + @Override + public String toString() { + return "Kryo"; + } +} \ No newline at end of file diff --git a/version5/krpc-common/src/main/java/common/serializer/myserializer/ObjectSerializer.java b/version5/krpc-common/src/main/java/common/serializer/myserializer/ObjectSerializer.java new file mode 100644 index 0000000..eaf13c0 --- /dev/null +++ b/version5/krpc-common/src/main/java/common/serializer/myserializer/ObjectSerializer.java @@ -0,0 +1,60 @@ +package common.serializer.myserializer; + +import java.io.*; + +/** + * @ClassName ObjectSerializer + * @Description JDK序列化方式 + * @Author Tong + * @LastChangeDate 2024-11-29 10:34 + * @Version v5.0 + */ +public class ObjectSerializer implements Serializer { + //利用Java io 对象 -》字节数组 + @Override + public byte[] serialize(Object obj) { + byte[] bytes=null; + ByteArrayOutputStream bos=new ByteArrayOutputStream(); + try { + //是一个对象输出流,用于将 Java 对象序列化为字节流,并将其连接到bos上 + ObjectOutputStream oos = new ObjectOutputStream(bos); + oos.writeObject(obj); + //刷新 ObjectOutputStream,确保所有缓冲区中的数据都被写入到底层流中。 + oos.flush(); + //将bos其内部缓冲区中的数据转换为字节数组 + bytes = bos.toByteArray(); + oos.close(); + bos.close(); + } catch (IOException e) { + e.printStackTrace(); + } + return bytes; + } + + //字节数组 -》对象 + @Override + public Object deserialize(byte[] bytes, int messageType) { + Object obj = null; + ByteArrayInputStream bis = new ByteArrayInputStream(bytes); + try { + ObjectInputStream ois = new ObjectInputStream(bis); + obj = ois.readObject(); + ois.close(); + bis.close(); + } catch (IOException | ClassNotFoundException e) { + e.printStackTrace(); + } + return obj; + } + + //0 代表Java 原生序列器 + @Override + public int getType() { + return 0; + } + + @Override + public String toString() { + return "JDK"; + } +} \ No newline at end of file diff --git a/version5/krpc-common/src/main/java/common/serializer/myserializer/ProtostuffSerializer.java b/version5/krpc-common/src/main/java/common/serializer/myserializer/ProtostuffSerializer.java new file mode 100644 index 0000000..dd97db2 --- /dev/null +++ b/version5/krpc-common/src/main/java/common/serializer/myserializer/ProtostuffSerializer.java @@ -0,0 +1,83 @@ +package common.serializer.myserializer; + + +import com.kama.pojo.User; +import common.exception.SerializeException; +import io.protostuff.LinkedBuffer; +import io.protostuff.ProtostuffIOUtil; +import io.protostuff.Schema; +import io.protostuff.runtime.RuntimeSchema; +/** + * @ClassName ProtostuffSerializer + * @Description protostuff序列化 + * @Author Tong + * @LastChangeDate 2024-11-29 11:55 + * @Version v5.0 + */ +public class ProtostuffSerializer implements Serializer { + + @Override + public byte[] serialize(Object obj) { + // 检查 null 对象 + if (obj == null) { + throw new IllegalArgumentException("Cannot serialize null object"); + } + // 获取对象的 schema + Schema schema = RuntimeSchema.getSchema(obj.getClass()); + + // 使用 LinkedBuffer 来创建缓冲区(默认大小 1024) + LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE); + // 序列化对象为字节数组 + byte[] bytes; + try { + bytes = ProtostuffIOUtil.toByteArray(obj, schema, buffer); + } finally { + buffer.clear(); + } + return bytes; + } + + @Override + public Object deserialize(byte[] bytes, int messageType) { + if (bytes == null || bytes.length == 0) { + throw new IllegalArgumentException("Cannot deserialize null or empty byte array"); + } + + // 根据 messageType 来决定反序列化的类,这里假设 `messageType` 是类的标识符 + Class clazz = getClassForMessageType(messageType); + + // 获取对象的 schema + Schema schema = RuntimeSchema.getSchema(clazz); + + // 创建一个空的对象实例 + Object obj; + try { + obj = clazz.getDeclaredConstructor().newInstance(); + } catch (Exception e) { + throw new SerializeException("Deserialization failed due to reflection issues"); + } + + // 反序列化字节数组为对象 + ProtostuffIOUtil.mergeFrom(bytes, obj, schema); + return obj; + } + + @Override + public int getType() { + return 4; + } + + // 用于根据 messageType 获取对应的类 + private Class getClassForMessageType(int messageType) { + if (messageType == 1) { + return User.class; // 假设我们在此反序列化成 User 类 + } else { + throw new SerializeException("Unknown message type: " + messageType); + } + } + + @Override + public String toString() { + return "Protostuff"; + } +} diff --git a/version5/krpc-common/src/main/java/common/serializer/myserializer/Serializer.java b/version5/krpc-common/src/main/java/common/serializer/myserializer/Serializer.java new file mode 100644 index 0000000..eb4b097 --- /dev/null +++ b/version5/krpc-common/src/main/java/common/serializer/myserializer/Serializer.java @@ -0,0 +1,35 @@ +package common.serializer.myserializer; + + + + +import java.util.HashMap; +import java.util.Map; + +/** + * @InterfaceName Serializer + * @Description 序列化接口 + * @Author Tong + * @LastChangeDate 2024-11-29 10:33 + * @Version v5.0 + */ +public interface Serializer { + byte[] serialize(Object obj); + + Object deserialize(byte[] bytes, int messageType); + + int getType(); + + // 使用 Map 存储序列化器 + static Serializer getSerializerByCode(int code) { + // 静态映射,保证只初始化一次 + Map serializerMap = new HashMap<>(); + serializerMap.put(0, new ObjectSerializer()); + serializerMap.put(1, new JsonSerializer()); + serializerMap.put(2, new KryoSerializer()); + serializerMap.put(3, new HessianSerializer()); + serializerMap.put(4, new ProtostuffSerializer()); + + return serializerMap.get(code); // 如果不存在,则返回 null + } +} diff --git a/version5/krpc-common/src/main/java/common/spi/SpiLoader.java b/version5/krpc-common/src/main/java/common/spi/SpiLoader.java new file mode 100644 index 0000000..e6a5ab9 --- /dev/null +++ b/version5/krpc-common/src/main/java/common/spi/SpiLoader.java @@ -0,0 +1,111 @@ +package common.spi; + + +import cn.hutool.core.io.resource.ResourceUtil; +import common.serializer.myserializer.Serializer; +import lombok.extern.slf4j.Slf4j; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @ClassName SpiLoader + * @Description spi实现 + * @Author Tong + * @LastChangeDate 2024-12-05 14:53 + * @Version v5.0 + */ +@Slf4j +public class SpiLoader { + + // 存储已加载的 SPI 实现类的映射 + private static final Map>> loadedSpiMap = new ConcurrentHashMap<>(); + + // 缓存实例,避免重复实例化 + private static final Map instanceCache = new ConcurrentHashMap<>(); + + // SPI 配置文件的路径 + private static final String SPI_CONFIG_DIR = "META-INF/serializer/"; + + /** + * 加载指定接口的 SPI 实现类 + * + * @param serviceInterface 接口类 + */ + public static void loadSpi(Class serviceInterface) { + String interfaceName = serviceInterface.getName(); + + // 如果已经加载过该接口的 SPI 实现,直接返回 + if (loadedSpiMap.containsKey(interfaceName)) { + return; + } + + Map> keyClassMap = new HashMap<>(); + + // 读取配置文件,获取所有实现类 + List resources = ResourceUtil.getResources(SPI_CONFIG_DIR + serviceInterface.getName()); + for (URL resource : resources) { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(resource.openStream()))) { + String line; + while ((line = reader.readLine()) != null) { + if (!line.trim().isEmpty() && !line.startsWith("#")) { + String[] parts = line.split("="); + if (parts.length == 2) { + String key = parts[0].trim(); + String className = parts[1].trim(); + Class implClass = Class.forName(className); + if (serviceInterface.isAssignableFrom(implClass)) { + keyClassMap.put(key, (Class) implClass); + } + } + } + } + } catch (IOException | ClassNotFoundException e) { + log.error("Failed to load SPI resource: " + resource, e); + } + } + + // 将该接口的 SPI 实现类存入缓存 + loadedSpiMap.put(interfaceName, keyClassMap); + } + + /** + * 根据接口和 key 获取 SPI 实现类实例 + * + * @param serviceInterface 接口类 + * @param key 序列化器的 key + * @param 接口类型 + * @return 对应的 SPI 实现类实例 + */ + public static T getInstance(Class serviceInterface, String key) { + String interfaceName = serviceInterface.getName(); + Map> keyClassMap = loadedSpiMap.get(interfaceName); + + if (keyClassMap == null) { + throw new RuntimeException("SPI not loaded for " + interfaceName); + } + + Class implClass = keyClassMap.get(key); + if (implClass == null) { + throw new RuntimeException("No SPI implementation found for key " + key); + } + + // 从缓存中获取实例,如果不存在则创建 + String implClassName = implClass.getName(); + if (!instanceCache.containsKey(implClassName)) { + try { + instanceCache.put(implClassName, implClass.newInstance()); + } catch (InstantiationException | IllegalAccessException e) { + throw new RuntimeException("Failed to instantiate SPI implementation: " + implClassName, e); + } + } + + return (T) instanceCache.get(implClassName); + } +} diff --git a/version5/krpc-common/src/main/java/common/util/ConfigUtil.java b/version5/krpc-common/src/main/java/common/util/ConfigUtil.java new file mode 100644 index 0000000..8be9a00 --- /dev/null +++ b/version5/krpc-common/src/main/java/common/util/ConfigUtil.java @@ -0,0 +1,48 @@ +package common.util; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.setting.dialect.Props; +import lombok.extern.slf4j.Slf4j; + +/** + * @ClassName ConfigUtil + * @Description 工具 + * @Author Tong + * @LastChangeDate 2024-12-05 11:12 + * @Version v5.0 + */ +@Slf4j +public class ConfigUtil { + + // 加载配置文件,使用默认环境 + public static T loadConfig(Class targetClass, String prefix) { + return loadConfig(targetClass, prefix, ""); + } + + // 加载配置文件,支持指定环境 + public static T loadConfig(Class targetClass, String prefix, String environment) { + StringBuilder configFileNameBuilder = new StringBuilder("application"); + + if (StrUtil.isNotBlank(environment)) { + configFileNameBuilder.append("-").append(environment); + } + configFileNameBuilder.append(".properties"); + + // 加载配置文件 + Props properties = new Props(configFileNameBuilder.toString()); + + if (properties.isEmpty()) { + log.warn("配置文件 '{}' 为空或加载失败!", configFileNameBuilder.toString()); + } else { + log.info("加载配置文件: '{}'", configFileNameBuilder.toString()); + } + + // 返回转化后的配置对象 + try { + return properties.toBean(targetClass, prefix); + } catch (Exception e) { + log.error("配置转换失败,目标类: {}", targetClass.getName(), e); + throw new RuntimeException("配置加载失败", e); + } + } +} diff --git a/version5/krpc-common/target/classes/common/exception/SerializeException.class b/version5/krpc-common/target/classes/common/exception/SerializeException.class new file mode 100644 index 0000000000000000000000000000000000000000..a4b8bf64d1b17c1e9ef67c41f9c1cf27c3ed86ca GIT binary patch literal 561 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 K@E&Sl)SPHjX&R zV~7V7WQDmjY7*eo-M|hz_yO)wFyoXd+_P`O;xis|@I&0kEbH921hX7f{mS}X1vBE{ zI37^QPWu%KJP@y-aPSPaW z(?aHto1Ha@Qs+N$@KgMZSuDAg^Do!jvclnZd_OD3XB-qUMLMK9;^Je}3!<5Ea2Ag! zbW*dF^oc@$Q|0K0<%~afFe{s8J=rl7fJYsims&hPfJb#f$@(uGJcfm2OIdG4-91V- zo^Y@z(b*erxFO;7v7IkCcoI)33~srb-ds>AxzW>pM0F*M+)BjyetdKxMVyW@wUSVo zTG%bYMptMr3l|w*yzB>)%U;!Y%l@nGYMH%}TlUv0Zd9vM?oXk`@Y98}LTgwHLz1WP zV7nCTbGy*lND^{z(XV(HYFn#bbtT!jZ!svjZX14A@*x6?$faq zu5-6&!)Khs`^R+VZmGSbFwh(}?-w2V^!EW3LQ^EyQzp1gMcKrDy{HY|sI=@1Q?=aG z6m_O(bbvZ*UE9vHWw-g1{s|p{aN!Er5=wohWt?Y#(i$Sni+fLVoPGl-+ zO^!li8}sK<_Vvn2Eoq23w_INKBX7#apE#vcOpMClo>Ak+bS6ctb6S^Y)CuN$p;Gax zaUFRit1pG2zacFAP2u<+g4&(i!aq3k_BUn56OuS^GQ6l2ehGsThY|16jEtf9$$3NF zaxAZKofC#~FLL-5E>ZI|-)6b4_&o9%R4@Ls1FNuut{oiWV{ivYKZ?D7&Hvfh?p{1Y z*-3mfVh+#ZIgK}^@p=VMeUsqb+Xm?V6i_dok3-JMrNsAYm2mJTpC+F};#@d-6QhOkn;0uh z+{DR3-nfZVg4mHl?28c}O1v~8{o@Qs7(;OmTcIw=kjn#b zZtpqJCm6j!m-K-K+a8Fcy^Zr&YX*>*tY&59+1SeaahuSzN;jeOUegm|L;8ZKf za2pVX64tbHk&y^@**F)i**p|`4 z$<|u9T|7zmHL{(Jvt92;pF3Q}X1hq^YD=g4b~{KtNu>NrOK{XYBh8VyYh#5EVH!8a zzC`94%#IslUm&wI&YpfKex>m6IDh{X9bcv|@NK$iOni+4dE+beUWYT0H+FEYZa!Bu zX);&L=1sB5eucj4=*pY;*1@xu5X7Cdf_ZFHGI{@;!^@PixXR9}QnK(Cjv}OFBjTRp=(6l@ z(QP5)e1`!`q;6Y%ha-uYON;DMes1gBoZ(Zd(G)UUJ29P_`qe^H2A!mapSn6V4-+Iy zm=}7crj425o~aES14uBk0{mm!ZwyUs#xb#ozHB^8W{W CJAF9- literal 0 HcmV?d00001 diff --git a/version5/krpc-common/target/classes/common/serializer/mycoder/MyDecoder.class b/version5/krpc-common/target/classes/common/serializer/mycoder/MyDecoder.class new file mode 100644 index 0000000000000000000000000000000000000000..44eb61ae71ab33ada344dd8f528898eff34d2ff2 GIT binary patch literal 3033 zcmb7GYf}?f7=8`}HVLaxG}3rMR8$aTy_G6KTS2U}L6jg=>1|04VPTV{n++Q6%}ZO` zO6|q_bjF!ZXVeeYYG)K^r~TNO{*How!gl(e-6e@!I`+fvo;~Nio#%bt_nbffnfwjF zZd~)D0EH4fGKx^lP^TG6l3P|r8B~)ootsM3i1Db>lCgNV(ZN$GHO}{OF=;XsH)~1F z+QU#-Q`_T*7d{Do86}VzDzcXP(*px!NSJ6z4=^k;xf)aZbuMO7wsN~rUM!;&Weh&C z9vL)D%U+B4P>v-MmdXfV8N>3Zkw_RxCE*04xIuMiMmU4i)pqzuTcn#|Wq4t^ke^IG zFC!=dmEzoLrTqvuD`cz`KD=FRkwcvaBW*rZVzq=S8EdeXVV!F;#Z67swUe~1M8*a~ z>BwYZDb$TPLwVRR<4Q^&*fFGpjd+}!Aup;KYNv0U`D=V^`qI7W3#X^={_yzL*!1O} zrpJD)uX48}w$fB}>U}>pV55W@8MWBNP&%ZJs*0{A;%1F7jyP#3|rABBRHKFUI@RaY2OFWgNvZhLRZ1B2E~Q z3Cd~ChD)9jyn#0*yd~ppyu+|K7e`o2i9%Onu~{h1(C(NY{fJ74$>1QaSIp+|el=><=3!lkUW7>1RyOm(D#TZ2X{Ra%TM{J4NI315n?c9Eevw=*p{Y7BG5X$MZD%huMl z{CaW)9itBUQq^I@7*3Dm9~^F<%R~GrMZuAI#QXB(XL>;1lxw0@)%A#Gaq4_uGi?38 zhXP}e+o=pqyESl1@+B(kIZ({ELkU+HD*s!Q`0Kzy_l8QkVgYnQj9_UDr5mIKi=JCokN*kk*xyi?gl$hy%5a+IJruzioTE_zJ|;V#WI3?Ljt%gYSB^vd8LI?L)KTc| h@L(C4{?t}HkI(4qrPa^r`HHp<3WwoaT*g%d{{^W>Sf>C0 literal 0 HcmV?d00001 diff --git a/version5/krpc-common/target/classes/common/serializer/mycoder/MyEncoder.class b/version5/krpc-common/target/classes/common/serializer/mycoder/MyEncoder.class new file mode 100644 index 0000000000000000000000000000000000000000..cbdc4b09db2e596ab663d69dd9d00a9822601e56 GIT binary patch literal 2549 zcma)8TXz#x6#h<{c9LNzP-r1gYM@1u(so2qpur2NsZ>)gO)p}-O{b^nw3C@Ia{>() z@v@eWzUfl`fX|{9!L@Mt;)B1*<#L~ybP`JH@*y+l?Ad#tefjox^4C9)p8$9jUnCJj zhl03KE8lFR%Ok}!LV5({+JEh_{1{DlxIDzLG4qC46a3SjYQf+00nvkyxp0BMisJA>z z@Y1U5QOWuO${HH8HDmQ5)d1VY{$xTqknVL~=8+iq0aT82%Q2$3!d8Yb};Lo(83 z=xdF73z&=f1g02{L?J9kjj;SQuT(`{!sQ(eMTwO}fQTtaNa1A-S8#O;CK5GN)}xul z{LVO0F@utVSy|CJs!W8ZRl`}f343z*O??aRu5{XDgiLl`#RA?{a9zWDSfuuDpUg?a z6s}jN%n9-#VeHWA?Up%eFb~LwVLsDDx=Eoq+}0<;_oXmU0+QexqRp%~EuS7IA_0bY zIE8zPmcuXCDofm(HI{6eOX4}xu;&e8MA|omaj|Miq_)LRK$A_vr0_dTvBB`aLnniZ zA5d&_q#CfGPi~E7KDA>q4BZj58l8_V<{5UEgki2v8`U5M1#1M4XfS;RE)j0?xabSR zSf*{3(IK85LDUOMnYduujd@f{Zp}0Kq$Mla9o->g@*raGyz2^Ict&-ai&b~o-_wPg zDsEvzL0!_sZHA#{OqO%gUFUi@)**95p!&9Tgc^#W(-0Q^P|d*H29(AQh3Tx zPHY?X#TNaoS#1$DTud0YT_TF6JMk_JLwhP^4B8Yy*b7LGQxonJrk+W-T^4*Z7G*X8ChUIHHC?C+z)KO^@DuQNy| zax?M>Z^kelPsRU4G8KRFJ$6g0o=br`2{!Te!e}bKiRpzOgLprp=Qz6Q*-y^_^wLE- zh%|;N%X74v#1JvjNpNIwgQR!qZu=5r_=+y9uj$VCm@LnPS;D{)?I>TESOyDL94k}} zDJx@@>T!eaVhd|jr*)FjAEm(q1Ak*kft^H!ByqBH=yx7fdQugsuCc&_I{XmVhEM&1 zoQymM8F^Q5N?#B~U#JyDp~@SIz9;C+xrVP}fj*GziT#TCpTMLm*5ay{Li7UnJ_NlJ U^tDF6JGe(Xl~$kNQ+$rpzak8`T>t<8 literal 0 HcmV?d00001 diff --git a/version5/krpc-common/target/classes/common/serializer/myserializer/HessianSerializer.class b/version5/krpc-common/target/classes/common/serializer/myserializer/HessianSerializer.class new file mode 100644 index 0000000000000000000000000000000000000000..e54e9e9af269116266f4e1335bcfbf6d8ec9bc62 GIT binary patch literal 2191 zcmah~Sy$Ue7`qr$CeRu}fGmw$mbRv(4&8*Uf~alz7`Z9^LwV@~ zeZl91bIN;vROuZ_7NW@sFSC5Q-*WGKGy3P>w|@h81U-%r!YY&mB4`u1eW?GYYh}H< zuWjue8bw>6ZON>f_Od`YmEKVi6}aPoOiNokwvE+#T|eGx*tLe8x9f&pi9?kU+XY68 zRz)l7jpBi&9T-PPre4+P^}=1cK5@ixLngl|kW8(&A_oI5rgtRjb^;%vL!kY2-L#EX zb={s%pkxQHqAP)J^bl`bew_kJD!scVOYBP^DH%tKW$VaLF(A-+Nq+mFZoSs`%0?Xh zxRbzL+!MH=mrD6YtyU)iqa@H5%8 z86!jfsogbMu_iF`!Sizbp9%ClHI6(bf{deB6i~gcQSnINJ`0r!w$Mghs~iV59=0W)QCIO;AbpM3C7f;Y z#3~sAx7N+7vDv8X8TD=14FVnOR#7kS=sYm)J)>eDm`4J0>(^v>r!EqU`E^r(U_-g0 zEwWwNc?ZEeBQP#AU3aw@ynF9`H&o!Bf6@H*xN_qJ1i5zms6Y!dYS`L4luBG)7q3

MM-A1MR&ve$C&X#s(RN6={`fw2$sb5Ja#%cmD;K!Lk{-9Ed(clt%7 z9}g@fwQa9ZTYcZyKCZDukyG)K?Y3{&u7R&!P9SPq?)_rYD^2X(IyQRVYSfFybKX%N zNNi@NPjPGJ6rEXxV{cM9*_87ev}ct}QaQz7=A7t} zLdRGW6X#G6#UMuc=#3$TY0kh9Z}Jdr51l#e)HQ(^zw{E_$5%Ot9wG-ZKwLkOy+jUR zo)%g61J=fu8=vC^>x?3U9KImGc|Pi2Vx22i?y-`q4Q!Iv7OhKAE*Oq~lO+`)m9Mr? z(SN~N@l7;1R`r5T#7Lwx3~~DvzUH_>h3+zQ8zZisAzhemVtVrovxQ6(_p^$W;gf76 zbBcvzuSP-@`l;4s(>VE71t4g4M?; zWhcgon;>qSs8OOOuz*QNo@Ex+iJSNadCDK7Jlpt|F%~>|!m?~%o>lDNB}qx!cPNmn z6zZ`QYS>fd71^gfrJ{&9k~p@ORM88%xuU*e*ST8l;(J#s%y5 literal 0 HcmV?d00001 diff --git a/version5/krpc-common/target/classes/common/serializer/myserializer/JsonSerializer.class b/version5/krpc-common/target/classes/common/serializer/myserializer/JsonSerializer.class new file mode 100644 index 0000000000000000000000000000000000000000..a9726a38c614041f8cbb1d0707f5191a54544c8c GIT binary patch literal 2794 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 literal 0 HcmV?d00001 diff --git a/version5/krpc-common/target/classes/common/serializer/myserializer/ProtostuffSerializer.class b/version5/krpc-common/target/classes/common/serializer/myserializer/ProtostuffSerializer.class new file mode 100644 index 0000000000000000000000000000000000000000..8cf83ad47d3f35ab97aa123e6f2aa99c8aa5e968 GIT binary patch literal 3186 zcmai0TUQfT7~LlqCWI&&6&n=^3KfVLFSROIte_|gc)?38y*MO?Fff@(CleKWzyF1< zKK0F)KG+v@>8if9YxU3c54dXgnHiD^vUIV?IdkT_>~DXUGynYe)Hl|WKp*O+cuwwtX=$IwmVzRX%R)6CkQ;UqMql-CUc+xjLOCTCp7uu8-IHx#t? z6gFYAK%ykwF;lNr1-A6{hors3;Uu5%Lq=dn^Q%e2T9U<4`jSo(Z((N&yUa>xiH_RtsX>cBW*ts+Z)o zlb=fshXmT!oF;J)M^bnPM}2c=WWm%OSsb&is_WDWu1!Bj`et9T7Pjxmd6R9D4=FL1 z$8cQ3i4=w~EYR8TyN)zdY00}2bg5ef;@{WgFvw-tV{oM5J@!JwbiMU7oMHzAq{`KX z%-6}Q3np+{Ab+NwFvO=1KoRjg-HIE!(1QCT`A zIqukHfx*7n(GXj&Ekrd-&I_aoG{2;m^?b!%wDZ@Q_%tWeg%mDgQeb;oUy|gOO?1<6 z7d)1A%jFo@)|}^HY9uf%kZhEUhRXu|Z>X?3HOzNx%q}tu?JRLQT`SK?=bAoe@+3XU zPBw4o9A$hC4rA_uK`cin-{7GEsNsv1(aLE3$;b*^r-j*&Y(v#KbP`byQG0GtVQJ(& zAenQz8qBqhHQgEvE=tUK6N-a*jYLyF3v8Hi^}^DWUh$&TFvqTH9ss^c4aKGsytd%j zcNIGtZn3OGvw(?YX*4xv9k(F4+d=`=>yrevmBXzqCcL4f-@h-gIi%tDI4NvS5ywH0 zrmbs*PY#UGy2cse4-H;zwB0&mlq}t?IYhL*xx73VzE;Au{bEzICft}o39|lX?3z=M z<7^~>%zC*$pz_DtcGR|A&KkWkCEW$PSZ!~?{Ukoe7aG1)9q^SvPb1Zab;n+kdEbxU z6yv%YQ@jCw371bG=o>w+TSZe=dnRprsa6>dQ4s(Roir&bYSs}C&%+>zVHN@tW4!ui z3|9^(@D16#ZY^2%T`L>pGwag!P*w<}CM-)j9=)=v;d_DJR{~xy4h=u@W_rCSE8Y(p z-Mn%l5V*wA(!!PBI{dxDr#x4|H9qhN;ty^jxEcrnn=!+E$``;jT<4R(4cz3fQvMfF z8UluLe`CW_Xt&a>kI?o!w}Oq2k)GNd!DH;0&g~z3jNL=A!4>q}e1Hu@ac`H2KhMM- z(qAWfF-R+e1f$ldhhP9n?mG~}c5K8B?mGF`P1(H|Ko2GN^6Kj0RnzOC93hfkB=7-l zQHPiUylVF$W{J3i(jVbt&toS(!Kd_ejQ_sIZR&I(y747;Bc>szA)-Oo_&p#r6kecJ zgQSspl>FcYh4~4`QJwn>iQKba0|xpO0|UEOFfh%q(+oTMCl0RQFqg;*-W`fRK>JY3 zQ;gh7zyAoM&vTi0CiWO(nfOCm?oyXBPBH|Q2A_lhv@(BNNKXcbDRGQ4LzFs>qd0+6 z{ER)vTS=wrDhW79Tq@u=-zKpD!%NYHIvp3hfK|>fU=d5CBZEUQQKnuO{V0lJwAvO} zu_&oZz?ZTqb(<&Mh_$x9q(a=|N|L05$bQs~Rzs9$G NN^#f%mc@k@<+9?X7cZh+Km!881}i_pLK>svPTkX!&k;J?UkHX z{Ab~1>VD5J*-&-;&97?^GTT6L5sM`bBFmqKqlhdliQfFLkDK#>*< z53%9SCCIcB#U>unZ}6DbzaSt)bN>SD;}?V*#qd{%7f6tiJjYxZABuvDqzJj`BQq|t zOvs9lL|i08$eNFcE|Mi=-AB@doa1%~Kal%OoMCLyxCW8NG<`xVG-gQaDyoF;A&VB) j@q~6$o<=;yGqPrJg6G(#3|^2W(d;2FX~j?`EQHD*^SWh_ literal 0 HcmV?d00001 diff --git a/version5/krpc-common/target/classes/common/spi/SpiLoader.class b/version5/krpc-common/target/classes/common/spi/SpiLoader.class new file mode 100644 index 0000000000000000000000000000000000000000..f8829d46f18ea29ea6471440faa28517c171a97d GIT binary patch literal 5133 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 lpYmN#^?ydnU&-fhrH2 literal 0 HcmV?d00001 diff --git a/version5/krpc-common/target/classes/common/util/ConfigUtil.class b/version5/krpc-common/target/classes/common/util/ConfigUtil.class new file mode 100644 index 0000000000000000000000000000000000000000..7fb59017dc3665e25b24aa0326bc52ab7a038efb GIT binary patch literal 2372 zcmbtV-A@!(6#rdVoMjzAmJda-0%Bp8kD=C9Ws$aQ)zYr`!H0!@=rCNC!P%L0b`~^- zhdwkmO?@>neKAd&rb%P8Z2+OP@BKRz=u`gz)AZb(k-Zh#J~W$|z4y%d_?_Q5_uSw9 zT=@mSDa=Puf>H$`6=m4PuxD1E(=|)C)7tsTS#Ed?yZTJq^!gb}+hb#4gc%T_-);q} ziV9RR)EiDFGc7IY*wbcul$Hz;%h9KN0mEQ>s^~mv>A74&5)6B;X{UFP#3Usj zQbNHVhP_2+zU@HXw5GTlMwDTQvE%ZU)87oz7&vn}w=RlvBNXRJ?+tw6~lz zLp0^MX)R|>ADh)uPCCt9LV1iK{`;qQHr5`j-@W~0b+M`K) z`bnSk_zKF16-$A$K_c6)TAY*{UW%CF>$Ca zQ_cT9`pAsy%hgX75oTs}4BjCSUqR$6G zZIgBRw0T2BC{h^kPh&CV$YF10pNFoOv%hPieFwtem?ocTTKm4k^lgTACOsvXEySqw zW3Uqhco)eRR#7z`tzJUSQ0Fr0@1e3Q+PH|9I_bIG6>SlJt7skXTExpsi1&sXLW}6Q zhuR;YMPC)t$9Sz2-=bOw629P6DH=lYjzzrru(wRe&%_t;PD9yaoC{$U7ssQ+4P{Fh z{gvQerj)PXZGs~RI!e%ieZ=m5RO0{|aS(^;66`?>PS9!)N5~>hx*bGZC$7`AbCoRm zDH)x#ufd`!P+D8GECjR&S%wHIA>TRJaEQ_#oI{p;ge200r6A3vrv{;CXjD-344n#E pE6yqe)MqnCe0y8vZZ6c4su+zd + + 4.0.0 + + com.kama + version5 + 1.0-SNAPSHOT + + + krpc-consumer + + + 17 + 17 + UTF-8 + + + + org.springframework.boot + spring-boot-starter-web + RELEASE + compile + + + com.kama + krpc-api + 1.0-SNAPSHOT + compile + + + com.kama + krpc-common + 1.0-SNAPSHOT + + + com.kama + krpc-core + 1.0-SNAPSHOT + + + cn.hutool + hutool-all + 5.8.10 + + + + \ No newline at end of file diff --git a/version5/krpc-consumer/src/main/java/com/kama/consumer/ConsumerTest.java b/version5/krpc-consumer/src/main/java/com/kama/consumer/ConsumerTest.java new file mode 100644 index 0000000..c04606a --- /dev/null +++ b/version5/krpc-consumer/src/main/java/com/kama/consumer/ConsumerTest.java @@ -0,0 +1,66 @@ +package com.kama.consumer; + +import com.kama.client.proxy.ClientProxy; +import com.kama.pojo.User; + +import com.kama.service.UserService; +import lombok.extern.slf4j.Slf4j; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * @ClassName ConsumerExample + * @Description 客户端测试 + * @Author Tong + * @LastChangeDate 2024-12-05 16:20 + * @Version v5.0 + */ +@Slf4j +public class ConsumerTest { + + private static final int THREAD_POOL_SIZE = 20; + private static final ExecutorService executorService = Executors.newFixedThreadPool(THREAD_POOL_SIZE); + + public static void main(String[] args) throws InterruptedException { + ClientProxy clientProxy = new ClientProxy(); + UserService proxy = clientProxy.getProxy(UserService.class); + for (int i = 0; i < 120; i++) { + final Integer i1 = i; + if (i % 30 == 0) { + // Simulate delay for every 30 requests + Thread.sleep(10000); + } + + // Submit tasks to executor service (thread pool) + executorService.submit(() -> { + try { + User user = proxy.getUserByUserId(i1); + if (user != null) { + log.info("从服务端得到的user={}", user); + } else { + log.warn("获取的 user 为 null, userId={}", i1); + } + + Integer id = proxy.insertUserId(User.builder() + .id(i1) + .userName("User" + i1) + .gender(true) + .build()); + + if (id != null) { + log.info("向服务端插入user的id={}", id); + } else { + log.warn("插入失败,返回的id为null, userId={}", i1); + } + } catch (Exception e) { + log.error("调用服务时发生异常,userId={}", i1, e); + } + }); + } + + // Gracefully shutdown the executor service + executorService.shutdown(); + } + +} diff --git a/version5/krpc-consumer/src/main/java/com/kama/consumer/ConsumerTestConfig.java b/version5/krpc-consumer/src/main/java/com/kama/consumer/ConsumerTestConfig.java new file mode 100644 index 0000000..b816281 --- /dev/null +++ b/version5/krpc-consumer/src/main/java/com/kama/consumer/ConsumerTestConfig.java @@ -0,0 +1,20 @@ +package com.kama.consumer; + + +import com.kama.config.KRpcConfig; +import common.util.ConfigUtil; + +/** + * @ClassName ConsumerTestConfig + * @Description 测试配置顶 + * @Author Tong + * @LastChangeDate 2024-12-05 11:29 + * @Version v1.0 + */ +public class ConsumerTestConfig { + public static void main(String[] args) { + KRpcConfig rpc = ConfigUtil.loadConfig(KRpcConfig.class, "rpc"); + System.out.println(rpc); + } + +} diff --git a/version5/krpc-consumer/src/main/resources/application.properties b/version5/krpc-consumer/src/main/resources/application.properties new file mode 100644 index 0000000..e784008 --- /dev/null +++ b/version5/krpc-consumer/src/main/resources/application.properties @@ -0,0 +1,7 @@ +rpc.name=krpc +rpc.version=1.0.0 +rpc.port=9999 +rpc.serializer=Hessian +rpc.host=localhost +rpc.registry=zookeeper +rpc.loadBalance=ConsistencyHash \ No newline at end of file diff --git a/version5/krpc-consumer/target/classes/application.properties b/version5/krpc-consumer/target/classes/application.properties new file mode 100644 index 0000000..e784008 --- /dev/null +++ b/version5/krpc-consumer/target/classes/application.properties @@ -0,0 +1,7 @@ +rpc.name=krpc +rpc.version=1.0.0 +rpc.port=9999 +rpc.serializer=Hessian +rpc.host=localhost +rpc.registry=zookeeper +rpc.loadBalance=ConsistencyHash \ No newline at end of file diff --git a/version5/krpc-consumer/target/classes/com/kama/consumer/ConsumerTest.class b/version5/krpc-consumer/target/classes/com/kama/consumer/ConsumerTest.class new file mode 100644 index 0000000000000000000000000000000000000000..03a4c00146356bb53eb0e9797fa1d705f3071b52 GIT binary patch literal 3941 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 literal 0 HcmV?d00001 diff --git a/version5/krpc-consumer/target/krpc-consumer-1.0-SNAPSHOT.jar b/version5/krpc-consumer/target/krpc-consumer-1.0-SNAPSHOT.jar new file mode 100644 index 0000000000000000000000000000000000000000..a2d7d283404a8cdefc8fe76221d6ec2b3b83ea01 GIT binary patch literal 3110 zcmb7G2|Scr8y{mCF-eRu)=Nc<*_shIu1ZCi8K$u%bu*2zWH8@wiRO}BO=Yd5uCiwh zxw;A^HxXm#wn7{q>#qci!dqKF|OB&w0-IKM#c{DJ2IIHd=U` z+n1NQ+cMxuwsNvW+uB=WDRXABAP>Ndr;$3r1b#;W9k$qvY-w+6ZRO~MAzM$9+uKM) zG^T4g5smC<=U<}Y?h>93vQc&&Ehr*-i!5Xy&m|phbbZZ%+Ou4$GLh;S{2Q_irYWzK zMxd&2l-sD2u%qwV-$KJ`&)N}!?5{424Gn1Ko)}? z?8^u;LfYJKBes)&=V6dGLxW(Ya7K~U&C?&kYT^NgGK$&JpK_Gki8_Z^Tt_ZmIS5}u zQjJ!M0q)fX<`^1^r63aA?9Q z{u+NLozigoeM0g>J4jU}Ecv{Wqm+^r!-E~YEdI}OO^Cpqq$3jn+9XFiW8;}P#wxrT z`o8x`>)yT}&Vi0syrF~BwAKAd*|rs9o6q?qQ&Xf=yCk{mEhm%Z%afMrUp=^b1v>i8 zPOrw*JRe;0DlERhzPUgBmQ#=+N02ez89;{zNm&%7>il4YNj0}3Orh@46>W=RvWYHc z$D)x(k6n4z2a8D`3Y#fNRomb5pill)2Hnv3U{^?mc^2%5wPA@3G~(GtOS(>=S|g`L z+8kSzSY+f*>|Dkm{k78haWkVTdo;|xLV5afErVxZs!>EUs?8i-yui zgcq6Lh54gty__arZ#e77$(o=}6F=LBkkdn-YYewqUW=8G4uc_(n^JZdFw0^#E6Q{@eV9Fy z_Ne$e-Q9ks?997^Q-mOvH1bp)?S1~%nN84w8l-54x@&;frY&#^I}2M4VmHiGt{H}o7) ziOR`IfN3R{-@F5_N8-=;RO(r#_P-u2Y(1_82~e*-^+A6kg_Fe5cfGnQsm7y}!d6e( zp}t+G_IB)Ua)05iGXwaBoYgtUJ}!T3rHy`L{IhxCT7)W7UTGaqpq`93`oxM%xR!l4 zRL$ydY&t2#rJCXFoc+?zK#*EM|HL-kD^QRo##SG0`}k8DBHW;3`(%C!;f$( zy5C7RLK+pi^NOx*s}h8LuK1%qa?OFB+(Yj*t#9#6ttVrReA~dJ$SqC12Fo|fO?i0L z>+$z;HKA3-#+ok#=8a9KO^vh&My#zT$OR=#Xxz#CmP-ZWrR%hpH%#IkXj3IWDs97e z>JUbsH8h)iDwLn<7=98}YSQqogeL8Do7qV*-tYgFM4n+?x5@R z`ZN4;)l0dOmvL?NKR!aGJpI`1dt+-(uWR^?3o#MdvpYbi!wk zS^fm!#r^BCgCEp5*iJq5Lhp4JiLuu_?@s%@U9>5L*&b<()d>YQLkQ4~|F2CU%6^U^ z4m={-4rNQ;VN>ihMn(~N{ex76Rs(02E!hQ!bH*9}j<2r9<0vczg&jCN4*zV>4tpDm z9wSwfJNWn7?Ayr$gLWunD~U+LNXU7qs;qgXKRm2*YN!A{&OVV3SMeSi?pA-P_Cn_G z4Kh*(1qowp2_SDufMJr~E?j`w08J6}#!~oPIFur{r3)FMv9RSutp)B2Zi~tq5rX*P z6c!t^O*rsP;B~h2_+F~Ra~^XVFz>c>F)}w2Kve-a*GrcsBC<=@7b1*E0AryF`ewE9 zHQv{$PK37&NPc2?Unf2hocLM_wRE;Af@Ws$(u<-oudMUyBknBJR*~~wapy%!`)w4V z){0P0{0qw0$~%v_wD!)U=0$aXH|CrYi&ktlpkJS05%dT|Y{eEP{ZG0=XwiA55T$_> R0RlmR%MA$PWm)0Z{{ZksFq8lQ literal 0 HcmV?d00001 diff --git a/version5/krpc-consumer/target/maven-archiver/pom.properties b/version5/krpc-consumer/target/maven-archiver/pom.properties new file mode 100644 index 0000000..96d4f06 --- /dev/null +++ b/version5/krpc-consumer/target/maven-archiver/pom.properties @@ -0,0 +1,5 @@ +#Generated by Maven +#Thu Dec 05 15:19:00 CST 2024 +groupId=com.kama +artifactId=krpc-consumer +version=1.0-SNAPSHOT diff --git a/version5/krpc-consumer/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/version5/krpc-consumer/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 0000000..e3ec4a4 --- /dev/null +++ b/version5/krpc-consumer/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -0,0 +1,2 @@ +com\kama\ConsumerTestConfig.class +com\kama\ConsumerApplication.class diff --git a/version5/krpc-consumer/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/version5/krpc-consumer/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 0000000..08a64ca --- /dev/null +++ b/version5/krpc-consumer/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1,2 @@ +D:\java_stduy\version5\krpc-consumer\src\main\java\com\kama\ConsumerTestConfig.java +D:\java_stduy\version5\krpc-consumer\src\main\java\com\kama\ConsumerApplication.java diff --git a/version5/krpc-core/pom.xml b/version5/krpc-core/pom.xml new file mode 100644 index 0000000..f0a7710 --- /dev/null +++ b/version5/krpc-core/pom.xml @@ -0,0 +1,114 @@ + + + 4.0.0 + + com.kama + version5 + 1.0-SNAPSHOT + + + krpc-core + + + 17 + 17 + UTF-8 + + + + com.kama + krpc-common + 1.0-SNAPSHOT + + + org.projectlombok + lombok + 1.18.30 + compile + + + io.netty + netty-all + 4.1.51.Final + compile + + + + org.apache.curator + curator-recipes + 5.1.0 + + + com.alibaba + fastjson + 1.2.83 + + + com.esotericsoftware + kryo + 4.0.2 + + + com.caucho + hessian + 4.0.66 + + + io.protostuff + protostuff-core + 1.7.4 + + + io.protostuff + protostuff-runtime + 1.7.4 + + + com.github.rholder + guava-retrying + 2.0.0 + + + org.jetbrains + annotations + 17.0.0 + compile + + + com.kama + krpc-api + 1.0-SNAPSHOT + + + cn.hutool + hutool-all + 5.8.10 + + + + org.junit.jupiter + junit-jupiter-api + 5.11.3 + test + + + org.junit.jupiter + junit-jupiter-engine + 5.11.3 + test + + + + org.junit.platform + junit-platform-launcher + 1.11.3 + test + + + junit + junit + + + \ No newline at end of file diff --git a/version5/krpc-core/src/main/java/com/kama/KRpcApplication.java b/version5/krpc-core/src/main/java/com/kama/KRpcApplication.java new file mode 100644 index 0000000..9529f74 --- /dev/null +++ b/version5/krpc-core/src/main/java/com/kama/KRpcApplication.java @@ -0,0 +1,48 @@ +package com.kama; + + +import com.kama.config.KRpcConfig; +import com.kama.config.RpcConstant; +import common.util.ConfigUtil; +import lombok.extern.slf4j.Slf4j; + +/** + * @ClassName RpcApplication + * @Description 测试配置顶,学习更多参考Dubbo + * @Author Tong + * @LastChangeDate 2024-12-05 11:22 + * @Version v5.0 + */ +@Slf4j +public class KRpcApplication { + private static volatile KRpcConfig rpcConfigInstance; + + public static void initialize(KRpcConfig customRpcConfig) { + rpcConfigInstance = customRpcConfig; + log.info("RPC 框架初始化,配置 = {}", customRpcConfig); + } + + public static void initialize() { + KRpcConfig customRpcConfig; + try { + customRpcConfig = ConfigUtil.loadConfig(KRpcConfig.class, RpcConstant.CONFIG_FILE_PREFIX); + log.info("成功加载配置文件,配置文件名称 = {}", RpcConstant.CONFIG_FILE_PREFIX); // 添加成功加载的日志 + } catch (Exception e) { + // 配置加载失败,使用默认配置 + customRpcConfig = new KRpcConfig(); + log.warn("配置加载失败,使用默认配置"); + } + initialize(customRpcConfig); + } + + public static KRpcConfig getRpcConfig() { + if (rpcConfigInstance == null) { + synchronized (KRpcApplication.class) { + if (rpcConfigInstance == null) { + initialize(); // 确保在第一次调用时初始化 + } + } + } + return rpcConfigInstance; + } +} diff --git a/version5/krpc-core/src/main/java/com/kama/client/cache/ServiceCache.java b/version5/krpc-core/src/main/java/com/kama/client/cache/ServiceCache.java new file mode 100644 index 0000000..8eba240 --- /dev/null +++ b/version5/krpc-core/src/main/java/com/kama/client/cache/ServiceCache.java @@ -0,0 +1,75 @@ +package com.kama.client.cache; + +import lombok.extern.slf4j.Slf4j; + +import java.util.ArrayList; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @ClassName serviceCache + * @Description 建立本地缓存 + * @Author Tong + * @LastChangeDate 2024-12-02 10:34 + * @Version v5.0 + */ +@Slf4j +public class ServiceCache { + //key: serviceName 服务名 + //value: addressList 服务提供者列表 + private static Map> cache = new ConcurrentHashMap<>(); + + //添加服务 + public void addServiceToCache(String serviceName, String address) { + if (cache.containsKey(serviceName)) { + List addressList = cache.get(serviceName); + addressList.add(address); + log.info("有服务名情况,将name为{}和地址为{}的服务添加到本地缓存中", serviceName, address); + } else { + List addressList = new ArrayList<>(); + addressList.add(address); + cache.put(serviceName, addressList); + log.info("无服务名情况,将name为{}和地址为{}的服务添加到本地缓存中", serviceName, address); + } + } + + //修改服务地址 + public void replaceServiceAddress(String serviceName, String oldAddress, String newAddress) { + if (cache.containsKey(serviceName)) { + List addressList = cache.get(serviceName); + addressList.remove(oldAddress); + addressList.add(newAddress); + log.info("将服务{}的地址{}替换为{}", serviceName, oldAddress, newAddress); + } else { + log.error("旧地址{}不在服务{}的地址列表中", oldAddress, serviceName); + } + } + + //从缓存中取服务地址列表 + public List getServiceListFromCache(String serviceName) { + if (!cache.containsKey(serviceName)) { + log.warn("服务{}未找到", serviceName); + //返回个不可修改的空列表,避免调用的时候出现空指针异常 + return Collections.emptyList(); + } + return cache.get(serviceName); + } + + //从缓存中删除服务地址 + public void delete(String serviceName, String address) { + List addressList = cache.get(serviceName); + if (addressList != null && addressList.contains(address)) { + addressList.remove(address); + log.info("将name为{}和地址为{}的服务从本地缓存中删除", serviceName, address); + if (addressList.isEmpty()) { + cache.remove(serviceName); // 移除该服务的缓存条目 + log.info("服务{}的地址列表为空,已从缓存中清除", serviceName); + } + } else { + log.warn("删除失败,地址{}不在服务{}的地址列表中", address, serviceName); + } + } +} diff --git a/version5/krpc-core/src/main/java/com/kama/client/circuitbreaker/CircuitBreaker.java b/version5/krpc-core/src/main/java/com/kama/client/circuitbreaker/CircuitBreaker.java new file mode 100644 index 0000000..53a6489 --- /dev/null +++ b/version5/krpc-core/src/main/java/com/kama/client/circuitbreaker/CircuitBreaker.java @@ -0,0 +1,109 @@ +package com.kama.client.circuitbreaker; + +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @ClassName CircuitBreaker + * @Description 熔断器的状态 + * @Author Tong + * @LastChangeDate 2024-12-02 10:45 + * @Version v5.0 + */ +@Slf4j +public class CircuitBreaker { + //当前状态 + private CircuitBreakerState state = CircuitBreakerState.CLOSED; + private AtomicInteger failureCount = new AtomicInteger(0); + private AtomicInteger successCount = new AtomicInteger(0); + private AtomicInteger requestCount = new AtomicInteger(0); + //失败次数阈值 + private final int failureThreshold; + //半开启-》关闭状态的成功次数比例 + private final double halfOpenSuccessRate; + //恢复时间 + private final long retryTimePeriod; + //上一次失败时间 + private long lastFailureTime = 0; + + public CircuitBreaker(int failureThreshold, double halfOpenSuccessRate, long retryTimePeriod) { + this.failureThreshold = failureThreshold; + this.halfOpenSuccessRate = halfOpenSuccessRate; + this.retryTimePeriod = retryTimePeriod; + } + + //查看当前熔断器是否允许请求通过 + public synchronized boolean allowRequest() { + long currentTime = System.currentTimeMillis(); + log.info("熔断前检查, 当前失败次数:{}", failureCount); + switch (state) { + case OPEN: + if (currentTime - lastFailureTime > retryTimePeriod) { + state = CircuitBreakerState.HALF_OPEN; + resetCounts(); + log.info("熔断已解除,进入半开启状态,允许请求通过"); + return true; + } + log.warn("熔断生效中,拒绝请求!"); + return false; + case HALF_OPEN: + requestCount.incrementAndGet(); + log.info("当前为半开启状态,计数请求"); + return true; + case CLOSED: + default: + log.info("当前为正常状态,允许请求通过"); + return true; + } + } + + //记录成功 + public synchronized void recordSuccess() { + if (state == CircuitBreakerState.HALF_OPEN) { + successCount.incrementAndGet(); + if (successCount.get() >= halfOpenSuccessRate * requestCount.get()) { + state = CircuitBreakerState.CLOSED; + resetCounts(); + log.info("成功次数已达到阈值,熔断器切换至关闭状态"); + } + } else { + resetCounts(); + log.info("熔断器处于关闭状态,重置计数器"); + } + } + + //记录失败 + public synchronized void recordFailure() { + failureCount.incrementAndGet(); + log.error("记录失败,当前失败次数:{}", failureCount); + lastFailureTime = System.currentTimeMillis(); + + if (state == CircuitBreakerState.HALF_OPEN) { + state = CircuitBreakerState.OPEN; + lastFailureTime = System.currentTimeMillis(); + log.warn("半开启状态下发生失败,熔断器切换至开启状态"); + } else if (failureCount.get() >= failureThreshold) { + state = CircuitBreakerState.OPEN; + log.error("失败次数已超过阈值,熔断器切换至开启状态"); + } + } + + //重置次数 + private void resetCounts() { + failureCount.set(0); + successCount.set(0); + requestCount.set(0); + } + + public CircuitBreakerState getState() { + return state; + } +} + +enum CircuitBreakerState { + //关闭,开启,半开启 + CLOSED, OPEN, HALF_OPEN +} diff --git a/version5/krpc-core/src/main/java/com/kama/client/circuitbreaker/CircuitBreakerProvider.java b/version5/krpc-core/src/main/java/com/kama/client/circuitbreaker/CircuitBreakerProvider.java new file mode 100644 index 0000000..f94bf5c --- /dev/null +++ b/version5/krpc-core/src/main/java/com/kama/client/circuitbreaker/CircuitBreakerProvider.java @@ -0,0 +1,28 @@ +package com.kama.client.circuitbreaker; + +import lombok.extern.slf4j.Slf4j; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @ClassName CircuitBreakerState + * @Description 提供熔断器 + * @Author Tong + * @LastChangeDate 2024-12-02 10:47 + * @Version v5.0 + */ +@Slf4j +public class CircuitBreakerProvider { + // 使用线程安全的 ConcurrentHashMap + private Map circuitBreakerMap = new ConcurrentHashMap<>(); + + public synchronized CircuitBreaker getCircuitBreaker(String serviceName) { + // 使用 computeIfAbsent,避免手动同步 + return circuitBreakerMap.computeIfAbsent(serviceName, key -> { + log.info("服务 [{}] 不存在熔断器,创建新的熔断器实例", serviceName); + // 创建并返回新熔断器 + return new CircuitBreaker(1, 0.5, 10000); + }); + } +} diff --git a/version5/krpc-core/src/main/java/com/kama/client/netty/NettyClientHandler.java b/version5/krpc-core/src/main/java/com/kama/client/netty/NettyClientHandler.java new file mode 100644 index 0000000..15c98ff --- /dev/null +++ b/version5/krpc-core/src/main/java/com/kama/client/netty/NettyClientHandler.java @@ -0,0 +1,34 @@ +package com.kama.client.netty; + +import common.message.RpcResponse; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.util.AttributeKey; +import lombok.extern.slf4j.Slf4j; + +/** + * @ClassName NettyClientHandler + * @Description 客户端处理器 + * @Author Tong + * @LastChangeDate 2024-12-02 10:15 + * @Version v5.0 + */ +@Slf4j +public class NettyClientHandler extends SimpleChannelInboundHandler { + + + @Override + protected void channelRead0(ChannelHandlerContext ctx, RpcResponse response) throws Exception { + // 接收到response, 给channel设计别名,让sendRequest里读取response + AttributeKey RESPONSE_KEY = AttributeKey.valueOf("RPCResponse"); + // 将响应存入 Channel 属性 + ctx.channel().attr(RESPONSE_KEY).set(response); + ctx.channel().close(); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + log.error("Channel exception occurred", cause); + ctx.close(); + } +} diff --git a/version5/krpc-core/src/main/java/com/kama/client/netty/NettyClientInitializer.java b/version5/krpc-core/src/main/java/com/kama/client/netty/NettyClientInitializer.java new file mode 100644 index 0000000..3836d7e --- /dev/null +++ b/version5/krpc-core/src/main/java/com/kama/client/netty/NettyClientInitializer.java @@ -0,0 +1,40 @@ +package com.kama.client.netty; + + +import common.serializer.mycoder.MyDecoder; +import common.serializer.mycoder.MyEncoder; +import common.serializer.myserializer.Serializer; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.socket.SocketChannel; +import lombok.extern.slf4j.Slf4j; + +/** + * @ClassName NettyClientInitializer + * @Description 配置自定义的编码器以及Handler + * @Author Tong + * @LastChangeDate 2024-12-02 10:16 + * @Version v5.0 + */ +@Slf4j +public class NettyClientInitializer extends ChannelInitializer { + + + @Override + protected void initChannel(SocketChannel ch) throws Exception { + ChannelPipeline pipeline = ch.pipeline(); + + // 使用自定义的编码器和解码器 + try { + // 根据传入的序列化器类型初始化编码器 + pipeline.addLast(new MyEncoder(Serializer.getSerializerByCode(3))); + pipeline.addLast(new MyDecoder()); + pipeline.addLast(new NettyClientHandler()); + + log.info("Netty client pipeline initialized with serializer type: {}",Serializer.getSerializerByCode(3).toString()); + } catch (Exception e) { + log.error("Error initializing Netty client pipeline", e); + throw e; // 重新抛出异常,确保管道初始化失败时处理正确 + } + } +} diff --git a/version5/krpc-core/src/main/java/com/kama/client/proxy/ClientProxy.java b/version5/krpc-core/src/main/java/com/kama/client/proxy/ClientProxy.java new file mode 100644 index 0000000..ef3f6e8 --- /dev/null +++ b/version5/krpc-core/src/main/java/com/kama/client/proxy/ClientProxy.java @@ -0,0 +1,92 @@ +package com.kama.client.proxy; + +import com.kama.client.circuitbreaker.CircuitBreaker; +import com.kama.client.circuitbreaker.CircuitBreakerProvider; +import com.kama.client.retry.GuavaRetry; +import com.kama.client.rpcclient.RpcClient; +import com.kama.client.rpcclient.impl.NettyRpcClient; +import com.kama.client.servicecenter.ServiceCenter; +import com.kama.client.servicecenter.ZKServiceCenter; + + +import common.message.RpcRequest; +import common.message.RpcResponse; +import lombok.extern.slf4j.Slf4j; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +/** + * @ClassName ClientProxy + * @Description 动态代理 + * @Author Tong + * @LastChangeDate 2024-12-02 10:14 + * @Version v5.0 + */ +@Slf4j +public class ClientProxy implements InvocationHandler { + //传入参数service接口的class对象,反射封装成一个request + + private RpcClient rpcClient; + private ServiceCenter serviceCenter; + private CircuitBreakerProvider circuitBreakerProvider; + + public ClientProxy() throws InterruptedException { + serviceCenter = new ZKServiceCenter(); + rpcClient = new NettyRpcClient(serviceCenter); + circuitBreakerProvider = new CircuitBreakerProvider(); + } + + //jdk动态代理,每一次代理对象调用方法,都会经过此方法增强(反射获取request对象,socket发送到服务端) + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + //构建request + RpcRequest request = RpcRequest.builder() + .interfaceName(method.getDeclaringClass().getName()) + .methodName(method.getName()) + .params(args).paramsType(method.getParameterTypes()).build(); + //获取熔断器 + CircuitBreaker circuitBreaker = circuitBreakerProvider.getCircuitBreaker(method.getName()); + //判断熔断器是否允许请求经过 + if (!circuitBreaker.allowRequest()) { + log.warn("熔断器开启,请求被拒绝: {}", request); + //这里可以针对熔断做特殊处理,返回特殊值 + return null; + } + //数据传输 + RpcResponse response; + //后续添加逻辑:为保持幂等性,只对白名单上的服务进行重试 + // 如果启用重试机制,先检查是否需要重试 + if (serviceCenter.checkRetry(request.getInterfaceName())) { + //调用retry框架进行重试操作 + try { + log.info("尝试重试调用服务: {}", request.getInterfaceName()); + response = new GuavaRetry().sendServiceWithRetry(request, rpcClient); + } catch (Exception e) { + log.error("重试调用失败: {}", request.getInterfaceName(), e); + circuitBreaker.recordFailure(); + throw e; // 将异常抛给调用者 + } + } else { + //只调用一次 + response = rpcClient.sendRequest(request); + } + //记录response的状态,上报给熔断器 + if (response != null) { + if (response.getCode() == 200) { + circuitBreaker.recordSuccess(); + } else if (response.getCode() == 500) { + circuitBreaker.recordFailure(); + } + log.info("收到响应: {} 状态码: {}", request.getInterfaceName(), response.getCode()); + } + + return response != null ? response.getData() : null; + } + + public T getProxy(Class clazz) { + Object o = Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, this); + return (T) o; + } +} diff --git a/version5/krpc-core/src/main/java/com/kama/client/retry/GuavaRetry.java b/version5/krpc-core/src/main/java/com/kama/client/retry/GuavaRetry.java new file mode 100644 index 0000000..0d1dde1 --- /dev/null +++ b/version5/krpc-core/src/main/java/com/kama/client/retry/GuavaRetry.java @@ -0,0 +1,48 @@ +package com.kama.client.retry; + +import com.kama.client.rpcclient.RpcClient; +import com.github.rholder.retry.*; + +import common.message.RpcRequest; +import common.message.RpcResponse; +import lombok.extern.slf4j.Slf4j; + + +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +/** + * @ClassName guavaRetry + * @Description 重试策略 + * @Author Tong + * @LastChangeDate 2024-12-02 10:44 + * @Version v5.0 + */ +@Slf4j +public class GuavaRetry { + + public RpcResponse sendServiceWithRetry(RpcRequest request, RpcClient rpcClient) { + Retryer retryer = RetryerBuilder.newBuilder() + //无论出现什么异常,都进行重试 + .retryIfException() + //返回结果为 error时进行重试 + .retryIfResult(response -> Objects.equals(response.getCode(), 500)) + //重试等待策略:等待 2s 后再进行重试 + .withWaitStrategy(WaitStrategies.fixedWait(2, TimeUnit.SECONDS)) + //重试停止策略:重试达到 3 次 + .withStopStrategy(StopStrategies.stopAfterAttempt(3)) + .withRetryListener(new RetryListener() { + @Override + public void onRetry(Attempt attempt) { + log.info("重试第 {} 次", attempt.getAttemptNumber()); + } + }) + .build(); + try { + return retryer.call(() -> rpcClient.sendRequest(request)); + } catch (Exception e) { + log.error("重试失败: 请求 {} 执行时遇到异常", request.getMethodName(), e); + } + return RpcResponse.fail("重试失败,所有重试尝试已结束"); + } +} diff --git a/version5/krpc-core/src/main/java/com/kama/client/rpcclient/RpcClient.java b/version5/krpc-core/src/main/java/com/kama/client/rpcclient/RpcClient.java new file mode 100644 index 0000000..80a4255 --- /dev/null +++ b/version5/krpc-core/src/main/java/com/kama/client/rpcclient/RpcClient.java @@ -0,0 +1,17 @@ +package com.kama.client.rpcclient; + + +import common.message.RpcRequest; +import common.message.RpcResponse; + +/** + * @InterfaceName RpcClient + * @Description 定义底层通信方法 + * @Author Tong + * @LastChangeDate 2024-12-02 10:11 + * @Version v5.0 + */ + +public interface RpcClient { + RpcResponse sendRequest(RpcRequest request); +} diff --git a/version5/krpc-core/src/main/java/com/kama/client/rpcclient/impl/NettyRpcClient.java b/version5/krpc-core/src/main/java/com/kama/client/rpcclient/impl/NettyRpcClient.java new file mode 100644 index 0000000..7bd2e75 --- /dev/null +++ b/version5/krpc-core/src/main/java/com/kama/client/rpcclient/impl/NettyRpcClient.java @@ -0,0 +1,103 @@ +package com.kama.client.rpcclient.impl; + +import com.kama.client.netty.NettyClientInitializer; +import com.kama.client.rpcclient.RpcClient; +import com.kama.client.servicecenter.ServiceCenter; + +import common.message.RpcRequest; +import common.message.RpcResponse; +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.util.AttributeKey; +import lombok.extern.slf4j.Slf4j; + + +import java.net.InetSocketAddress; + +/** + * @ClassName NettyRpcClient + * @Description Netty客户端 + * @Author Tong + * @LastChangeDate 2024-12-02 11:03 + * @Version v5.0 + */ +@Slf4j +public class NettyRpcClient implements RpcClient { + + private static final Bootstrap bootstrap; + private static final EventLoopGroup eventLoopGroup; + + private ServiceCenter serviceCenter; + + public NettyRpcClient(ServiceCenter serviceCenter) throws InterruptedException { + this.serviceCenter = serviceCenter; + } + + //netty客户端初始化 + static { + eventLoopGroup = new NioEventLoopGroup(); + bootstrap = new Bootstrap(); + bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class) + .handler(new NettyClientInitializer()); + } + + @Override + public RpcResponse sendRequest(RpcRequest request) { + //从注册中心获取host,post + InetSocketAddress address = serviceCenter.serviceDiscovery(request.getInterfaceName()); + if (address == null) { + log.error("服务发现失败,返回的地址为 null"); + return RpcResponse.fail("服务发现失败,地址为 null"); + } + String host = address.getHostName(); + int port = address.getPort(); + try { + // 连接到远程服务 + ChannelFuture channelFuture = bootstrap.connect(host, port).sync(); + Channel channel = channelFuture.channel(); + // 发送数据 + channel.writeAndFlush(request); + //sync()堵塞获取结果 + channel.closeFuture().sync(); + // 阻塞的获得结果,通过给channel设计别名,获取特定名字下的channel中的内容(这个在hanlder中设置) + // AttributeKey是,线程隔离的,不会由线程安全问题。 + // 当前场景下选择堵塞获取结果 + // 其它场景也可以选择添加监听器的方式来异步获取结果 channelFuture.addListener... + AttributeKey key = AttributeKey.valueOf("RPCResponse"); + RpcResponse response = channel.attr(key).get(); + + if (response == null) { + log.error("服务响应为空,可能是请求失败或超时"); + return RpcResponse.fail("服务响应为空"); + } + + log.info("收到响应: {}", response); + return response; + } catch (InterruptedException e) { + log.error("请求被中断,发送请求失败: {}", e.getMessage(), e); + Thread.currentThread().interrupt(); + } catch (Exception e) { + log.error("发送请求时发生异常: {}", e.getMessage(), e); + } finally { + // 连接断开后,优雅地关闭 Netty 资源 + shutdown(); + } + return RpcResponse.fail("请求失败"); + } + + // 优雅关闭 Netty 资源 + private void shutdown() { + try { + if (eventLoopGroup != null) { + eventLoopGroup.shutdownGracefully().sync(); + } + } catch (InterruptedException e) { + log.error("关闭 Netty 资源时发生异常: {}", e.getMessage(), e); + Thread.currentThread().interrupt(); + } + } +} diff --git a/version5/krpc-core/src/main/java/com/kama/client/rpcclient/impl/SimpleSocketRpcClient.java b/version5/krpc-core/src/main/java/com/kama/client/rpcclient/impl/SimpleSocketRpcClient.java new file mode 100644 index 0000000..589cc98 --- /dev/null +++ b/version5/krpc-core/src/main/java/com/kama/client/rpcclient/impl/SimpleSocketRpcClient.java @@ -0,0 +1,57 @@ +package com.kama.client.rpcclient.impl; + +import com.kama.client.rpcclient.RpcClient; +import common.message.RpcRequest; +import common.message.RpcResponse; + + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.net.Socket; +import java.net.UnknownHostException; + +/** + * @ClassName SimpleSocketRpcClient + * @Description 实现简单客户都 + * @Author Tong + * @LastChangeDate 2024-12-02 10:12 + * @Version v5.0 + */ +public class SimpleSocketRpcClient implements RpcClient { + private String host; + private int port; + + public SimpleSocketRpcClient(String host, int port) { + this.host = host; + this.port = port; + } + + @Override + public RpcResponse sendRequest(RpcRequest request) { + // 定义响应对象 + RpcResponse response = null; + + // 创建 Socket 和流对象 + try (Socket socket = new Socket(host, port); + ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream()); + ObjectInputStream ois = new ObjectInputStream(socket.getInputStream())) { + + // 发送请求对象 + oos.writeObject(request); + oos.flush(); + + // 接收响应对象 + response = (RpcResponse) ois.readObject(); + + } catch (UnknownHostException e) { + System.err.println("未知的主机: " + host); + } catch (IOException e) { + System.err.println("I/O 错误: " + e.getMessage()); + } catch (ClassNotFoundException e) { + System.err.println("无法识别的类: " + e.getMessage()); + } + + return response; + } +} diff --git a/version5/krpc-core/src/main/java/com/kama/client/servicecenter/ServiceCenter.java b/version5/krpc-core/src/main/java/com/kama/client/servicecenter/ServiceCenter.java new file mode 100644 index 0000000..f1aa801 --- /dev/null +++ b/version5/krpc-core/src/main/java/com/kama/client/servicecenter/ServiceCenter.java @@ -0,0 +1,20 @@ +package com.kama.client.servicecenter; + + +import java.net.InetSocketAddress; + +/** + * @InterfaceName ServiceCenter + * @Description 服务中心接口 + * @Author Tong + * @LastChangeDate 2024-12-02 10:31 + * @Version v5.0 + */ + +public interface ServiceCenter { + // 查询:根据服务名查找地址 + InetSocketAddress serviceDiscovery(String serviceName); + + //判断是否可重试 + boolean checkRetry(String serviceName); +} diff --git a/version5/krpc-core/src/main/java/com/kama/client/servicecenter/ZKServiceCenter.java b/version5/krpc-core/src/main/java/com/kama/client/servicecenter/ZKServiceCenter.java new file mode 100644 index 0000000..93f2701 --- /dev/null +++ b/version5/krpc-core/src/main/java/com/kama/client/servicecenter/ZKServiceCenter.java @@ -0,0 +1,113 @@ +package com.kama.client.servicecenter; + +import com.kama.client.cache.ServiceCache; +import com.kama.client.servicecenter.ZKWatcher.watchZK; +import com.kama.client.servicecenter.balance.LoadBalance; +import com.kama.client.servicecenter.balance.impl.ConsistencyHashBalance; +import com.kama.client.servicecenter.balance.impl.RandomLoadBalance; +import lombok.extern.slf4j.Slf4j; +import org.apache.curator.RetryPolicy; +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.CuratorFrameworkFactory; +import org.apache.curator.retry.ExponentialBackoffRetry; + +import java.net.InetSocketAddress; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; + +/** + * @ClassName ZKServiceCenter + * @Description 从服务中心获取服务地址 + * @Author Tong + * @LastChangeDate 2024-12-02 10:33 + * @Version v5.0 + */ +@Slf4j +public class ZKServiceCenter implements ServiceCenter { + // curator 提供的zookeeper客户端 + private CuratorFramework client; + //zookeeper根路径节点 + private static final String ROOT_PATH = "MyRPC"; + private static final String RETRY = "CanRetry"; + //serviceCache + private ServiceCache cache; + + private final LoadBalance loadBalance = new ConsistencyHashBalance(); + + //负责zookeeper客户端的初始化,并与zookeeper服务端进行连接 + public ZKServiceCenter() throws InterruptedException { + // 指数时间重试 + RetryPolicy policy = new ExponentialBackoffRetry(1000, 3); + // zookeeper的地址固定,不管是服务提供者还是,消费者都要与之建立连接 + // sessionTimeoutMs 与 zoo.cfg中的tickTime 有关系, + // zk还会根据minSessionTimeout与maxSessionTimeout两个参数重新调整最后的超时值。默认分别为tickTime 的2倍和20倍 + // 使用心跳监听状态 + this.client = CuratorFrameworkFactory.builder().connectString("127.0.0.1:2181") + .sessionTimeoutMs(40000).retryPolicy(policy).namespace(ROOT_PATH).build(); + this.client.start(); + log.info("Zookeeper 连接成功"); + //初始化本地缓存 + cache = new ServiceCache(); + //加入zookeeper事件监听器 + watchZK watcher = new watchZK(client, cache); + //监听启动 + watcher.watchToUpdate(ROOT_PATH); + } + + //根据服务名(接口名)返回地址 + @Override + public InetSocketAddress serviceDiscovery(String serviceName) { + try { + //先从本地缓存中找 + List addressList = cache.getServiceListFromCache(serviceName); + //如果找不到,再去zookeeper中找 + //这种i情况基本不会发生,或者说只会出现在初始化阶段 + if (addressList == null) { + addressList = client.getChildren().forPath("/" + serviceName); + // 如果本地缓存中没有该服务名的地址列表,则添加 + List cachedAddresses = cache.getServiceListFromCache(serviceName); + if (cachedAddresses == null || cachedAddresses.isEmpty()) { + // 假设 addServiceToCache 方法可以处理单个地址 + for (String address : addressList) { + cache.addServiceToCache(serviceName, address); + } + } + } + if (addressList.isEmpty()) { + log.warn("未找到服务:{}", serviceName); + return null; + } + // 负载均衡得到地址 + String address = loadBalance.balance(addressList); + return parseAddress(address); + } catch (Exception e) { + log.error("服务发现失败,服务名:{}", serviceName, e); + } + return null; + } + //保证线程安全使用CopyOnWriteArraySet + private Set retryServiceCache = new CopyOnWriteArraySet<>(); + //写一个白名单缓存,优化性能 + public boolean checkRetry(String serviceName) { + // 如果缓存为空,则从 Zookeeper 中加载白名单 + if (retryServiceCache.isEmpty()) { + try { + // 获取 Zookeeper 上的 /RETRY 路径下的所有子节点(服务名称) + List serviceList = client.getChildren().forPath("/" + RETRY); + // 将从 Zookeeper 获取到的服务名称列表添加到缓存中 + retryServiceCache.addAll(serviceList); + } catch (Exception e) { + log.error("检查重试失败,服务名:{}", serviceName, e); + } + } + // 判断服务是否在缓存的白名单中 + return retryServiceCache.contains(serviceName); + } + + // 字符串解析为地址 + private InetSocketAddress parseAddress(String address) { + String[] result = address.split(":"); + return new InetSocketAddress(result[0], Integer.parseInt(result[1])); + } +} diff --git a/version5/krpc-core/src/main/java/com/kama/client/servicecenter/ZKWatcher/watchZK.java b/version5/krpc-core/src/main/java/com/kama/client/servicecenter/ZKWatcher/watchZK.java new file mode 100644 index 0000000..396d4e8 --- /dev/null +++ b/version5/krpc-core/src/main/java/com/kama/client/servicecenter/ZKWatcher/watchZK.java @@ -0,0 +1,98 @@ +package com.kama.client.servicecenter.ZKWatcher; + +import com.kama.client.cache.ServiceCache; +import lombok.extern.slf4j.Slf4j; +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.recipes.cache.ChildData; +import org.apache.curator.framework.recipes.cache.CuratorCache; +import org.apache.curator.framework.recipes.cache.CuratorCacheListener; + + +/** + * @ClassName watchZK + * @Description 节点监听 + * @Author Tong + * @LastChangeDate 2024-12-02 10:37 + * @Version v5.0 + */ +@Slf4j +public class watchZK { + // curator 提供的zookeeper客户端 + private CuratorFramework client; + //本地缓存 + ServiceCache cache; + + + public watchZK(CuratorFramework client, ServiceCache cache) { + this.client = client; + this.cache = cache; + } + + /** + * 监听当前节点和子节点的 更新,创建,删除 + * + * @param path + */ + public void watchToUpdate(String path) throws InterruptedException { + CuratorCache curatorCache = CuratorCache.build(client, "/"); + curatorCache.listenable().addListener(new CuratorCacheListener() { + @Override + public void event(Type type, ChildData childData, ChildData childData1) { + // 第一个参数:事件类型(枚举) + // 第二个参数:节点更新前的状态、数据 + // 第三个参数:节点更新后的状态、数据 + // 创建节点时:节点刚被创建,不存在 更新前节点 ,所以第二个参数为 null + // 删除节点时:节点被删除,不存在 更新后节点 ,所以第三个参数为 null + // 节点创建时没有赋予值 create /curator/app1 只创建节点,在这种情况下,更新前节点的 data 为 null,获取不到更新前节点的数据 + switch (type.name()) { + case "NODE_CREATED": // 监听器第一次执行时节点存在也会触发次事件 + String[] pathList = pasrePath(childData1); + if (pathList.length <= 2) break; + else { + String serviceName = pathList[1]; + String address = pathList[2]; + //将新注册的服务加入到本地缓存中 + cache.addServiceToCache(serviceName, address); + log.info("节点创建:服务名称 {} 地址 {}", serviceName, address); + } + break; + case "NODE_CHANGED": // 节点更新 + if (childData.getData() != null) { + log.debug("修改前的数据: {}", new String(childData.getData())); + } else { + log.debug("节点第一次赋值!"); + } + String[] oldPathList = pasrePath(childData); + String[] newPathList = pasrePath(childData1); + cache.replaceServiceAddress(oldPathList[1], oldPathList[2], newPathList[2]); + log.info("节点更新:服务名称 {} 地址从 {} 更新为 {}", oldPathList[1], oldPathList[2], newPathList[2]); + break; + case "NODE_DELETED": // 节点删除 + String[] pathList_d = pasrePath(childData); + if (pathList_d.length <= 2) break; + else { + String serviceName = pathList_d[1]; + String address = pathList_d[2]; + //将新注册的服务加入到本地缓存中 + cache.delete(serviceName, address); + log.info("节点删除:服务名称 {} 地址 {}", serviceName, address); + } + break; + default: + break; + } + } + }); + //开启监听 + curatorCache.start(); + } + + //解析节点对应地址 + public String[] pasrePath(ChildData childData) { + //获取更新的节点的路径 + String path = new String(childData.getPath()); + log.info("节点路径:{}",path); + //按照格式 ,读取 + return path.split("/"); + } +} diff --git a/version5/krpc-core/src/main/java/com/kama/client/servicecenter/balance/LoadBalance.java b/version5/krpc-core/src/main/java/com/kama/client/servicecenter/balance/LoadBalance.java new file mode 100644 index 0000000..1f1742c --- /dev/null +++ b/version5/krpc-core/src/main/java/com/kama/client/servicecenter/balance/LoadBalance.java @@ -0,0 +1,20 @@ +package com.kama.client.servicecenter.balance; + + +import java.util.List; + +/** + * @InterfaceName LoadBalance + * @Description 负载均衡接口 + * @Author Tong + * @LastChangeDate 2024-12-02 10:40 + * @Version v5.0 + */ + +public interface LoadBalance { + String balance(List addressList); + + void addNode(String node); + + void delNode(String node); +} diff --git a/version5/krpc-core/src/main/java/com/kama/client/servicecenter/balance/impl/ConsistencyHashBalance.java b/version5/krpc-core/src/main/java/com/kama/client/servicecenter/balance/impl/ConsistencyHashBalance.java new file mode 100644 index 0000000..119b9ea --- /dev/null +++ b/version5/krpc-core/src/main/java/com/kama/client/servicecenter/balance/impl/ConsistencyHashBalance.java @@ -0,0 +1,152 @@ +package com.kama.client.servicecenter.balance.impl; + +import com.kama.client.servicecenter.balance.LoadBalance; +import lombok.extern.slf4j.Slf4j; + + +import java.util.*; +import java.util.concurrent.ConcurrentSkipListMap; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * @ClassName ConsistencyHashBalance + * @Description 一致性哈希算法负载均衡 + * @Author Tong + * @LastChangeDate 2024-12-02 10:42 + * @Version v5.0 + */ +@Slf4j +public class ConsistencyHashBalance implements LoadBalance { + + // 虚拟节点的个数 + private static final int VIRTUAL_NUM = 5; + + // 虚拟节点分配,key是hash值,value是虚拟节点服务器名称 + private SortedMap shards = new TreeMap(); + + // 真实节点列表 + private List realNodes = new LinkedList<>(); + + // 获取虚拟节点的个数 + public static int getVirtualNum() { + return VIRTUAL_NUM; + } + + // 初始化虚拟节点 + public void init(List serviceList) { + for (String server : serviceList) { + realNodes.add(server); + log.info("真实节点[{}] 被添加", server); + for (int i = 0; i < VIRTUAL_NUM; i++) { + String virtualNode = server + "&&VN" + i; + int hash = getHash(virtualNode); + shards.put(hash, virtualNode); + log.info("虚拟节点[{}] hash:{},被添加", virtualNode, hash); + } + } + } + + /** + * 获取被分配的节点名 + * + * @param node 请求的节点(通常是请求的唯一标识符) + * @return 负责该请求的真实节点名称 + */ + public String getServer(String node, List serviceList) { + if (shards.isEmpty()) { + init(serviceList); // 初始化,如果shards为空 + } + + int hash = getHash(node); + Integer key = null; + + SortedMap subMap = shards.tailMap(hash); + if (subMap.isEmpty()) { + key = shards.firstKey(); // 如果没有大于该hash的节点,则返回最小的hash值 + } else { + key = subMap.firstKey(); + } + + String virtualNode = shards.get(key); + return virtualNode.substring(0, virtualNode.indexOf("&&")); + } + + /** + * 添加节点 + * + * @param node 新加入的节点 + */ + public void addNode(String node) { + if (!realNodes.contains(node)) { + realNodes.add(node); + log.info("真实节点[{}] 上线添加", node); + for (int i = 0; i < VIRTUAL_NUM; i++) { + String virtualNode = node + "&&VN" + i; + int hash = getHash(virtualNode); + shards.put(hash, virtualNode); + log.info("虚拟节点[{}] hash:{},被添加", virtualNode, hash); + } + } + } + + /** + * 删除节点 + * + * @param node 被移除的节点 + */ + public void delNode(String node) { + if (realNodes.contains(node)) { + realNodes.remove(node); + log.info("真实节点[{}] 下线移除", node); + for (int i = 0; i < VIRTUAL_NUM; i++) { + String virtualNode = node + "&&VN" + i; + int hash = getHash(virtualNode); + shards.remove(hash); + log.info("虚拟节点[{}] hash:{},被移除", virtualNode, hash); + } + } + } + + /** + * FNV1_32_HASH算法 + */ + private static int getHash(String str) { + final int p = 16777619; + int hash = (int) 2166136261L; + for (int i = 0; i < str.length(); i++) + hash = (hash ^ str.charAt(i)) * p; + hash += hash << 13; + hash ^= hash >> 7; + hash += hash << 3; + hash ^= hash >> 17; + hash += hash << 5; + // 如果算出来的值为负数则取其绝对值 + if (hash < 0) + hash = Math.abs(hash); + return hash; + } + + @Override + public String balance(List addressList) { + // 如果 addressList 为空或 null,抛出 IllegalArgumentException + if (addressList == null || addressList.isEmpty()) { + throw new IllegalArgumentException("Address list cannot be null or empty"); + } + + // 使用UUID作为请求的唯一标识符来进行一致性哈希 + String random = UUID.randomUUID().toString(); + return getServer(random, addressList); + } + public SortedMap getShards() { + return shards; + } + + public List getRealNodes() { + return realNodes; + } + @Override + public String toString() { + return "ConsistencyHash"; + } +} + diff --git a/version5/krpc-core/src/main/java/com/kama/client/servicecenter/balance/impl/RandomLoadBalance.java b/version5/krpc-core/src/main/java/com/kama/client/servicecenter/balance/impl/RandomLoadBalance.java new file mode 100644 index 0000000..583030d --- /dev/null +++ b/version5/krpc-core/src/main/java/com/kama/client/servicecenter/balance/impl/RandomLoadBalance.java @@ -0,0 +1,48 @@ +package com.kama.client.servicecenter.balance.impl; + +import com.kama.client.servicecenter.balance.LoadBalance; +import lombok.extern.slf4j.Slf4j; + +import java.util.List; +import java.util.Random; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * @ClassName RandomLoadBalance + * @Description 随机法 + * @Author Tong + * @LastChangeDate 2024-12-02 10:40 + * @Version v5.0 + */ +@Slf4j +public class RandomLoadBalance implements LoadBalance { + // 将Random声明为类级别的字段 + private final Random random = new Random(); + + private final List addressList = new CopyOnWriteArrayList<>(); + + @Override + public String balance(List addressList) { + if (addressList == null || addressList.isEmpty()) { + throw new IllegalArgumentException("Address list cannot be null or empty"); + } + + int choose = random.nextInt(addressList.size()); + log.info("负载均衡选择了第 {} 号服务器,地址是:{}", choose, addressList.get(choose)); + return addressList.get(choose); // 返回选择的服务器地址 + } + + @Override + public void addNode(String node) { + // 如果是动态添加节点,可以将节点加入到addressList中 + addressList.add(node); + log.info("节点 {} 已加入负载均衡", node); + } + + @Override + public void delNode(String node) { + // 如果是动态删除节点,可以将节点从addressList中移除 + addressList.remove(node); + log.info("节点 {} 已从负载均衡中移除", node); + } +} diff --git a/version5/krpc-core/src/main/java/com/kama/client/servicecenter/balance/impl/RoundLoadBalance.java b/version5/krpc-core/src/main/java/com/kama/client/servicecenter/balance/impl/RoundLoadBalance.java new file mode 100644 index 0000000..15398a9 --- /dev/null +++ b/version5/krpc-core/src/main/java/com/kama/client/servicecenter/balance/impl/RoundLoadBalance.java @@ -0,0 +1,52 @@ +package com.kama.client.servicecenter.balance.impl; + +import com.kama.client.servicecenter.balance.LoadBalance; +import lombok.extern.slf4j.Slf4j; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @ClassName RoundLoadBalance + * @Description 轮询法 + * @Author Tong + * @LastChangeDate 2024-12-02 10:41 + * @Version v5.0 + */ +@Slf4j +public class RoundLoadBalance implements LoadBalance { + + // 使用 AtomicInteger 保证线程安全 + private AtomicInteger choose = new AtomicInteger(0); + + private List addressList = new CopyOnWriteArrayList<>(); + + @Override + public String balance(List addressList) { + if (addressList == null || addressList.isEmpty()) { + throw new IllegalArgumentException("Address list cannot be null or empty"); + } + + // 获取当前索引并更新为下一个 + int currentChoose = choose.getAndUpdate(i -> (i + 1) % addressList.size()); + + String selectedServer = addressList.get(currentChoose); + log.info("负载均衡选择了服务器: {}", selectedServer); + return selectedServer; // 返回被选择的服务器地址 + } + + @Override + public void addNode(String node) { + // 如果是动态添加节点,可以将节点加入到 addressList 中 + addressList.add(node); + log.info("节点 {} 已加入负载均衡", node); + } + + @Override + public void delNode(String node) { + // 如果是动态删除节点,可以将节点从 addressList 中移除 + addressList.remove(node); + log.info("节点 {} 已从负载均衡中移除", node); + } +} diff --git a/version5/krpc-core/src/main/java/com/kama/config/KRpcConfig.java b/version5/krpc-core/src/main/java/com/kama/config/KRpcConfig.java new file mode 100644 index 0000000..1c53401 --- /dev/null +++ b/version5/krpc-core/src/main/java/com/kama/config/KRpcConfig.java @@ -0,0 +1,37 @@ +package com.kama.config; + +import com.kama.client.servicecenter.balance.impl.ConsistencyHashBalance; +import com.kama.server.serviceRegister.impl.ZKServiceRegister; +import common.serializer.myserializer.Serializer; +import lombok.*; + +/** + * @ClassName KRpcConfig + * @Description 配置文件 + * @Author Tong + * @LastChangeDate 2024-12-05 11:02 + * @Version v5.0 + */ +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@Builder +@ToString +public class KRpcConfig { + //名称 + private String name = "krpc"; + //端口 + private Integer port = 9999; + //主机名 + private String host = "localhost"; + //版本号 + private String version = "1.0.0"; + //注册中心 + private String registry = new ZKServiceRegister().toString(); + //序列化器 + private String serializer = Serializer.getSerializerByCode(3).toString(); + //负载均衡 + private String loadBalance = new ConsistencyHashBalance().toString(); + +} diff --git a/version5/krpc-core/src/main/java/com/kama/config/RpcConstant.java b/version5/krpc-core/src/main/java/com/kama/config/RpcConstant.java new file mode 100644 index 0000000..6bda0b6 --- /dev/null +++ b/version5/krpc-core/src/main/java/com/kama/config/RpcConstant.java @@ -0,0 +1,20 @@ +package com.kama.config; + + +/** + * @InterfaceName RpcConstants + * @Description + * @Author Tong + * @LastChangeDate 2024-12-05 11:17 + * @Version v5.0 + */ + +public interface RpcConstant { + + //默认的配置文件前缀 + String CONFIG_FILE_PREFIX = "rpc"; + + //默认的服务版本号 + String DEFAULT_VERSION_DEFAULT = "1.0.0"; + +} \ No newline at end of file diff --git a/version5/krpc-core/src/main/java/com/kama/server/netty/NettyRpcServerHandler.java b/version5/krpc-core/src/main/java/com/kama/server/netty/NettyRpcServerHandler.java new file mode 100644 index 0000000..0601b82 --- /dev/null +++ b/version5/krpc-core/src/main/java/com/kama/server/netty/NettyRpcServerHandler.java @@ -0,0 +1,71 @@ +package com.kama.server.netty; + + +import common.message.RpcRequest; +import common.message.RpcResponse; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import com.kama.server.provider.ServiceProvider; +import com.kama.server.ratelimit.RateLimit; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * @ClassName NettyRpcServerHandler + * @Description 服务端处理器 + * @Author Tong + * @LastChangeDate 2024-12-02 10:26 + * @Version v5.0 + */ +@AllArgsConstructor // 使用 Lombok 自动生成构造器 +@Slf4j +public class NettyRpcServerHandler extends SimpleChannelInboundHandler { + + private final ServiceProvider serviceProvider; // 确保通过构造器注入 ServiceProvider + + @Override + protected void channelRead0(ChannelHandlerContext ctx, RpcRequest request) throws Exception { + if (request == null) { + log.error("接收到非法请求,RpcRequest 为空"); + return; + } + RpcResponse response = getResponse(request); + ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + log.error("处理请求时发生异常: ", cause); + ctx.close(); + } + + private RpcResponse getResponse(RpcRequest rpcRequest) { + //得到服务名 + String interfaceName = rpcRequest.getInterfaceName(); + + //接口限流降级 + RateLimit rateLimit = serviceProvider.getRateLimitProvider().getRateLimit(interfaceName); + if (!rateLimit.getToken()) { + //如果获取令牌失败,进行限流降级,快速返回结果 + log.warn("服务限流,接口: {}", interfaceName); + return RpcResponse.fail("服务限流,接口 " + interfaceName + " 当前无法处理请求。请稍后再试。"); + } + + //得到服务端相应服务实现类 + Object service = serviceProvider.getService(interfaceName); + //反射调用方法 + Method method; + try { + method = service.getClass().getMethod(rpcRequest.getMethodName(), rpcRequest.getParamsType()); + Object invoke = method.invoke(service, rpcRequest.getParams()); + return RpcResponse.sussess(invoke); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + log.error("方法执行错误,接口: {}, 方法: {}", interfaceName, rpcRequest.getMethodName(), e); + return RpcResponse.fail("方法执行错误"); + } + } +} diff --git a/version5/krpc-core/src/main/java/com/kama/server/netty/NettyServerInitializer.java b/version5/krpc-core/src/main/java/com/kama/server/netty/NettyServerInitializer.java new file mode 100644 index 0000000..91c8f44 --- /dev/null +++ b/version5/krpc-core/src/main/java/com/kama/server/netty/NettyServerInitializer.java @@ -0,0 +1,33 @@ +package com.kama.server.netty; + + +import common.serializer.mycoder.MyDecoder; +import common.serializer.mycoder.MyEncoder; +import common.serializer.myserializer.Serializer; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.socket.SocketChannel; +import lombok.AllArgsConstructor; +import com.kama.server.provider.ServiceProvider; + + +/** + * @ClassName NettyServerInitializer + * @Description 服务端初始化器 + * @Author Tong + * @LastChangeDate 2024-12-02 10:55 + * @Version v5.0 + */ +@AllArgsConstructor +public class NettyServerInitializer extends ChannelInitializer { + private ServiceProvider serviceProvider; + + @Override + protected void initChannel(SocketChannel ch) throws Exception { + ChannelPipeline pipeline = ch.pipeline(); + //使用自定义的编/解码器 + pipeline.addLast(new MyEncoder(Serializer.getSerializerByCode(3))); + pipeline.addLast(new MyDecoder()); + pipeline.addLast(new NettyRpcServerHandler(serviceProvider)); + } +} diff --git a/version5/krpc-core/src/main/java/com/kama/server/provider/ServiceProvider.java b/version5/krpc-core/src/main/java/com/kama/server/provider/ServiceProvider.java new file mode 100644 index 0000000..4bbde28 --- /dev/null +++ b/version5/krpc-core/src/main/java/com/kama/server/provider/ServiceProvider.java @@ -0,0 +1,60 @@ +package com.kama.server.provider; + + +import com.kama.server.ratelimit.provider.RateLimitProvider; + +import com.kama.server.serviceRegister.ServiceRegister; +import com.kama.server.serviceRegister.impl.ZKServiceRegister; + + +import java.net.InetSocketAddress; +import java.util.HashMap; +import java.util.Map; + + +/** + * @ClassName ServiceProvider + * @Description 本地注册中心 + * @Author Tong + * @LastChangeDate 2024-12-02 10:21 + * @Version v5.0 + */ +public class ServiceProvider { + private Map interfaceProvider; + + private int port; + private String host; + //注册服务类 + private ServiceRegister serviceRegister; + //限流器 + private RateLimitProvider rateLimitProvider; + + public ServiceProvider(String host, int port) { + //需要传入服务端自身的网络地址 + this.host = host; + this.port = port; + this.interfaceProvider = new HashMap<>(); + this.serviceRegister = new ZKServiceRegister(); + this.rateLimitProvider = new RateLimitProvider(); + } + + public void provideServiceInterface(Object service, boolean canRetry) { + String serviceName = service.getClass().getName(); + Class[] interfaceName = service.getClass().getInterfaces(); + + for (Class clazz : interfaceName) { + //本机的映射表 + interfaceProvider.put(clazz.getName(), service); + //在注册中心注册服务 + serviceRegister.register(clazz.getName(), new InetSocketAddress(host, port), canRetry); + } + } + + public Object getService(String interfaceName) { + return interfaceProvider.get(interfaceName); + } + + public RateLimitProvider getRateLimitProvider() { + return rateLimitProvider; + } +} diff --git a/version5/krpc-core/src/main/java/com/kama/server/ratelimit/RateLimit.java b/version5/krpc-core/src/main/java/com/kama/server/ratelimit/RateLimit.java new file mode 100644 index 0000000..02a9998 --- /dev/null +++ b/version5/krpc-core/src/main/java/com/kama/server/ratelimit/RateLimit.java @@ -0,0 +1,15 @@ +package com.kama.server.ratelimit; + + +/** + * @InterfaceName RateLimit + * @Description 限流接口 + * @Author Tong + * @LastChangeDate 2024-12-02 10:50 + * @Version v5.0 + */ + +public interface RateLimit { + //获取访问许可 + boolean getToken(); +} diff --git a/version5/krpc-core/src/main/java/com/kama/server/ratelimit/impl/TokenBucketRateLimitImpl.java b/version5/krpc-core/src/main/java/com/kama/server/ratelimit/impl/TokenBucketRateLimitImpl.java new file mode 100644 index 0000000..df379d7 --- /dev/null +++ b/version5/krpc-core/src/main/java/com/kama/server/ratelimit/impl/TokenBucketRateLimitImpl.java @@ -0,0 +1,60 @@ +package com.kama.server.ratelimit.impl; + +import com.kama.server.ratelimit.RateLimit; +import lombok.extern.slf4j.Slf4j; + + +/** + * @ClassName TokenBucketRateLimitImpl + * @Description 全局限流 + * @Author Tong + * @LastChangeDate 2024-12-02 10:53 + * @Version v5.0 + */ + +@Slf4j +public class TokenBucketRateLimitImpl implements RateLimit { + + // 令牌产生速率(单位:ms) + private final int rate; + // 桶容量 + private final int capacity; + // 当前桶容量 + private volatile int curCapacity; + // 上次请求时间戳 + private volatile long lastTimestamp; + + public TokenBucketRateLimitImpl(int rate, int capacity) { + this.rate = rate; + this.capacity = capacity; + this.curCapacity = capacity; + this.lastTimestamp = System.currentTimeMillis(); + } + + @Override + public boolean getToken() { + // 优化:同步仅限于关键部分,减少锁竞争 + synchronized (this) { + // 如果当前桶还有剩余,就直接返回 + if (curCapacity > 0) { + curCapacity--; + return true; + } + + long currentTimestamp = System.currentTimeMillis(); + // 如果距离上一次请求的时间大于 RATE 的时间间隔 + if (currentTimestamp - lastTimestamp >= rate) { + // 计算这段时间内生成的令牌数量 + int generatedTokens = (int) ((currentTimestamp - lastTimestamp) / rate); + if (generatedTokens > 1) { + // 只添加剩余令牌,确保不会超过桶的容量 + curCapacity = Math.min(capacity, curCapacity + generatedTokens - 1); + } + // 更新时间戳 + lastTimestamp = currentTimestamp; + return true; + } + return false; // 如果无法获取令牌,返回 false + } + } +} diff --git a/version5/krpc-core/src/main/java/com/kama/server/ratelimit/provider/RateLimitProvider.java b/version5/krpc-core/src/main/java/com/kama/server/ratelimit/provider/RateLimitProvider.java new file mode 100644 index 0000000..9ba551c --- /dev/null +++ b/version5/krpc-core/src/main/java/com/kama/server/ratelimit/provider/RateLimitProvider.java @@ -0,0 +1,34 @@ +package com.kama.server.ratelimit.provider; + +import com.kama.server.ratelimit.RateLimit; +import com.kama.server.ratelimit.impl.TokenBucketRateLimitImpl; +import lombok.extern.slf4j.Slf4j; + + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @ClassName RateLimitProvider + * @Description 提供限流器 + * @Author Tong + * @LastChangeDate 2024-12-02 10:54 + * @Version v5.0 + */ +@Slf4j +public class RateLimitProvider { + private final Map rateLimitMap = new ConcurrentHashMap<>(); + + // 默认的限流桶容量和令牌生成速率 + private static final int DEFAULT_CAPACITY = 100; + private static final int DEFAULT_RATE = 10; + + // 提供限流实例 + public RateLimit getRateLimit(String interfaceName) { + return rateLimitMap.computeIfAbsent(interfaceName, key -> { + RateLimit rateLimit = new TokenBucketRateLimitImpl(DEFAULT_CAPACITY, DEFAULT_RATE); + log.info("为接口 [{}] 创建了新的限流策略: {}", interfaceName, rateLimit); + return rateLimit; + }); + } +} diff --git a/version5/krpc-core/src/main/java/com/kama/server/server/RpcServer.java b/version5/krpc-core/src/main/java/com/kama/server/server/RpcServer.java new file mode 100644 index 0000000..beb6bb0 --- /dev/null +++ b/version5/krpc-core/src/main/java/com/kama/server/server/RpcServer.java @@ -0,0 +1,16 @@ +package com.kama.server.server; + + +/** + * @InterfaceName RpcServer + * @Description 服务端接口 + * @Author Tong + * @LastChangeDate 2024-12-02 10:21 + * @Version v1.0 + */ + +public interface RpcServer { + void start(int port); + + void stop(); +} diff --git a/version5/krpc-core/src/main/java/com/kama/server/server/impl/NettyRpcServer.java b/version5/krpc-core/src/main/java/com/kama/server/server/impl/NettyRpcServer.java new file mode 100644 index 0000000..ebab364 --- /dev/null +++ b/version5/krpc-core/src/main/java/com/kama/server/server/impl/NettyRpcServer.java @@ -0,0 +1,81 @@ +package com.kama.server.server.impl; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.ChannelFuture; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.kama.server.netty.NettyServerInitializer; +import com.kama.server.provider.ServiceProvider; +import com.kama.server.server.RpcServer; + +/** + * @ClassName NettyRpcServer + * @Description Netty服务端 + * @Author Tong + * @LastChangeDate 2024-12-02 10:25 + * @Version v5.0 + */ +@Slf4j +@AllArgsConstructor +public class NettyRpcServer implements RpcServer { + private final ServiceProvider serviceProvider; // 只需要 ServiceProvider + private ChannelFuture channelFuture; // ChannelFuture 在 start 方法内初始化 + public NettyRpcServer(ServiceProvider serviceProvider) { + this.serviceProvider = serviceProvider; + } + + @Override + public void start(int port) { + NioEventLoopGroup bossGroup = new NioEventLoopGroup(); + NioEventLoopGroup workGroup = new NioEventLoopGroup(); + log.info("Netty服务端启动了"); + + try { + ServerBootstrap serverBootstrap = new ServerBootstrap(); + serverBootstrap.group(bossGroup, workGroup) + .channel(NioServerSocketChannel.class) + .childHandler(new NettyServerInitializer(serviceProvider)); + + // 同步阻塞,绑定端口启动服务 + channelFuture = serverBootstrap.bind(port).sync(); + log.info("Netty服务端已绑定端口:{}", port); + + // 阻塞,等待服务关闭 + channelFuture.channel().closeFuture().sync(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + log.error("Netty服务端启动中断:{}", e.getMessage(), e); + } finally { + shutdown(bossGroup, workGroup); // 集中管理线程组资源 + log.info("Netty服务端关闭了"); + } + } + + @Override + public void stop() { + if (channelFuture != null) { + try { + channelFuture.channel().close().sync(); + log.info("Netty服务端主通道已关闭"); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + log.error("关闭Netty服务端主通道时中断:{}", e.getMessage(), e); + } + } else { + log.warn("Netty服务端主通道尚未启动,无法关闭"); + } + } + + private void shutdown(NioEventLoopGroup bossGroup, NioEventLoopGroup workGroup) { + if (bossGroup != null) { + bossGroup.shutdownGracefully().syncUninterruptibly(); + } + if (workGroup != null) { + workGroup.shutdownGracefully().syncUninterruptibly(); + } + } +} diff --git a/version5/krpc-core/src/main/java/com/kama/server/server/impl/SimpleRpcServer.java b/version5/krpc-core/src/main/java/com/kama/server/server/impl/SimpleRpcServer.java new file mode 100644 index 0000000..792c4ec --- /dev/null +++ b/version5/krpc-core/src/main/java/com/kama/server/server/impl/SimpleRpcServer.java @@ -0,0 +1,70 @@ +package com.kama.server.server.impl; + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.kama.server.provider.ServiceProvider; +import com.kama.server.server.RpcServer; +import com.kama.server.server.work.WorkThread; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * @ClassName SimpleRpcServer + * @Description 简单服务端 + * @Author Tong + * @LastChangeDate 2024-12-02 10:23 + * @Version v5.0 + */ +@AllArgsConstructor +@Slf4j +public class SimpleRpcServer implements RpcServer { + private ServiceProvider serviceProvider; + // 控制服务器运行状态 + private AtomicBoolean running = new AtomicBoolean(true); + private ServerSocket serverSocket; + + @Override + public void start(int port) { + try { + serverSocket = new ServerSocket(port); + log.info("服务器启动了,监听端口:{}", port); + while (running.get()) { + try { + Socket socket = serverSocket.accept(); + new Thread(new WorkThread(socket, serviceProvider)).start(); + } catch (IOException e) { + if (running.get()) { // 如果不是因为服务器被停止导致的异常 + log.error("接受连接时发生异常:{}", e.getMessage(), e); + } + } + } + } catch (IOException e) { + log.error("服务器启动失败:{}", e.getMessage(), e); + } finally { + stop(); + } + } + + @Override + public void stop() { + if (!running.get()) return; // 防止重复停止 + + running.set(false); + log.info("服务器正在关闭..."); + + // 关闭 ServerSocket + if (serverSocket != null && !serverSocket.isClosed()) { + try { + serverSocket.close(); + log.info("服务器已关闭"); + } catch (IOException e) { + log.error("关闭服务器时发生异常:{}", e.getMessage(), e); + } + } + } +} diff --git a/version5/krpc-core/src/main/java/com/kama/server/server/work/WorkThread.java b/version5/krpc-core/src/main/java/com/kama/server/server/work/WorkThread.java new file mode 100644 index 0000000..447a600 --- /dev/null +++ b/version5/krpc-core/src/main/java/com/kama/server/server/work/WorkThread.java @@ -0,0 +1,61 @@ +package com.kama.server.server.work; + + +import common.message.RpcRequest; +import common.message.RpcResponse; +import lombok.AllArgsConstructor; +import com.kama.server.provider.ServiceProvider; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.Socket; + +/** + * @ClassName WorkThread + * @Description + * @Author Tong + * @LastChangeDate 2024-12-02 10:22 + * @Version v5.0 + */ +@AllArgsConstructor +public class WorkThread implements Runnable { + private Socket socket; + private ServiceProvider serviceProvide; + + @Override + public void run() { + try { + ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream()); + ObjectInputStream ois = new ObjectInputStream(socket.getInputStream()); + //读取客户端传过来的request + RpcRequest rpcRequest = (RpcRequest) ois.readObject(); + //反射调用服务方法获取返回值 + RpcResponse rpcResponse = getResponse(rpcRequest); + //向客户端写入response + oos.writeObject(rpcResponse); + oos.flush(); + } catch (IOException | ClassNotFoundException e) { + e.printStackTrace(); + } + } + + private RpcResponse getResponse(RpcRequest rpcRequest) { + //得到服务名 + String interfaceName = rpcRequest.getInterfaceName(); + //得到服务端相应服务实现类 + Object service = serviceProvide.getService(interfaceName); + //反射调用方法 + Method method; + try { + method = service.getClass().getMethod(rpcRequest.getMethodName(), rpcRequest.getParamsType()); + Object invoke = method.invoke(service, rpcRequest.getParams()); + return RpcResponse.sussess(invoke); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + return RpcResponse.fail("方法执行错误"); + } + } +} diff --git a/version5/krpc-core/src/main/java/com/kama/server/serviceRegister/ServiceRegister.java b/version5/krpc-core/src/main/java/com/kama/server/serviceRegister/ServiceRegister.java new file mode 100644 index 0000000..dd0398d --- /dev/null +++ b/version5/krpc-core/src/main/java/com/kama/server/serviceRegister/ServiceRegister.java @@ -0,0 +1,16 @@ +package com.kama.server.serviceRegister; + + +import java.net.InetSocketAddress; + +/** + * @InterfaceName ServiceRegister + * @Description 服务注册接口 + * @Author Tong + * @LastChangeDate 2024-12-02 10:27 + * @Version v5.0 + */ + +public interface ServiceRegister { + void register(String serviceName, InetSocketAddress serviceAddress, boolean canRetry); +} diff --git a/version5/krpc-core/src/main/java/com/kama/server/serviceRegister/impl/ZKServiceRegister.java b/version5/krpc-core/src/main/java/com/kama/server/serviceRegister/impl/ZKServiceRegister.java new file mode 100644 index 0000000..ce952e6 --- /dev/null +++ b/version5/krpc-core/src/main/java/com/kama/server/serviceRegister/impl/ZKServiceRegister.java @@ -0,0 +1,74 @@ +package com.kama.server.serviceRegister.impl; + +import lombok.extern.slf4j.Slf4j; +import org.apache.curator.RetryPolicy; +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.CuratorFrameworkFactory; +import org.apache.curator.retry.ExponentialBackoffRetry; +import org.apache.zookeeper.CreateMode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.kama.server.serviceRegister.ServiceRegister; + +import java.net.InetSocketAddress; + +/** + * @ClassName ZKServiceRegister + * @Description zk服务注册中心 + * @Author Tong + * @LastChangeDate 2024-12-02 10:28 + * @Version v5.0 + */ +@Slf4j +public class ZKServiceRegister implements ServiceRegister { + private CuratorFramework client; + private static final String ROOT_PATH = "MyRPC"; + private static final String RETRY = "CanRetry"; + + public ZKServiceRegister() { + RetryPolicy policy = new ExponentialBackoffRetry(1000, 3); + this.client = CuratorFrameworkFactory.builder() + .connectString("127.0.0.1:2181") + .sessionTimeoutMs(40000) + .retryPolicy(policy) + .namespace(ROOT_PATH) + .build(); + this.client.start(); + log.info("Zookeeper 连接成功"); + } + + @Override + public void register(String serviceName, InetSocketAddress serviceAddress, boolean canRetry) { + try { + if (client.checkExists().forPath("/" + serviceName) == null) { + client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath("/" + serviceName); + log.info("服务节点 {} 创建成功", "/" + serviceName); + } + + String path = "/" + serviceName + "/" + getServiceAddress(serviceAddress); + if (client.checkExists().forPath(path) == null) { + client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(path); + log.info("服务地址 {} 注册成功", path); + } else { + log.info("服务地址 {} 已经存在,跳过注册", path); + } + + if (canRetry) { + path = "/" + RETRY + "/" + serviceName; + client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(path); + log.info("重试标识 {} 注册成功", path); + } + } catch (Exception e) { + log.error("服务注册失败,服务名:{},错误信息:{}", serviceName, e.getMessage(), e); + } + } + + @Override + public String toString() { + return "zookeeper"; + } + + private String getServiceAddress(InetSocketAddress serverAddress) { + return serverAddress.getHostName() + ":" + serverAddress.getPort(); + } +} diff --git a/version5/krpc-core/src/main/java/com/kama/test/balance/ConsistencyHashBalanceTest.java b/version5/krpc-core/src/main/java/com/kama/test/balance/ConsistencyHashBalanceTest.java new file mode 100644 index 0000000..fa94dc6 --- /dev/null +++ b/version5/krpc-core/src/main/java/com/kama/test/balance/ConsistencyHashBalanceTest.java @@ -0,0 +1,100 @@ +package com.kama.test.balance; + +import com.kama.client.servicecenter.balance.impl.ConsistencyHashBalance; +import org.junit.Before; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.*; + +/** + * @ClassName ConsistencyHashBalanceTest + * @Description 一致性哈希测试类 + * @Author Tong + * @LastChangeDate 2024-12-05 15:39 + * @Version v5.0 + */ +public class ConsistencyHashBalanceTest { + + private ConsistencyHashBalance balance; + + @Before + public void setUp() { + balance = new ConsistencyHashBalance(); + } + + @Test + public void testInit() { + // 模拟真实节点 + List nodes = Arrays.asList("server1", "server2", "server3"); + balance.init(nodes); + + // 验证虚拟节点的初始化是否正确 + assertTrue("shards should not be empty", balance.getShards().size() > 0); + assertTrue("realNodes should contain all nodes", balance.getRealNodes().containsAll(nodes)); + } + + @Test + public void testGetServer() { + // 模拟真实节点 + List nodes = Arrays.asList("server1", "server2", "server3"); + balance.init(nodes); + + // 使用 UUID 作为请求的唯一标识符进行负载均衡 + String server = balance.getServer("request-1", nodes); + assertNotNull("Server should not be null", server); + assertTrue("Server should be one of the real nodes", nodes.contains(server)); + + // 确保多个请求的分配在不同节点上(可根据测试的多次运行结果观察) + String server2 = balance.getServer("request-2", nodes); + assertNotEquals("Server should be different from the previous request", server, server2); + } + + @Test + public void testAddNode() { + // 模拟真实节点 + List nodes = Arrays.asList("server1", "server2"); + balance.init(nodes); + + // 新加入一个节点 + balance.addNode("server3"); + + // 验证新节点是否被加入 + assertTrue("server3 should be added", balance.getRealNodes().contains("server3")); + assertTrue("shards should contain virtual nodes for server3", balance.getShards().size() > 0); + } + + @Test + public void testDelNode() { + // 模拟真实节点 + List nodes = Arrays.asList("server1", "server2"); + balance.init(nodes); + + // 删除一个节点 + balance.delNode("server1"); + + // 验证该节点是否被移除 + assertFalse("server1 should be removed", balance.getRealNodes().contains("server1")); + assertFalse("shards should not contain virtual nodes for server1", balance.getShards().values().stream().anyMatch(vn -> vn.startsWith("server1"))); + } + + @Test(expected = IllegalArgumentException.class) + public void testBalanceWithEmptyList() { + // 测试地址列表为空时,抛出 IllegalArgumentException + balance.balance(Arrays.asList()); + } + + @Test(expected = IllegalArgumentException.class) + public void testBalanceWithNullList() { + // 测试地址列表为 null 时,抛出 IllegalArgumentException + balance.balance(null); + } + + @Test + public void testGetVirtualNum() { + // 测试虚拟节点的数量 + assertEquals("Virtual nodes count should be 5", 5, ConsistencyHashBalance.getVirtualNum()); + } +} diff --git a/version5/krpc-core/src/main/java/com/kama/test/balance/RandomLoadBalanceTest.java b/version5/krpc-core/src/main/java/com/kama/test/balance/RandomLoadBalanceTest.java new file mode 100644 index 0000000..4f1aa33 --- /dev/null +++ b/version5/krpc-core/src/main/java/com/kama/test/balance/RandomLoadBalanceTest.java @@ -0,0 +1,76 @@ +package com.kama.test.balance; + +import com.kama.client.servicecenter.balance.impl.RandomLoadBalance; +import org.junit.Before; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * @ClassName RandomLoadBalanceTest + * @Description 随机负载均衡器测试 + * @Author Tong + * @LastChangeDate 2024-12-05 15:43 + * @Version v5.0 + */ +public class RandomLoadBalanceTest { + + private RandomLoadBalance loadBalance; + + @Before + public void setUp() { + // 在每个测试前初始化负载均衡器 + loadBalance = new RandomLoadBalance(); + } + + @Test + public void testBalance_WithNonEmptyList() { + // 准备一个非空的地址列表 + List addressList = Arrays.asList("server1", "server2", "server3"); + + // 使用 balance 方法选择一个服务器 + String selectedServer = loadBalance.balance(addressList); + + // 确保选择的服务器在列表中 + assertTrue(addressList.contains(selectedServer)); + } + + @Test(expected = IllegalArgumentException.class) + public void testBalance_WithEmptyList() { + // 测试空的节点列表,应该抛出 IllegalArgumentException 异常 + loadBalance.balance(Arrays.asList()); + } + + @Test(expected = IllegalArgumentException.class) + public void testBalance_WithNullList() { + // 测试 null 的节点列表,应该抛出 IllegalArgumentException 异常 + loadBalance.balance(null); + } + + @Test + public void testAddNode() { + // 测试添加节点到负载均衡器 + loadBalance.addNode("server4"); + + // 确保新添加的节点在负载均衡器中 + List addressList = Arrays.asList("server1", "server2", "server3", "server4"); + String selectedServer = loadBalance.balance(addressList); + assertTrue(addressList.contains(selectedServer)); + } + + @Test + public void testDelNode() { + // 测试从负载均衡器中移除节点 + loadBalance.addNode("server4"); + loadBalance.delNode("server4"); + + // 确保删除后的节点不再在负载均衡器中 + List addressList = Arrays.asList("server1", "server2", "server3"); + String selectedServer = loadBalance.balance(addressList); + assertFalse(addressList.contains("server4")); + } +} diff --git a/version5/krpc-core/src/main/java/com/kama/test/balance/RoundLoadBalanceTest.java b/version5/krpc-core/src/main/java/com/kama/test/balance/RoundLoadBalanceTest.java new file mode 100644 index 0000000..796a60f --- /dev/null +++ b/version5/krpc-core/src/main/java/com/kama/test/balance/RoundLoadBalanceTest.java @@ -0,0 +1,93 @@ +package com.kama.test.balance; + +import com.kama.client.servicecenter.balance.impl.RoundLoadBalance; +import org.junit.Before; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.*; + +/** + * @ClassName RoundLoadBalanceTest + * @Description 轮询测试类 + * @Author Tong + * @LastChangeDate 2024-12-05 15:46 + * @Version v5.0 + */ +public class RoundLoadBalanceTest { + + private RoundLoadBalance loadBalance; + + @Before + public void setUp() { + // 在每个测试前初始化负载均衡器 + loadBalance = new RoundLoadBalance(); + } + + @Test + public void testBalance_WithNonEmptyList() { + // 准备一个非空的地址列表 + List addressList = Arrays.asList("server1", "server2", "server3"); + + // 执行 balance 方法并获取返回的服务器 + String selectedServer = loadBalance.balance(addressList); + + // 确保选择的服务器在列表中 + assertTrue(addressList.contains(selectedServer)); + } + + @Test(expected = IllegalArgumentException.class) + public void testBalance_WithEmptyList() { + // 测试空的节点列表,应该抛出 IllegalArgumentException 异常 + loadBalance.balance(Arrays.asList()); + } + + @Test(expected = IllegalArgumentException.class) + public void testBalance_WithNullList() { + // 测试 null 的节点列表,应该抛出 IllegalArgumentException 异常 + loadBalance.balance(null); + } + + @Test + public void testAddNode() { + // 测试添加节点到负载均衡器 + loadBalance.addNode("server4"); + + // 确保新添加的节点在负载均衡器中 + List addressList = Arrays.asList("server1", "server2", "server3", "server4"); + String selectedServer = loadBalance.balance(addressList); + assertTrue(addressList.contains(selectedServer)); + } + + @Test + public void testDelNode() { + // 测试从负载均衡器中移除节点 + loadBalance.addNode("server4"); + loadBalance.delNode("server4"); + + // 确保删除后的节点不再在负载均衡器中 + List addressList = Arrays.asList("server1", "server2", "server3"); + String selectedServer = loadBalance.balance(addressList); + assertFalse(addressList.contains("server4")); + } + + @Test + public void testBalance_RoundRobin() { + // 测试负载均衡是否按轮询顺序选择服务器 + List addressList = Arrays.asList("server1", "server2", "server3"); + + // 轮询选择服务器 + String firstSelection = loadBalance.balance(addressList); + String secondSelection = loadBalance.balance(addressList); + String thirdSelection = loadBalance.balance(addressList); + String fourthSelection = loadBalance.balance(addressList); // Should loop back to first + + // 确保选择的服务器是轮询顺序的 + assertNotEquals(firstSelection, secondSelection); + assertNotEquals(secondSelection, thirdSelection); + assertNotEquals(thirdSelection, fourthSelection); + assertEquals(firstSelection, fourthSelection); // Should be back to the first + } +} diff --git a/version5/krpc-core/src/main/java/com/kama/test/serializer/HessianSerializerTest.java b/version5/krpc-core/src/main/java/com/kama/test/serializer/HessianSerializerTest.java new file mode 100644 index 0000000..46c2854 --- /dev/null +++ b/version5/krpc-core/src/main/java/com/kama/test/serializer/HessianSerializerTest.java @@ -0,0 +1,52 @@ +package com.kama.test.serializer; + + +import common.exception.SerializeException; +import common.serializer.myserializer.HessianSerializer; +import org.junit.Test; + +/** + * @ClassName HessianSerializerTest + * @Description Hessian测试类 + * @Author Tong + * @LastChangeDate 2024-12-05 15:21 + * @Version v5.0 + */ +import org.junit.Test; +import static org.junit.Assert.*; + +public class HessianSerializerTest { + + private HessianSerializer serializer = new HessianSerializer(); + + @Test + public void testSerializeAndDeserialize() { + // 创建一个测试对象 + String original = "Hello, Hessian!"; + + // 序列化 + byte[] serialized = serializer.serialize(original); + assertNotNull("序列化结果不应为 null", serialized); + + // 反序列化 + Object deserialized = serializer.deserialize(serialized, 3); + assertNotNull("反序列化结果不应为 null", deserialized); + + // 校验反序列化的结果 + assertEquals("反序列化的对象应该与原对象相同", original, deserialized); + } + + @Test + public void testDeserializeWithInvalidData() { + byte[] invalidData = new byte[]{1, 2, 3}; // 假数据 + + // 测试无效数据反序列化 + try { + serializer.deserialize(invalidData, 3); + fail("反序列化时应抛出异常"); + } catch (SerializeException e) { + assertEquals("Deserialization failed", e.getMessage()); + } + } +} + diff --git a/version5/krpc-core/src/main/java/com/kama/test/serializer/KryoSerializerTest.java b/version5/krpc-core/src/main/java/com/kama/test/serializer/KryoSerializerTest.java new file mode 100644 index 0000000..b8532ae --- /dev/null +++ b/version5/krpc-core/src/main/java/com/kama/test/serializer/KryoSerializerTest.java @@ -0,0 +1,89 @@ +package com.kama.test.serializer; + + +import com.kama.pojo.User; +import common.exception.SerializeException; +import common.serializer.myserializer.KryoSerializer; +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * @ClassName KryoSerializer + * @Description kryo测试类 + * @Author Tong + * @LastChangeDate 2024-12-05 15:31 + * @Version v5.0 + */ +public class KryoSerializerTest { + + private KryoSerializer serializer = new KryoSerializer(); + + @Test + public void testSerializeAndDeserialize() { + // 创建一个 User 对象 + User originalUser = User.builder() + .id(1) + .userName("TestUser") + .gender(true) + .build(); + + // 序列化 + byte[] serialized = serializer.serialize(originalUser); + assertNotNull("序列化结果不应为 null", serialized); + + // 反序列化 + Object deserialized = serializer.deserialize(serialized, 1); + assertNotNull("反序列化结果不应为 null", deserialized); + + // 校验反序列化的对象是否与原对象相同 + assertTrue("反序列化的对象应该是 User 类型", deserialized instanceof User); + User deserializedUser = (User) deserialized; + assertEquals("反序列化的 User 应该与原 User 相同", originalUser, deserializedUser); + } + + @Test + public void testSerializeNullObject() { + // 测试序列化 null 对象 + try { + serializer.serialize(null); + fail("序列化 null 对象时应抛出 IllegalArgumentException"); + } catch (IllegalArgumentException e) { + assertEquals("Cannot serialize null object", e.getMessage()); + } + } + + @Test + public void testDeserializeNullBytes() { + // 测试反序列化 null 或空字节数组 + try { + serializer.deserialize(null, 1); + fail("反序列化 null 字节数组时应抛出 IllegalArgumentException"); + } catch (IllegalArgumentException e) { + assertEquals("Cannot deserialize null or empty byte array", e.getMessage()); + } + } + + @Test + public void testDeserializeEmptyBytes() { + // 测试反序列化空字节数组 + try { + serializer.deserialize(new byte[0], 1); + fail("反序列化空字节数组时应抛出 IllegalArgumentException"); + } catch (IllegalArgumentException e) { + assertEquals("Cannot deserialize null or empty byte array", e.getMessage()); + } + } + + @Test + public void testDeserializeInvalidMessageType() { + // 测试反序列化未知的 messageType + byte[] serialized = serializer.serialize(new User(1, "TestUser", true)); + try { + serializer.deserialize(serialized, 99); // 使用无效的 messageType + fail("反序列化时应抛出 SerializeException"); + } catch (SerializeException e) { + assertEquals("Deserialization failed", e.getMessage()); + } + } +} \ No newline at end of file diff --git a/version5/krpc-core/src/main/java/com/kama/test/serializer/ProtostuffSerializerTest.java b/version5/krpc-core/src/main/java/com/kama/test/serializer/ProtostuffSerializerTest.java new file mode 100644 index 0000000..ed9fce5 --- /dev/null +++ b/version5/krpc-core/src/main/java/com/kama/test/serializer/ProtostuffSerializerTest.java @@ -0,0 +1,89 @@ +package com.kama.test.serializer; + + +import com.kama.pojo.User; +import common.exception.SerializeException; +import common.serializer.myserializer.ProtostuffSerializer; +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * @ClassName ProtostuffSerializerTest + * @Description protostuff 序列化测试 + * @Author Tong + * @LastChangeDate 2024-12-05 15:32 + * @Version v5.0 + */ +public class ProtostuffSerializerTest { + + private ProtostuffSerializer serializer = new ProtostuffSerializer(); + + @Test + public void testSerializeAndDeserialize() { + // 创建一个 User 对象 + User originalUser = User.builder() + .id(1) + .userName("TestUser") + .gender(true) + .build(); + + // 序列化 + byte[] serialized = serializer.serialize(originalUser); + assertNotNull("序列化结果不应为 null", serialized); + + // 反序列化 + Object deserialized = serializer.deserialize(serialized, 1); + assertNotNull("反序列化结果不应为 null", deserialized); + + // 校验反序列化的对象是否与原对象相同 + assertTrue("反序列化的对象应该是 User 类型", deserialized instanceof User); + User deserializedUser = (User) deserialized; + assertEquals("反序列化的 User 应该与原 User 相同", originalUser, deserializedUser); + } + + @Test + public void testSerializeNullObject() { + // 测试序列化 null 对象 + try { + serializer.serialize(null); + fail("序列化 null 对象时应抛出 IllegalArgumentException"); + } catch (IllegalArgumentException e) { + assertEquals("Cannot serialize null object", e.getMessage()); + } + } + + @Test + public void testDeserializeNullBytes() { + // 测试反序列化 null 字节数组 + try { + serializer.deserialize(null, 1); + fail("反序列化 null 字节数组时应抛出 IllegalArgumentException"); + } catch (IllegalArgumentException e) { + assertEquals("Cannot deserialize null or empty byte array", e.getMessage()); + } + } + + @Test + public void testDeserializeEmptyBytes() { + // 测试反序列化空字节数组 + try { + serializer.deserialize(new byte[0], 1); + fail("反序列化空字节数组时应抛出 IllegalArgumentException"); + } catch (IllegalArgumentException e) { + assertEquals("Cannot deserialize null or empty byte array", e.getMessage()); + } + } + + @Test + public void testDeserializeInvalidMessageType() { + // 测试反序列化未知的 messageType + byte[] serialized = serializer.serialize(new User(1, "TestUser", true)); + try { + serializer.deserialize(serialized, 99); // 使用无效的 messageType + fail("反序列化时应抛出 SerializeException"); + } catch (SerializeException e) { + assertEquals("Unknown message type: 99", e.getMessage()); + } + } +} \ No newline at end of file diff --git a/version5/krpc-core/src/main/resources/META-INF/serializer/com.kama.common.serializer.myserializer.Serializer b/version5/krpc-core/src/main/resources/META-INF/serializer/com.kama.common.serializer.myserializer.Serializer new file mode 100644 index 0000000..90c3187 --- /dev/null +++ b/version5/krpc-core/src/main/resources/META-INF/serializer/com.kama.common.serializer.myserializer.Serializer @@ -0,0 +1,5 @@ +Hessian=com.kama.common.serializer.myserializer.HessianSerializer +protobuf=com.kama.common.serializer.myserializer.ProtobufSerializer +json=com.kama.common.serializer.myserializer.JsonSerializer +kryo=com.kama.common.serializer.myserializer.KryoSerializer +jdk=com.kama.common.serializer.myserializer.ObjectSerializer diff --git a/version5/krpc-core/target/classes/META-INF/serializer/com.kama.common.serializer.myserializer.Serializer b/version5/krpc-core/target/classes/META-INF/serializer/com.kama.common.serializer.myserializer.Serializer new file mode 100644 index 0000000..90c3187 --- /dev/null +++ b/version5/krpc-core/target/classes/META-INF/serializer/com.kama.common.serializer.myserializer.Serializer @@ -0,0 +1,5 @@ +Hessian=com.kama.common.serializer.myserializer.HessianSerializer +protobuf=com.kama.common.serializer.myserializer.ProtobufSerializer +json=com.kama.common.serializer.myserializer.JsonSerializer +kryo=com.kama.common.serializer.myserializer.KryoSerializer +jdk=com.kama.common.serializer.myserializer.ObjectSerializer diff --git a/version5/krpc-core/target/classes/com/kama/KRpcApplication.class b/version5/krpc-core/target/classes/com/kama/KRpcApplication.class new file mode 100644 index 0000000000000000000000000000000000000000..0ec83d5467882dc1d049ad9d87dff4d8cf84de75 GIT binary patch literal 1617 zcmaJ>-E$LF6#w0%WH;%Srb|CazeroKNr0|Y6g35fKouHvY{if9 zQDqQjcyJhB{F*`Q6GOp)YOOf(;0%8UY0H!E4&!+4Zb;U2^kw(nbI<*q-}#+$?xVkd z{uRJMFIS&@ zd~JR8R`Ou-!;f0gi7pA<3VN`evTDX*Xfdogn_)DSZ)7~}I)+uu1}we?+4PKrUWVTP zW-pQlcAgLlx#~|a1B%Qm3W($3^CK@=Y0di zfgsJC$t#s0%Np*|$nHda@Ck=AZ}`grADT{C}My?$+d@wTU* zgcQTJhBC+A(+g!$*BBBA%h*lI`A9x$_2$yXkKYmA6L?ggVlEz_XhG$F*VU0_JU(y^7+dXx>+0X*tEUi=!Ho`b|j?7Qk7DfYSD|rB2XhAp8=n3G(aoD3H@Sc(Rp1cL=aW$b1EMv!SNK6UX zUFv{`yNK@m3KHk;BkkEK+MW z`P|g+-ZHX`{h5c0kn@>`yC0D0Hu<%Z4hiy35Hs=j)$Sz)K+GdZkw=+MV!XP5lEMSdvEo literal 0 HcmV?d00001 diff --git a/version5/krpc-core/target/classes/com/kama/client/cache/ServiceCache.class b/version5/krpc-core/target/classes/com/kama/client/cache/ServiceCache.class new file mode 100644 index 0000000000000000000000000000000000000000..84cacfec6ce39ddd148f83a722a6550eeeaba5c1 GIT binary patch literal 3168 zcmb_f+jA3D82_ClZPH{*X@SOar<6A7g@AG?q!vn`+)Aki0tKpW(_32 z3^3Gj^Z~&-0}c-8$V>s9-VlBA!9T-k0zUcTn~vjmb~owv!oY*mna$aA{awEAJKNv> zop=o3Reb1#1y(z30`id0u&_t$6Ma!J9`POS?2*DrhWrLOE+-oqtR8Qu01gz`;S}J4 zz)&7mV!ktCO!S4LvJ_AH!eY2v@&zTePYz1~{=$%_butvSXq%^!a@2Q7?De~_07Z5z z6i|#3hQb-646d*ePl|FpaZu`KSmM!Fo1pop`m5$rJ> z(z1}-O=;-HK>;mXNk=y49B5_mzd!^}O@1;fcGH(`{&D>_-?dG^Yj~Z(E+_VI2oy>l zJ!g+_=Sm$2F>KB%l5;Do;lz_~CwLl-KA=N#sgW7Gpl!bv$GM3C2Tm|-)elYIzW?-x zZ}=K5*9Hb(#HCYQ{3<8jz?*iwB|tZSGpsWk6^M$7gx@>2 zMA*?qb!plh(m0$_RbDOwx#m7G(M?6si7>2mNKlT%#bipQ^0`;HK3g^$Op!1n;d748 zGo#Vp=%?@sC|$H+QHvaxT2rx3Nj)NVMyZGDT3C#RL{;YRMyD;=EmQPVv^>AY)855Y zFpaW*L@^5gbK=-Q@1-Dc(P~vnBzQnlxic}=LX;*X^ed&63M$8|sj1xs#x4|P#vTl% zGwJmAW+_$7+IU{2=q3s##qb$&!%$+0D)mOiuw?LS(y>0b_|9<0jFmHXD$%aoM$Wi& zF8AW505eYFi3 z#~xsL$?6(Q!s)+{ZV?e-ilwk$YTuImA zDr~@N)YIEel;a@P?qgV|VctY|%Lu=MURY_yDx*bM4IaF87OKYWI1<#dAW3JG(yzWU zm0QVMeHs?ahDus-4sRO@{7y>c(MZUoz)rIQ+ihhwqP>wcD6Vo6gVkK#cxKi6{Nlm{E)BMZHR=`XAtI60JVp>aob!f(V>?aeCVq=c?QX1jB zxig_0+fa^e2>lvcKKc%|tQv3bvc57!D>>VISpLR-7xfR|T~dWRewJqQ@bcvN-arY5 z+(?LbS}}&*BWC&|G}o#*Sfb(COboWrWmHdIZr2cVy=lK3PLF|;Bd~%kGvaoTV7!+L zybNu|>2;XTYOVVfyLiAI;Bi>bTU>UI?vN2sWWZrI9i%~H_$YxtmQ%l-tAEn0ekBU% zupKC*I9rA$Qhyg|9v~LGskH}fbmgDI0gX?!4lKu~Bd2?Z5oLumUyn;3Z(Mi1CmUr2 zu>6BIJKQcZ{e4rWa-o1lC5|z4JjC)zgei%nhp_WFVVcVP9|)R43*FRnp4t|C@E-s> Bk39eY literal 0 HcmV?d00001 diff --git a/version5/krpc-core/target/classes/com/kama/client/circuitbreaker/CircuitBreaker$1.class b/version5/krpc-core/target/classes/com/kama/client/circuitbreaker/CircuitBreaker$1.class new file mode 100644 index 0000000000000000000000000000000000000000..9651ad6680772b58a7dc9cd7eb76a0bbfd55726a GIT binary patch literal 889 zcmbVK+iuf95Ix(Zb`o6bUZA1eN--rV7mEti2PhJRM2*M=$yIsi1KMm>%{GpW)=v2Y zeuZa1LI|OL03U^zbtiShcFxTF`u*bszz*)0FoT?pyn|U-3|k@|`9nV9 zz6h0!QeP-7#wzV;$%j(=wF!JVfkDbs$zUDxa4Zvs?Mn5aHGPDqMHEo9QF3qr4ud~c z2=5WYju(7TsTj2RdykwwqrjUe@Xjmn{;6Q*pjo6sTy!uGmm%MMQ|~Z5{vR!-poMv>IDkSB>u+%+1D>olg!Et|nGJ7R8PjqEK#ACaRSu>BqE@Vh<`-X*l_Y?B3Gq^%<_6)8Pv@-aa+>2Po3NE3H+t?s`7h8BjHivZzY-Qm$=?v5b mz@vWw)?1rywEr&1<4$&AgWN%=LhEM6RB(@MDa)HSZs8AP>EE9K literal 0 HcmV?d00001 diff --git a/version5/krpc-core/target/classes/com/kama/client/circuitbreaker/CircuitBreaker.class b/version5/krpc-core/target/classes/com/kama/client/circuitbreaker/CircuitBreaker.class new file mode 100644 index 0000000000000000000000000000000000000000..6d1b74adb386cbdb006efcaa366fdde6aa1fdf4f GIT binary patch literal 3249 zcmb7G?N=1{6@G?Y++`gEK_yB{#G-+XvWYP!4a5W&Fjx=;g3-J=3<1( zLqH9QNi>PcX+5bLJvnN82?z#FKlkr&7x?5Kke;4CcV@)Zq&elo%>BL1eR=NlyLbNe zpPRPky{$!JTD&*X+O^lvZ3PQz&A4fAQsAqs>dZqBc>#n} zEQG3Hy`C6|^lJlJM30$9+>Yp`rKe20%QCcn!-_=R>1KD@Zfmwdh0*5L_J(=|8=B|; zc}*S`p&)=l6^q4#b@Ni8AU7#a1W>G?%84~)o3V(Vi0diKk_a?AF<|PEI!83cZKKz) zLMXygL1>u*e`S-vun3Q+SdJA6)E>=@r7Rpmmi?wW3M(xcSNnQ9dg$B1Rv)&Wt!-LT9=cL+~neE?6X z*np=W;7j{~q-_i+SnMGw-?W&qn3;5_2qsWrf+@9S^DhNneWeKE`FUkdFAfZmj@3mB!f2v z@PdjLQAcMUR>y3a@t%Z&$12^dIK*zZEi>L*leOYfy+-mHMSTDbDjKmx0eLYGdRIKD z;Q5O7{idz=wP^b)_;H2YjSBZ}R6KY$Dt_^9IrHPN!27ab+o@pbFCbXe z8N@3J%3T}NCr)Qhj@+5JL|vI(Y!|d?r^sURT-|15qg6LPUX6Evh^sSRFrv?81P4NL0ap8BaVOa@F zSg=pUTfzdr5FiNNuooV}>CVKl^hX~vCvyhwyEqp{76F1t5s2&?CP0i2kp{m8M3x3g z@_zBOQ$RkT;!jd8edJ>Zf5xEz{-WX?3@Rvj_?k9qx}C5NkkrhGTL=`a&L&)xr7l@h z_49hcZc^D)zqLw;6-{)r1{O4%absI*pv$m2w5}LeSY~uB)~Q*h%)Ldw-6w@9GQSqY z;-qS^SYm&hTa%r8xtsSctL7=lXQR{mSpqy;`Ie#cB70Sq`|jK;-3o#X+v|-RE34+` zk@Fqh3f8j9xP3a*o=91`(P&EW#SfO5YH3dNcN-f@iylN|mW z{FBhkh?g{_q&{3g&zwj8^SlB zgs&kmhWzjkDB9(_fhA)o$r_htjmyG$V<-=oj$w7Uatu}BW&Sa&yXu(vE8k&+_+Cln z#r!u}f)Xrcn_R{&R>oFU&OlaS6Ibf#(tFN%Ki>@)!uymmk{}dkBw+{y4blUtu^ht~ zaotz^ocp+UWCBmTcnuqio(q4El1bG1a1EO|&L781)!*_2u!oOml=G#0n)&Q`lxNBf zzSf=iJ5~^YW&vOvt)i@L5*<14UzG9AN$ld!*+m{L8^>!qW$n$Zea#y?1_XjlDB!!2 z4puQIVP8^An=^^!kH2um4HSlAA92RdNsBy)v0Z%!e@3D}1{bQW-r?NTS?z$%WFH!Tb~3hCxVwv^-O%t_ zRy17?N3*$%iDJ+9a?r||M6+Cc@r~n;l%<|56M6rl6pS+gD$T#bL5`CXu?_TOeVprk9K=U+o|QYNNS>la6F7Js8^sePOFmVpo(Y2kKmzBANdD}*^84+f+DgHyXVZ#d^7XSoH>8| zv-SwUIeZXD1W^ew86D_kNUiWyuISvTDwoSELUkBAbDE(!6AaPOv66%?hGU*m)6sNA zH4U|CS;BCX{N{3++sm`O5r>3mHxiH~9FXx8k_@k?W?iZAI#*O(bFEa(Qk$Anwgj&U zOUe7&Nq>9AGFP>Vuo!v*z2FC-Gb9UJagZRJri^-Ir13OE2X8d=YYeAHeI+lWqGM@B zH9HovTWT7rqnU;>6)ds|JcB+7{W6}#0KbhFBkE|^wTvGt`hD@wtvR)v-A!Vp7lfP1|L7T-n#Qm>*nnzpMAY?<4)__+kgM|RqONbTkC5ZH|{<8?$b?q>+TPaAAOm? z3mBGgRK_ukP=D-n!4T7oC6i$!OhdqQi2Hz;u~O&xzo<_1boif*Wd`-W9{}DTb@z zinRIM1v0k3%qgyDO;GK-ax_Ey(G)M5O-mJ1n#=OR?PRo>I_Wy54xThk$962?Q#;REpO2NyLEcjBp^%C{)?$2b}6fV=04`ww@jA01NJAuNMP@V$poUb0#f7m!N%7iE#61>uZaKXX}?Bq_b#OWWS7S4w32;& zyn!t3hLEER)5n@7ELXR8{1?QJA73LQJVb94zaw*x&0jDy3iGzU|Lh1zY%=5VZ zi$=50{!zyJT9&cQF9Hd#_r7!QJ@;_>`_K1Z0G{Kij0nS~+3RYDyvsGyu`SovOxrUD zw%_tBerS1GB@ADLq2Y7iN`gUB5ks6IUeW7D^(DiW{{M|iqEkXeQgo8)Z>k3(aZQCP z63P8Nz4qZeJFQ{{DF%6^v8TVQ8VuX-CmE?AjaeBf6?3@35Hp>gYcb5_@_L6Kan0dw zTdTJ^mg$!iWN=f)f{H9|F-%_os_vjmT#mRiu9nKr#VUJU&qI5UWCwfJUF;SuQ`3CN%xL zg;eQ%Bspeu+qDh`-InFOLY`~RSopX3ioOe&fMihwxdf zjqqED1X(4`3a6Mq0~7ejCHy1Gs38iEQ4H?MPlOT^HxQ*SAQ#paPq6qkxE>#OQ7%Ih z)Uh65Hd2Z(*NBJ)iLyW@{u8Vj9U^Kpr6FXa85@#bSGP!DlV*oozaAAAe^71uZuA8A rju8!lWhn^cAh;h30_7Ad$CwgSLuC}&1#pN=hn_-uPq0O~2)6$M3H=yp literal 0 HcmV?d00001 diff --git a/version5/krpc-core/target/classes/com/kama/client/netty/NettyClientHandler.class b/version5/krpc-core/target/classes/com/kama/client/netty/NettyClientHandler.class new file mode 100644 index 0000000000000000000000000000000000000000..a6cecd45981413cbb45afa454f8b9348a1334d8f GIT binary patch literal 2135 zcmbVNYjfK~6g_J@ksqRUZPKIR154qzQGvq&H*Athr1lMKs- ztytXiP897>wJdHbZKKyWd2R6a#Om0-)qSj5U6VTulWT@$cpD7K^5T9PX@*>Dx7Olr z-?m(yMHV>;vW$0diQ#(W+xHApdEj}D(eXXL%}*Gl*Q)9BomUJi<;IcvT2)Nd(v>#t zvGnR@l?y9^7_*0U5s@FD7&ZF&6Iqmbb`!_9Imz|bjje-+abW@KE$ zb%vQy?io@lrNnTrJPvIv(nT65&`rE2;e8pmP-2*l0$tokWV@W;PsMrK=^Sy*t1j*f zv~w~(z-@*J&9q(4FgGT>E#LDUUd>pT`u6B zjL-1}gB+H-#ntW#!=qU3hJd%hgRmTwTp)uth8ZMD20KZjD zDMC{7j!6aQIv&pjL)r;@o|OBS zMBPt?Sfh>|+{u}?pU1y~nbFu$7foYD| zoG(akkZCTR;xfaXQ%ohWe2NWzfhk5{ifExt?xTSDhs4QV7;rTVn5`@=|AEzC@Yyh{ w`53F&(0}9$X?zo~zeyNoDHZX{6c*m#{?C{fu2`%@9HsAg3%%6Y%{2PFqcqk)+sDzk;0Sq!sn~r94 z&ueN%Rkv+!Y58E@v`x>{E%RIMG7PSmv|VM0rqVk}#F3C7D@a0NnCg3k>OgCLQ*rmMey_3isOk+s!`*brVD5Gl9MrvR|V#m zgz5h<6nJ03YZ9(1xP}P^^)yJHBgY`=<#Iu9cnlk<2d5$(LYO;KEhb+A^BVVImZ1uE z%`l`08|_f9>6&4g-1b726$RJwz9ftT?#Vg3$q8%Iag&tE%ntt6sCtv+8%>%h2$;gf|qt ziR%m_2m05#X6bfCySsP54FWjsIVI0EZHhlmtZpj=(p(a6;cW@;D0mm|k%#US8Dgfr z?=Z}#dW}0ILA+dgM?#J$*^aaRjlt`l>DY1HB-Gbk*Kyl-qr}v{*B5!+Qt&=LU>M-S zh#~Xh6t=6b^H|@rIAtMIz48jyu+ESaNVeLXob(X1--F;g`4D#`Y%2H&A2VF+#%4n| zJjZP^B&Y*}iqdn_nPlG58x10Gw|IM1x_fSF`&`WEd)Wx^cLr*TcN!RzS-B zY9HuB7lbx(b%lB$c*heZvtsMskxTg(PYv90*OgwSzExeNi_0bF$Tj$eDd4`+(eWMr zmIZ?`Ni>Skzr=7^G(N3jw5E@V#scj$ni{kr!Mt{NZ?%*?=JI2M*2`)!)zjdJQ9%HIoI8DOYQ1~O} zMi+(gQ!Jn0Mif8bTu}&DmQJu5#nSIjC>;q8@DN!<{bVzcDTzxYy+)7Y1ofRte4iqn zIntWOBh26cv#R6gccVJifodWQ-Eu`g|8A=|lRom~ fYIOb?Hh#jKa78%N7!+uXB7H%-2;KEdfBx!UF>p>_ literal 0 HcmV?d00001 diff --git a/version5/krpc-core/target/classes/com/kama/client/proxy/ClientProxy.class b/version5/krpc-core/target/classes/com/kama/client/proxy/ClientProxy.class new file mode 100644 index 0000000000000000000000000000000000000000..680b45a7c06728db595a361053a1462b56fbd9ab GIT binary patch literal 4534 zcmb7Idvp}l8UNj6lSwuMBt*akq(MNFAgl>}&!) zC}`^grM}P>D=k_qSZfIi3jx$V)mr<0_urlb`d82K_-EVG-<_Fk_64Wpob1fK_j}*( z`+fJ$NB=$lE`Zhedk{q^RuE88f>MFXUD}`)j%u+;cysqILw5v9SD7)>StC$fTi2za zOkk024TSr(0WGXYO(W)n6NWu#>W0pD!w&Dfqr)3FyW=1f6qlm{s)9)>CS!`g+*}E9 z+uFS+-0X_mBHslj`BFV8)Y_WMl&v;PX`_Ow0*2ag-8b1Bh)2WihU4tn7S}z!K~$nz z#^E}FTWhaLTV0oQaJq^axL%-~YCMqw3-aVl*pSvtYi9~9&-G6?Z9Qo^-L|3iGp^0v zu*n-TN`q#PVFwZN!h3^?Irxmgbe}DUPGCizDgT#(#vtb6Mg{X!%*O(O5X~I0V&MTJ zkq8|M^lBAr@OhSNT(h+S#-yqC?hKt?H8)??wd_bZ z5$#>JE8J>DB8J^qhPw$bmmWPd_TrnPhfj=N+&B8n$$wpZ>hj66W2YXz{OTKHPd<0) z!jXoW2Og@x7jdtGFRA!4G=hAtEP=p~X2%3>E)b3aNz^4FL08cu3#*c4)RLL)73f7o zL7xf}yS&25isn^dn;E0`8G8RV!?D={rWG)!Zf6;y0<%Ylk6b?a!apB7$luGuk6b!* zV(jIEqX%9Y_cx}(lFkOqSg)nvK7pECe6^*P@TMeNo~tm3IEv+uqaukx=6Aw~_4s|| zF4O7r&AQiZ4uzquF%M)5;eFw>PsFWQ!eBGlO+=f3s|~w#Bkq`1tPJ-P-ZNbuJ$CBy z>DR_xeo)0j*vrx|Y}+CRuPhM0{N2o-spI}`#Xc4L<&j{LZRnQWvr#jnN!zHvBlxO< zuc>$xj|t4ni)p&Urn{R!g^Xi5!)ewPvGfW6ti*)E|70+Zi)REM6wy-ihCopI1P-^t?;nC+0j-Edx!&!6bKx%B?{!2&q%TW-A z@PdLDReS^AB#=z(jZAzNBh?LR(WJ4tmm5^ulG%)Wibh$3FUg$TE4_GG#SwhlvvR%W zFi*34kPp|ghaFY%3SJeM%sO*Hv(?hv78}ayS{X}3q+@tZ!S_@g#|eQM6ZVcbQ~!)r z88e35*Opkq(PBEI)t#9%nL_iCJ|lC+<+j8IEnSnzdb<|ui4vwVW0Cgi`*=ga4^+H~ zAJV*BINGS`jwS2AoGyE4N2JTMi=uNmSv$i_%6xzG>>@CD*)gTnj2Z38fo^s+tvkvH z5NmZU+QsvQ9Q%_2r_W@^o0nJK@!XSZBS9=(vIzy}m|hbGxkzaz%q>Yb_z7M9lt7tnjF-jig0<(@mw=!R(=rsMZKPKvuB$RW1yq!J zrx6xq#_D)h_~tU^njMkHwE~8-7VPn3pClSC*c|_qovPj;0+$V2$?vpnW zJ_DrVwUXxTum@T=A@D_ik zQw}hX66RCG0^W3PLLILRZ*x|q7D~lwoZ&OYf2o%`FoN@*Zt*GS%DD2$In>;e!c8gg z_cs22E`^ngQdq^;wJEGm;r50AZJF&YwKkN5N>bSJ5~|)p*VLV%zzFUsh8ULSh5{*c zkH9FxVKm9*et%}57#9$y4|Yh(nwCPMq4YQwHYk#6k4qI&M({uYQuq(Qu=kWXp4pS!CDBWl9>z3YkmlnyEWt9Y#ByxF3T)!Hf$dm}2-cBzBe|Qf z7tMGY>)8u8un)Gd$F$;gv@!1OxQNa809)`ew&5SR)7_VQP)=SMi;K*Gy*!D(gLnBc zfV<%Dm3(^-KXUi)6Z{xI;cm&fpWehpjJiB|zaZrtl>Y~t z6wFny)lo1s^WX9#!xJ;6Ojp+`lr^3UA5p`{_?4Fn^r?i>f$D`Rd?)Y@7VRir*inBD z-)*~PnA2X6CxO{=k_c{E>QY206QwRrfcP{||?CK+XUF literal 0 HcmV?d00001 diff --git a/version5/krpc-core/target/classes/com/kama/client/retry/GuavaRetry$1.class b/version5/krpc-core/target/classes/com/kama/client/retry/GuavaRetry$1.class new file mode 100644 index 0000000000000000000000000000000000000000..7b72bc512ee1601902a93ac2aaf6532514b4be52 GIT binary patch literal 1343 zcmah}TTc@~6#j-*wwDzuRJ?1&0xc*DiZ`}a4e>@QB$U)QlI?cbE^c?~?v{iYpMCPt zAK*KQ(Znb|_yhbQUjBsf%(mQw*oSVb9SqB6O)6Tu&f|xPpX=Bvgjf8WCxZ z;ab{ab&Z<=u@!9B4vGwsT)q-VANm!fR2;(qL#7))MLEJ^7$^zP(tKxiW=$&z%i^9M zLz?pae*Nax`?o(gH#09^WqxeFOJWEo6r5CX3a2{>EIX*Ts+w09PK|q=lJ5rrZ!}3? z%Hlz1vCwK%xfd#(Pv9)hDLAj<0x}GJyM>j6ONx|r!)fuwRffUbe7>}QTTfybmlR}G zj9`><{HvZJYP+j~VJx>Nv>bT0Yw3GD?I!B^ih>-&sBG7v)r4nPvi*R&vVf6X_mj(4 zNW`3|afbeq?ecc<%SP295B1wLoQmPua@}!8DQ_@4fB=Y1PC#y(n=}C-Fd;%N)4CO*)HbZuI)|uHr zYHgKX5XIiCm#Wm-YVF0AfHo|br%!+G(|>~$f9fy&x$S#qCdq_d2F#OZ_RN{@e3$q8 zzW1E-kN;l!8-NFKC5{HH(Gb(I7L5XlqsFX}EEuJHa%}Rb%(?=NgJ#Ke_Xw=%?l~Ms z6Ph)|b+kYi*p{`5$-L=KS004Zx zQWig>W4$t$@N^GP?LVHCW!JPyZ49DSM;mSvSZkEag*kz)CFPctFUTbA6s=Nn(r{$* zF&e>joVgal62}K)51B>z1mPCYGW&k z`>qucx zpj9Q!)08$HSC&-!W+Jk-O7v4iqdI$!jxXaOHt(b&ics^`D-X|*v{P*m*r#I%4-3Sy zMxh{J)QPg34FDQ1XNNqfQmU~P8x*DE7m1EAg;9%~C$qf^Xm{4d2x9Eg0;s>wRRwNZYnpm=6RXdmsj@L;CzpELA(x zbGQjvwtG0te_uJhcd>Nwb+zlvg-Ql828wt zrpEm2F)kBFHO%NJpeS&!Dxy_cs;yap7DtwHL7931-w<8D`EF!f1z_nY<7w_L%)*eB zBmUgghvRVIYN+U##W51VNk!aijKtbJv^u3c^xP4eSzDrFM|U{*0pTz`OOT+uU?PvR=gW3 z7>+|&u3wpA4*mGor_GWatrRDveMmLB*AgU^!-j3D_h2&SPMh?6t9LY5l``+Rv%al@m)`D)5xYQ8n4|>8Dn2Z7D>jS3uDD4WZO5lLLLyY@g=@?usBmB55y? zC6T-(r##)>VQ&eZr~@Hg*_gJ%_|BD=Fn-9h7qxn@uJ1u}?V;{<8P~|pj2Pu0Z5;)p zIGHoL>Sd&B*HTVy)`QXeM!j|nT;jQRISH(uNN@Ui(yLz~fwG<>0!_B`-vsWC!h({6 z-2ME6dpu)R?5sRss_I_9QmQ*upO9Zhx&Mtw)dBP|vCpzxho{7{&(hBNM*KF8_wc@k z-|6@Ozh|0P8q6%sS~D`4_F(1;V``}>|>mPlS(QCsT7id;zfsD!9Ljr$fr-dU5NUY}w(fTmXz4*^Meu*~|>VN+F zO)e0o=y@gjk&f5!dIEnHxW~V{)NsUdIqAbCp2s{^la7Weo3thu^W3=s1-vSPAie1FlV`!VKA~#Pn{n*1}WtxVRTRVAI1vX&~FJLX` zKsz>bv;{rbitW6o^rM&3eN??2k5gK?KR_4v(zBOvnp)^=5QrMRj30Z!P2eZ^DMRna zHv9}fr~X$csRH+Be!-bnIdccZ|7aklVNAn>22@tKz#KVN@kugp$62)_>(GeFwkhukvL8?{cI({tc1% d1O9}+c%!p8hmV;>@ZR1)DgW;RF5)sa{T~_^1nmF- literal 0 HcmV?d00001 diff --git a/version5/krpc-core/target/classes/com/kama/client/rpcclient/RpcClient.class b/version5/krpc-core/target/classes/com/kama/client/rpcclient/RpcClient.class new file mode 100644 index 0000000000000000000000000000000000000000..b6f7885f2c596cfa758563e38e66dfd3d2255d12 GIT binary patch literal 207 zcmZurI|{-;6r2||@dM%=Y_xG}308uYAjA`_n@32@?#685;Ve9WhY~jutZat)z|6ef zk0*c~CJ_R{QdupoQcI!AoJ}L@O7$$M)c)&57!nqRyh~9^lZn%%VAT+2j!l~It!3xc zc6Ib!+8UwRIhnEW1$`Q?|7NbT#&JxTo^4wzKIA16=D*=hw;2)$1AiGH|6_!(V~9Kk G7=HlVEjt7N literal 0 HcmV?d00001 diff --git a/version5/krpc-core/target/classes/com/kama/client/rpcclient/impl/NettyRpcClient.class b/version5/krpc-core/target/classes/com/kama/client/rpcclient/impl/NettyRpcClient.class new file mode 100644 index 0000000000000000000000000000000000000000..9b54f6584288467ea4371775e9ac24f3d25a973c GIT binary patch literal 4718 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 literal 0 HcmV?d00001 diff --git a/version5/krpc-core/target/classes/com/kama/client/servicecenter/ServiceCenter.class b/version5/krpc-core/target/classes/com/kama/client/servicecenter/ServiceCenter.class new file mode 100644 index 0000000000000000000000000000000000000000..fec34996abc937f64943bcb7df5ec7c2a3d613c1 GIT binary patch literal 260 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 literal 0 HcmV?d00001 diff --git a/version5/krpc-core/target/classes/com/kama/client/servicecenter/ZKWatcher/watchZK$1.class b/version5/krpc-core/target/classes/com/kama/client/servicecenter/ZKWatcher/watchZK$1.class new file mode 100644 index 0000000000000000000000000000000000000000..c4bad2e2a388afa1fe2418ccaee97116a39c76fb GIT binary patch literal 3154 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 XL92{VFDWWt6db1!DQ=yno8bEw>M@x~ literal 0 HcmV?d00001 diff --git a/version5/krpc-core/target/classes/com/kama/client/servicecenter/balance/LoadBalance.class b/version5/krpc-core/target/classes/com/kama/client/servicecenter/balance/LoadBalance.class new file mode 100644 index 0000000000000000000000000000000000000000..7da849a5594749914f21ecfe9709a9edcfd0bc4b GIT binary patch literal 337 zcmZutu}%U(5Pb{63ZkL$4=ki{V`l`|yJXqp<0$w5K8iSZM?utT z=Iwj)cHSIM`vZU_?lgJ|&$il_Z`zQt!E>3+mgCM_w$gJnD+*LvHd#gPt?Mhw?FJTTYCTi literal 0 HcmV?d00001 diff --git a/version5/krpc-core/target/classes/com/kama/client/servicecenter/balance/impl/ConsistencyHashBalance.class b/version5/krpc-core/target/classes/com/kama/client/servicecenter/balance/impl/ConsistencyHashBalance.class new file mode 100644 index 0000000000000000000000000000000000000000..66b6169bbb7e3ee7b5da2bbc531d3723a47c967d GIT binary patch literal 5355 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 Gp#KXwV>O!q literal 0 HcmV?d00001 diff --git a/version5/krpc-core/target/classes/com/kama/client/servicecenter/balance/impl/RoundLoadBalance.class b/version5/krpc-core/target/classes/com/kama/client/servicecenter/balance/impl/RoundLoadBalance.class new file mode 100644 index 0000000000000000000000000000000000000000..a0c60f2047581b8df4cc2cbf2334ebc891d3efb2 GIT binary patch literal 2805 zcma)8ZF3V<6n<_RyKS z0QBI)7$T@q5LNLQY8e{G_ykWI+$^RChR1~FGSqhKrta=#sOd-zDyU;fd7CP(ZlpEK z)GD?uOgGJ4tE6k`Uhk1LT~QQv3<_d0betjDkxdQOqXEklELYKp6$}~8Dy7GHiKjI~ zmq8q1Pw1M^Xf5pYFefIONb9Atkv?Ko%tFrMh5f#jm}#Sy#k(6g>X04En6{ zT zV!nf-I|oW-chZydq=E#)hI!4ih9QdF=(UTL5=n62v?j`~Zkcu1z>w_qkrM_1B{XiD zmYWzB3A17t3Cm6h88?P?NJ_=q8JatMc3wI2uC1HJOlq)+3cpE32T}|%UM?Gxy$+R& zVOOx-E3B7HDwN$w#ndE~H0?cRa(i;1ENrT|osmWEQt>o4GpI%3_L_xb*g+AxN~8e zcqC@0uFPKiX7;1+!klHW7gZdR!D?+$vL-|wUSepk^1C(jMcC=q^`Gy2KXdoex6(VO z;xPIt3r9agC8ATEjo}r%s^Ex6#*Qn6cv-MOOUrs^{r{8CA$H}A~R9(R! z!?vXz^QyyLpVx~fcPln=OosYUHfDE7=Af_lW+&mTiWFfjXq;l;8vCLCeVuoZI*rNly;K_VF4x8n^Ky{d?uASi6)b$b?qev_p#7eVQPX=U1!4Gx zMuV4z3p^PloZQN=av@MAo1%1>&QRA)Bi+9^VtK1#YvQ0TYtg)Lj&G5f(mC62S+3*S zynI-=qgKILUJJTE9JmT9Dkktw)uhzT32R)Wa~>t~;3M~XG+qk7{TuGmz{v+V#La>s zoMg_j#w+E_gMx=FRgOnV`0)0n|L8+L+BaO4AaWl!j67XRnMTkVl=oj?SL0wrOmM;4f_Nzk;1jyEji`PYpKzLII+X;H!x=u!d4?Bj^O$v4QTT zjdUv{JtCbxSU^PniSiR~r}R&O6REA_I}D9z6)2KVT<$tSw1tNgX{Zl`8X;SeFnpQv z?FZ_c8m?pDH|&%Guu!NKq?bvy2rQK8DYbr^Y^QOCKIh01Bg=a@j|*7!KWMk< ALjV8( literal 0 HcmV?d00001 diff --git a/version5/krpc-core/target/classes/com/kama/config/KRpcConfig$KRpcConfigBuilder.class b/version5/krpc-core/target/classes/com/kama/config/KRpcConfig$KRpcConfigBuilder.class new file mode 100644 index 0000000000000000000000000000000000000000..444ef885ef6623441cdb7c5a55ea8c625d00859f GIT binary patch literal 2349 zcmc&!ZEq7t5PsH}oMR45UYay1FHSJUHfh}V7fhhoC4exgL{O@#pEl>MedK&=-EAVJ zANv>jr9Y#UsF0|{58y{3X3m%T&|M;**Xyc4An(h85}56bqimbN^?OamJ!hkY^W-(u0mYq5cs=lq>Zu@d zo3$~=vGaW$8b0@djRiJX2Pz0%?X&Ge8%t~}1ghtTCOBl{M>a07u^6hrm7e>X3fO$f z#$`62@wDu=BwgN77FGn7kH1|JWnAH!RRn&oCNhohldv4+o-n1I{+Fe;GfU0<*v2(f z1r`T#Pf-sXX?}7|pR~|O--H5xy!S-YHK0DWb(e;s)Ny^awLjQZ!4tXbMPs5J>Fr4B z6@ACXyy?4imG#c?y-81AQ=pY`_u9LHw38r5eSK$?)4Tj01TIxiI!ZYgj2_K}z&!b; z9t}r%<^b=JWt1$&tnSeILTA3+^C36VNm^xTJVhq;DM8RJP ze%KH-0sDdPtDx=4FjO>?GpTSE+5%ToRn01vg}XEnC%Y0Ck1C55qOBagd5d-;V$yQV zoslGRj3jYlB#8$jNz@lf;yRK=TEzGesFBtnDcDkf4)HfBIn-(A-ypWVPP;vFz#2ZG zFJeoq)6R#Rv&YPo_qUb50vl9dH#TjTWJENJ2fw*!E zw2%!nKQR!Oo`IIKffgqQ;@&aPg>0bZiGjHJ40Jgg=;FjcJUtBbDL$i}yS9aC(tygp zDF0l4MicN1XKBsQny0l$Ynj%?=UC024MwE%|Bcq?_#(#c(46qURXTqx?h9N6PC{Od pk@NVHuyXhcUq`ZxZ%7s>!nd?;;d}a;rT+z#affcz!VlQQ(i>?h17-jK literal 0 HcmV?d00001 diff --git a/version5/krpc-core/target/classes/com/kama/config/KRpcConfig.class b/version5/krpc-core/target/classes/com/kama/config/KRpcConfig.class new file mode 100644 index 0000000000000000000000000000000000000000..e7ad52a3e0975a49f4b0986290ec6a5a32472346 GIT binary patch literal 3648 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 R;7{}`OyB;DzvAzx`#-Kn)AaxV literal 0 HcmV?d00001 diff --git a/version5/krpc-core/target/classes/com/kama/server/netty/NettyServerInitializer.class b/version5/krpc-core/target/classes/com/kama/server/netty/NettyServerInitializer.class new file mode 100644 index 0000000000000000000000000000000000000000..829a868c7d43baa64138f674ca40c5b8eace17e6 GIT binary patch literal 1682 zcmb7ETT|0O6#h1?4NapU2wo5MbSj^~E zk6|P6vMc2Qm+790a`hj4ubDpI5=Jr3^POM_C6v5~Nroixr5)%cotNr}%G2nZEbpFY zWrIj2VGvCe)0h!4E8`mG7*c<6ZTM{wr$PA{`uPQxVeVAiBa0e=LFH8cad|n=Y23fX zmTDD;!edt%=KT`&=BMN(q%baGo;d1OoR%;pN>P(>9XA+ayd|2mq9)pl)oH5IN}eIy zXvqEy%~tlXv};zL>$^6!=wQY*bUUxpRM0sPhP{2OO0CyMM@uc*2u(8x#y-Pbk6|%P zwOgJZa0Y4pwPBV$%XO+EmT1ym=|{R|>&~9m)G&;7AS1x?^N;4NJxBKrE0l7!>$NSs zFSQ$WBzKw8mU8Z4#V|K4PUxuLscq~m@USsS)lAU~NmTKcrCkVNUR*lgq|;l7Bmtf# zkHHwWQ{k@=3!yKF79yVztK)p`7{dZyG!>(TI>t^5;|29Srs_xpigyGlV3GbK^d5u8 zPQ(~B@HlQ`0$JMKB5AyFHOcPKiGsjg+@nxMY?9Y~LU=&ZFiFz%QRma22fa)hfmTVa zBdI1oVCv+)2-yI`KK@9gif8^J$v4B-plh|&U^m)=a07lZecTl5W*4>15v~T zjz70{EVE+O%I1Uh=XS{xh|M@P$D0)hkBu+)AdVi1gn=Xsfw5A(YHnFo%WT^2j_sNa zx4z?Sa8e}glOBp-wVc4Iz~Hcf4{%nXuR~5?qVRvJJ9HAzd4U_9Qm*CM6{qTW2T@$&Qb8@X;+Vj> z0M11Nm+&F|S5k*q=xP_B_fef!rEuB67{&$SW!sytSk0!uiLvpvocW7n&Eg}8D+aE@ zWK0fns)Z!w7pKrdMrT1lim3OwmDn8XyOB|bKA6Q5A#pfH$V zmS$s{RdaK!O)e5<%;R$bvopCJRns~jtukZ8U0sL*lSh~c{5TR_epQ*D!W{!&;x2>1 zfC`MZE75));krOi(J9v~Z`) zkF50yR|X38l2uu@Tu0G*G2(4HOmwEJO#iEU(85W*r;pNp!E_`)US0zT>F$r!=Bg`?{*m7%s4Xhj{%&IkJgEV1ZN!U-9ED;Lmxz_<#0X|IcQ}e}ud| zg&gmZ`Uzn^z_rXCL?*innHk4ja4{N`&ca6Aji z@jUg8pa&Omj7>F7OP4ST6PIz{-}P-^5f4bwn!5UkK+P1|5a$o^b%05v5ThnwE&B?i zuaH>`{?~GmbR-u|N8e7z!qak44#s}QY&tq9Cv)-4??|QNWX`_EjW8aQKI7AGkXs=k z`StE$X81L3NARN*zr6cX`4GW@4;jWZ%ggZ!M-zEWV1@IixbFKfOH^6*>LnD(M|At% ztl<&9@x58YG9J^LIKJg$FtYvhcm=Dp^Mo=(+zWJkN1kBsd+zJ15r zrxF%l!7%-eIqF|WDTokmQ;*7iQn^i!zX;)1k|E!jm=9&z2dxZ}Z69iqU{v4*HehRI arcbk!sBENDP0m*nWr i;DhVe18HSrWB@7y+RVVf2sDnBfeplBU*LlU?kW8 literal 0 HcmV?d00001 diff --git a/version5/krpc-core/target/classes/com/kama/server/ratelimit/impl/TokenBucketRateLimitImpl.class b/version5/krpc-core/target/classes/com/kama/server/ratelimit/impl/TokenBucketRateLimitImpl.class new file mode 100644 index 0000000000000000000000000000000000000000..83a936027befaf0dcc86197960989ff79def5dab GIT binary patch literal 1290 zcmaJ>T~8ZF6g^|FS=(9jSxO9Xu&(Qz`Zjc{MJmk-^26@SH(hCZqDK)}$E0-~-F`z;J;xU4G=r0c~; z+BsxlSUWGchtW#UpT`FnHSwW^F^Ixrc7M5a19lK zu`p^m9e?NMp3?|hEfw9DZX8BO0=broyDovLO5+rBh50+RYU95*Ynr5OVHz_`>4z;kRAd z*+tjy4P|~kJd9j*-_zxk&YQPc4a44MIqd?Ua@NPS%6tWe^&lsf6G*$Phc2-k71}i8 zGjO=haTjo(rk${Vfv_izVcM6DQP9hveS(o=6o2G#3ZHQuN1qObL7*H;=;x4);4=MX zT<1x-3xylF$(@eWR7CQm$!My#FyBorO2pB4p8dG-hy61KUt-99Olxd2xiS6f2j*WY z^Q5S_nBP5rfm>&`!LxZ|)G)rsn9bvYMi@1|F}6=o(&Wpeu0hiL-^onBDrSCXLIW4k zWYQwOPV5$BVw>1WY&t1)9tFlsG5Q)ys9+RT-k4#@vs~v;W7HDMe8gg&;5N3fz}t7; ziBQ8Ny%z76@FhN{Tq#{>PnZQhmtp*cqKVYss2SolvTvwe%0!kf2$NV@EK;6sm#)z2 cUv4;ig2|sy)jbgjRUHC;Yy`i3x=(lC0-5CyYXATM literal 0 HcmV?d00001 diff --git a/version5/krpc-core/target/classes/com/kama/server/ratelimit/provider/RateLimitProvider.class b/version5/krpc-core/target/classes/com/kama/server/ratelimit/provider/RateLimitProvider.class new file mode 100644 index 0000000000000000000000000000000000000000..d8bfe2cab9c3779309b6b2e42a0492f9f63a4edd GIT binary patch literal 2264 zcma)7-%lJ>6#gzt85jmwx0G6J>C)250y3>)YrBXpTadL2McA%lt?gy@vJA}5Y-VOj znx+pX#`vVZ*hGCaeKe^*8d^w<&&EHaK>R0+XJ&_GpoQ=KA z;9cC6QHO|xdId+&zz|*HD_k|WSyXS#FA2?MXc*Q_-5p_w^d|BW8X3;}Mio~#RLwHA zifs$iRnxWQ1a}rEd0B>pNE4c&NN7>eiZ+HJ%_^zOyu?*U*ek+TZSD#~FX^sYwyhPt zK+p_9Sr3{H5C+9F^D*+v(3aiFfPiF-&hm+U=;pMV%kKtl()yO}~tZVCLF_{SQ z7AmIZ>XxaF2a9Agj-f-saRsm91VhWN7!;IJFIQZVSxC)0bYO;bI7C&h>DuNuk2b#jIezQ@gWK_quYcNDUwgjx z)#k&;TR(pJ?+@Q?KKWwnmxo*5Ke`&f|DYMYNJ!{Y(2oIXsJ%`Z>UDF$Vz?4kFDOu` zWV`eXE0)O17=$X}f`W^9li~Q@5aV2PEt`6vsVLm)Q5epJ!=(-GIMfb%nMvZz>P3^g z6HXKTCJK_BcQY%1mgxp52jg6;nW^;Gasp(WYlbd5;8EP`RHIBv{j&t+OoPSrYfa*Vdwa~K?s#i<3^s_y0;I&dUsKG7%pY^ zPjCP0M%gMPTrK~Xmnw09?;4d{wdkfR>;>Dm2xnK!m}^bySs?Fdu0(eKm4E7kOyd}m83fsTQ!jbS9Rr3WwU#5+O&l$Q7Hl4656sy#8^qaDNhNCmzo$d_l^-uDOO{{A&YfoEus;4gGO zCcF-wKQA(9{(HVUs`h~|Vqz3&u&C^*8hE`+k-eeQxsH=_(Lz*SMc3qS=#HK18+eA- z>oKt7yVbWmMLBlSqj}no5wEg+&*X;m824h3O)EaY1ZB{OOihn&KRci)*8CK2{Elu< l2o_2_M(#c&tPaTn*Y{7#N=+_dWMC~WNh|`=EE=AgVL+<5B)@=>fms8@;R?<#ElN&x%gjk-WZ;7v mr3W&FjgbMU4Cn#|1}31%tPE@nARE{jIDjII44fcw1}*>>NF{^- literal 0 HcmV?d00001 diff --git a/version5/krpc-core/target/classes/com/kama/server/server/impl/NettyRpcServer.class b/version5/krpc-core/target/classes/com/kama/server/server/impl/NettyRpcServer.class new file mode 100644 index 0000000000000000000000000000000000000000..2172d9fa733680c164d5b2537811c74249bc2435 GIT binary patch literal 3723 zcmb7G`*Rc575=Uzv$9xW$AG~x4h8~PvWXfJ^1y}!9PBvAhQ`FyebBDtwY+$(RaYw; zCry*IC248WBux`qm%KBRPCFz88b2`8bfz<8rhf-^hM)2uwEgbxT9ReCNoO>=d+$B> z+;hJ3opbKL{(b%L01n~9Fscwx5L8i(bpmx~wFxb5YMFHWMDnbjas<|O8X3bmED&go z4%Q%q8U!Aw7Qr38UOdHqYIo+Pn?d8E3&zkXm-EpR#%%%q1ErD7oW2E$xwl!f4 z>o$)k%Fk#0y|_G7BHN*0gTNlcif5$OcxqJ3WOOs0;Yz<@9h=ZIPQtRXJ+_t4h7mzP zT6%|ydTgZSrj-_`Pgr(3o-;@8KO0Y2>9lTlgs@p)i)-l8rB@a&y#M82K3#n6)5Qya zx^?}<_1J=~3K~`1i6%l*ep(=CWJWB3&8>+dYy*yMWYQhcK?ThMI|~*kEz8L{ww85W z)9s_n^Dwp{Apcra?7&We>a=u1V6-(+IKAxeN=G}sRv=oGSknMvd$+*;Rd?mA)R^up zL+nLyz+LO|(3eKT zIyaR`k!h`8t68SRAr*)5h(N@@+`CkurAU;@W`%Hsmz1T-?A0$9USIrVTBgmbAAWUX z`k80L=)z+Pj;ZKIkH8(p%;?QHI+KoEnb6F{6?|L8lM>&pYvC)fK4n@t-9so$>`tk83WEZn+-Tkz zwk9(I0~NSZu_jeW)yi_w!4ST)x+tB$cKhOG79|DWB_~!wX50C!qYocDm(sJ2VP(Sj zK7OF!jEW!PM=NUjsZm?k$iqk~Z`&-H%kxbHtfCeR?jZrFn4WMSeJCxW`nO7-#Y0EfzVlqKd2;_7bB@ zh;RayimVK7wHv_@sIX-5s4KYP98s~aybU3_op+MAwo>FzldQ?jW9CpDY#ir^U)Kf$wXQp?=xVQth$ z@}@Z@%SK6V=N-e0r>u-0!g!%-vsy|xC@WlfGV;1$){?i0UgDOtayj>rBJhn%2aio!_LzSxGT>;b zu|6&9LZIMnhH^#jT9Y1N!JK398PWHD^u?mvm79P>E7X}X{TE?)z{=Yxz1xuS+303M zA(Hn*d>#W0mYg9M^H59i0Ea=B%6RRUE^yS%_HMXHc%V zvM=+01GNB62(q)*pc$`l*5FCwH+YpZo^!PmZM=rpJ)PnPPpsqK7Ym3C&7!u?AN#MO zu71-z8mh2>ZQQx*V6ZXRzHbKG>vzpy_dl?00nwq@-=NH*Z3g$8jx`3GX0U%A4+wmQ zPI>;(ra2s)$KwG|u5%v81?JH&|0r`hCbbPnxzp}FALFru!8v?y2AW(PZVb*~q%k;; zvq7wu-FmRPe6g|m^DA`cPS`k)A$ODjoyR+n;}u+U#{eW?XIN_B24N@au#3`r_`etJ zXyxNSiX;3wN$Ah;A!OqK&fx);j|ZvkURHqp1o%GcJ%Eq6{t4Hw@SA?&pZvawhui=h zCbYZQQ-6y$XxB@;@ppKWUxd35lrQiWE;1M)e2(Aa4-8oyW--mL0RBkHCItS&OM?nR zLP3N-a2y&_aA^~dz2kC3vg0}xOLinA);Dpe1afv`4|0j?kA1NBB#Kz)7{QS?7wL}? zky812Ojgk;K1r~fkPOir30sUT0y?>Rn5I3#y)N!P>H-$WQLc6ox??zwo&pD>*z7tk zq;1>yQLJQ?pDNtZ{yWOz}A}mnaO0~Lev5Upl7o1L~%e32>b!N7J zh$03hLLz*Hrx}wQ2vZ|~n!>GYJ0qaF;9 zc)C2V8%4Z`O$_Cn3YwzsF^yrZk7pp3nt4jjf9F!ONS6Ya@Diuz7KWWZ+(n)~`$5sJ zFzT?CAGMA6Se&^|%81;XRVhYr^)2Gf^+)p{cnG8?jCo>$9+~d;1ve6*>SC1+HIMN{az z1oSDy`S+TLR=iG5sHSNUWLt|Jek#>wBVU+OkrL6yLke29kqe=NVfl=5a()_paPzY7 zYz^KN(TOzC#^U4&MtY)MA-u(~bb8z5)l1IBE6#}ZBUW3U9ze1XMeHOa_f`YnwRLJLSSID zYq7WfUy3Fvf%XB-VyJtfTzrpa>*0J`&fo>WS{df0ZCM#?lXI>s0zR242bZ{1^u|HY zaU=4Ck*5~-|7d_k0T*3fRIx;+?Ml|ax58;7Zz^hw#vxoZ<5EiS@}(vmLLaftK#(X) z7Z2T#0p6%wNiEuybmNf>lHLxK()c03dT17;)swLaucUaayP!!{#x(TtIT<(VuP8x)uD4#&`kE@!NPch>=GV zu>#&0!Rj{trv337Z#mq~W!=3|@}>q&)KwbDt6pfnJVs9%4ui`Qm^8MdlEyU@(^`tT zhW^$gPB%#tjk^&+3!>PA81~b1FY3{cjdVhS%0r@a>T#5wkC8k<@=LkBJL4D;-N7+rZedld_7?dc=d(Iu(kST!+;q(+-$36l zL98TtE^+O~yfdZ&t#&EPJ*ej26)s@a{6mfQhA3H6V6G-F_^+%YG!E?s=5aI3HN#N{ MmeTkc$pDW33o5t4#Q*>R literal 0 HcmV?d00001 diff --git a/version5/krpc-core/target/classes/com/kama/server/server/work/WorkThread.class b/version5/krpc-core/target/classes/com/kama/server/server/work/WorkThread.class new file mode 100644 index 0000000000000000000000000000000000000000..de933d024b8e49dec1d5ade8837416a14cd71410 GIT binary patch literal 2959 zcma)8TX)n{6#h#knPf;Z<>CeJ zisBtmAARBxSjvF1mdj`VgRd<713s$XNis8;T3D<$Ig@kt-sjujzMS*NKfnG4;3<5s zpiW@T32k1>n097l@`Ry#BSo)J^u|2L(DF+VN49bq+&T*xaA(xGQB>5w&dc%sR;!uxm@e*fK_o;B70cHDy$Z0B8bS6z^Vv_ z$O0j(z#0i*t;AW`IVzHxkV22BSdVsrgk&gST3}__YEH9eLjJP~HV`JM&f8WdZ@8{D zV`N4P`lxZTXt-X2F?m$QV|ZL(DPfKpZo#%(LtuAu@Sa-P)S_#Fs!!x56-lIM^OR$H zMj2si=u;G+bV#5}#b$I1)KBM%?yNv8DH+ZPtSB=)Fmmv;ZWKJzwiIj==kivqzg@JZDpCpb;7JABRqT+K14F)iMTGfLa7>H&sp)g$j;0$4bYPc?-RNa9_+j%5 zXPOJcTArlTQp~hAt(mOM>pdzffcerSbJ^65!;U>~PEjK*nW{M8#pe5Ud>594+sTpD6?k9t#q?8akR9!?=PMRZQSW zxs0pp0!u3P1@Y#OFDZ_fp#Q?l%0#)3i>$0E)wniBV zE#0?56_^6;RgA;-SW%x1=&2Nkg1MS18_4C187;S8*STp#D$nYznkthrKrVDmR_nOt zFnSe*1Z-KB1tw}$Ycz|Bex126vY06!zs`twZxhp$&v^lp#RKws4C}BfZaIvPz1p)$n;f9e*#e;Y3 z)C40mW)~gZ=r?7qG*|B}TcjV@z_X#B=i3?7@sIZ&UX|+PQNc0B`8Uwb_nZ9sli&3m z_uYm#n*JHF5|-R#zTL)(@U!(M+J-ilu#-4?JodZ!?!hL?r>T)d7AfpS7v8319~vl|#5ue} zjO)>cckv#rcd}mI#|O09O^YAmBl?)-^D#c596i8!e9G}NKD-m2RnQZs8t)jPtAA6q zL2f|2JBR3NmR{{0rbh=k^E{v75=MLDZSijYeyN04dh6TjZ(;J9980iTPTqohjaHhl z727Zyjwiw~7dpJrr#nMhw{c}VUEe|1cJgZ%o!-rNukYv~!t5fV4&8+bV|}9o#$1B7QVvQ^h~1ohPpA|<<$`Xa*Mbk|2q_XtH=yc*!FmX#ifvg zA?n4*L38>hPNlnUAeP>I1KI1Gs`I1OKtb^SC?nAkh*lsp`qwHm0^Sl25 D`b{FL literal 0 HcmV?d00001 diff --git a/version5/krpc-core/target/classes/com/kama/server/serviceRegister/ServiceRegister.class b/version5/krpc-core/target/classes/com/kama/server/serviceRegister/ServiceRegister.class new file mode 100644 index 0000000000000000000000000000000000000000..64da4624d510167076ab8b7818c8a546061f5039 GIT binary patch literal 215 zcmZ8by9&ZU5S)#VL@NtFLK_ckgN2PC2!aO|cIRaYmnU4#e4K?J;75rU6%?D^cBKvtD zEN8p#T{>&3G=3`;JF)d3Yn`)OCyC{ziBIz*VJz#5)x0qU6Q+Nmi_ULIAoP57fP9Am LhTVqHV}R%hfTlh{ literal 0 HcmV?d00001 diff --git a/version5/krpc-core/target/classes/com/kama/server/serviceRegister/impl/ZKServiceRegister.class b/version5/krpc-core/target/classes/com/kama/server/serviceRegister/impl/ZKServiceRegister.class new file mode 100644 index 0000000000000000000000000000000000000000..18c339c3eb801a0b1f6658ec8e21ea3179ece7fe GIT binary patch literal 4674 zcmbtYYgiQL6@I_vGRrceZWIgg0uhx z#DEh!ttmA2>B=BKpy>miM$5+TgkZPgCzHbrQtyx&3UHlaS8<8= z|Ity@U}>mv;vR;aipm}3Tj*a!b!A0Wg$pZ@D`Az4k0Fnt_HGO?Eb(jMFd^zQbTu4g z=q&cmfo%zE?(z_6z}PO~^=cVwu$ICZ<NjQHJ{Bmb3&H#C;6e zX52fpkm{#+JYJkC2qp!rC3ky32}KOs{o0^sKp9j#QLgXjy7{B}c_$C5QG=e1w!7V_2Dr%H84+56fu6ZsG~m3jaXUK?)FYuyQd}m555UL@Z9^r<=-Evy2vef??I5 zGQbC zW;{D(_|PRtp>xIHcZfoxxj2DBHoCD#!lz~QVDFtcK5siAXWFLn)1l}@%xFtr8|MKY zU}(LQkaXypK^3gN(QD~#)dJjSD2BkIJ{)KmHh5I&4RK0>&)~BXJ}2WbJkGGr2JjOJ zs(I#gFt|FJI(;p^uBJ9ZsL-A*sn!#oz3@v2$l&N>XtC)}G^Dxg5&^tZ&l#Kth*IKU z4BO_Fvy_gM*vJ)+wFKft-J%{5h@wRXb!{jdxD~)`1kvim0K@8;vC-IxbF(LozJGM8 z;LzcM*zxCM*QVzZbWmhxm|;^2>MwLobl&xd$oM>{Yc3CRBVHxz0|A|S1vU4$o-j8& zEp+i|{sC^pUsL^F(6iiNFhph!F;v~&iDZ{sphOVnfQ*BJEm=(+%}uRMo%LQPo=74P z8@m`gdqf~GbNNE-*hvckC%(W?W??k9A~t#H{cBIhUOOEdyYS)lle3eTXK##H+uSto zG&}Jn()+>4=KuY@NV?wND5>7buS4C2AAA6|d%&|wmM@XVRn@$vU=oSS)a+?2U+1YebL3?syN!oHOdSAxW}6_kB*#^NxE z^7}OzC-8Nu=v>z|idb<9fFHGOjdQT1+lf&^GRavNp2jH&-;nW5VLab7mNLXttI4xw zEo!t;V-)+Ik?}0PO;wNN9hy$+%ggnLY?jHU5|CtcQuRdxWHkW z+7jKRTL}$Oq%u02x;lHDc$F$eqY^e}8|s6NX2DqMRl~e(Xt0;-U7`+}9oMgfx)og& z&v9Y8(XUdbRe2Y4H}xP>Lp>xCpSTL_r7}_a>;o6MsmJFc(Qy_Y(UPR3Le1WiZaQ1U z>~xGyv(mObnGxhpwiv`{)Dn!LDLVd?EGesRXM-H{Cvjxd)Rs_3@ei~r5fd#5*XDhd zB&KJlp(SpY2zy*lnse6D*kygPxP01>?&rHz5sTafBVoA+IH_;YG$U&0N~Dz={aPTp zG!s8_;pg~;gkK7Zzs*oI#|<^SpQa^`W!@S?lEgi2M>xLFQrfJ910fzQ@>2gBiqzPU zv=rGT^EP%3N4WiEe0i#@w<(RQXjDQWpK5TL^M1|X@GvKSLm~ArarZlloF_qIA@~EO zHmha~atnQ`=w`BUVyYCMKP3Ex@^JwYsSYUNU52%{UrIc-&_E$IB?tA9mynKpF|pFL z=MXbA{pRDlc$wb4V#+qq^Lg9z_vovVvJ#ney+A9@k_HF;)|XC#U4zuKJZ&~}5?S8T ztB~F939NAEPvG9N39NH(n82n9l)i!T(km#RLM6i`>~PmiVW$JHo94bx*K%ajbu}qo zLtm?Fk&AV>7X@@JL=o=8CUSl=-EGGP?7~L;fL7;PPT+_55j`!VgS>_xlZy=FxM;b` zZj#d(0L!$jH&P>;#>asvJmSFHsItVLWF+{M@a)kkC=T32FtJQ^V2^NkUqTQ{Ls{wl z6VRp+1*+4Niu2OrN*}mY>HdULu_U2zSUixjSFu#+KNNpla|Tsp>hPTEIp5h<($4A^J literal 0 HcmV?d00001 diff --git a/version5/krpc-core/target/classes/com/kama/test/balance/ConsistencyHashBalanceTest.class b/version5/krpc-core/target/classes/com/kama/test/balance/ConsistencyHashBalanceTest.class new file mode 100644 index 0000000000000000000000000000000000000000..e75c2b623447c3eb2922522d5f0093c34eb862b5 GIT binary patch literal 4410 zcmbVP`F9i775<(VM#$r!3}AxO#sW-Yn?)D{C5X`44vWDUgB-giN!zjXY!8fP#LURW z>5{Ie`;zW!O4oEr7uv)uY0~AK{*u$*`u{Zj-poiE*-qdTj-$1ndP`PO7CDw)ZqDTSqRd(4ik%yE9pCZ;=@ggGnEv8Ow3mj4){W?ZuAx)M z1K1>RpsqELeo)u4zJe)=2xze>fzk2$>HpWKyoLvx2q*-ex9|Icp=-7*WG#NS?dR3R9Kfd*x3=?83jT{bW7}RkPL&WWR z3%M&*x|+Ov}ldwoOpn zP-xzx<0u{%(23?!?Sz2Q1Sq7i19=?R@Pv*Nc(1?%4J=V0I%56)ux$%$PqI7e+BUP_ za-DqtLZF3X%}=%;-WyR zMv%bPq^YPigv=BY%&;P!5;(NRPRW{|mmZr#cHVQ#p}mSHmn^sH({RYDLOG-3{g@Tl zR7ZL8TGg~!I*%uf$FwDgjNMtg(m$@GMK!ER|GxtpXG4Q!IUub-|=%r4Q92 zL7I3>mQpYUa?N#COV1_C3+QOXUv}R0vau3ny{MxUS}(~kxl@>DxFdnVgcVPg-6dI4 zL@esCR55i#M4TcAGKH%GL(TS8o!`rcK~>OY9S&T!pe5604huZgR4j>P@4z*98hjlA zssdXYCDRb19zS5hlm#Ac(i5%bNGLb+1}~To>$r|(HVoC$1+q=z^HnFTu-v34OIFbg zB)iW?@KFsP)A4aU&tNpnH|b_(n$GfhGbkG(QcVCVD$(+m6=Dv|_ zQ|6q_Bsa$0qG?Z?o~7pTVq36iG5!Y|*X4a)wt0beUj|QAn7o(BgjJT)mTyt>u){nw zRjZ_88m(UdBa)>cc`&7HjD=TNd)UT#Ppoc?yc(MQU9Qx8?p-B&JW=Q|5hrM+vk;iY ztGvTvsF{Q->bamqlq08k3C10JLsW*!?J(1lIl%H$(!;f$5zd~BYM2G2_TtBkiqA=P z*G9KWN?xz<>X0QSTSDy^FO&t-9`+WhWwyeT*Nd_e-Y-n^RmxOzk90j4JKRVq)}(Eg z=SpU;+KT<@Js{AswWto-pGFWs={-zDM znZ}>-7Y(oIcoi$Wl^e`h&XRjo=Eg(&JYmk8#lZDg-yP-T?BB`Ujt@l!&X`WgmVWQJ z>t3x^@(Ft4Kqgk24o)#0lH&Kj8JT?Nv&j|^YaZpBqhPTmWpI(K+)4AXdRNEq@IpqF zratC4(i^o^QTrO+;Hz#8PwPR}@OObn-dU9WHR1<5XtaebhS61TWRB7t^P$e~Z*Z35 zuTU*$;1i5;@n3YOjcXU~fl&c5-bb_}=Z#Z)Z{v(HzGv?pOoXVy%}Z)?!kF5-?>43t z&ejm;PNX;E8YnL6f^YcQ*owQ>7Z9m)wt zO_hB{xzx(oF=8wD#EkMGY70?GPqUBAgf^qrkcn-oeCVKt8~nUf#IO1Do4WF&vGTN1 zF4j}~3^wA~gxd7mtNm?VZJrgaeC{+py#=4WjnDD(#nleav4D#S&7BF&st4U5=Co?e z9IK`{82hRX7!2E2U*=M>Elws>X?U5{q3Z7Uq0}GnNB$~a{=~Ig4B>0|D_2z4eI0+p HKd}9OvgckM literal 0 HcmV?d00001 diff --git a/version5/krpc-core/target/classes/com/kama/test/balance/RandomLoadBalanceTest.class b/version5/krpc-core/target/classes/com/kama/test/balance/RandomLoadBalanceTest.class new file mode 100644 index 0000000000000000000000000000000000000000..6fc428a911e213473d07ff06cab3cd83f75520f1 GIT binary patch literal 2086 zcmbVM?Q+{h6g_J@kv~LhM3XdWfV3pkAB{zm07)H8oi=IHx-P`Q9hV=BA}_9jEO{ik z(>wvsz+c{g8E6Ykc>o@Y;jW~}id`l%{E>F|?mhS1bMM~2|M}xD0AJuBiv*GqQZlA6 z&5%FVo@=V9Sv~df(J|LOhUsm?GQ1i?vQTVGNHc8fc3=HL>uaiR8rZS1>gCx@8&doGtqRm$c@-hDD@iLcCa+tHq~T6j72;mT?2Cl#K24)Z-zQUah;N1m%#` z{KcU&p(sw%^j z%e`*~4D$y=%QO1CZMX)3)-B8SG|#YwauhF8Wru%nJDeOB1$@}u-#szBXHDDM?GL<@ zpz=AA!r^Y%2l??*QmI^S(o5srmfvq8%BWL9#TaJKrg}0sO?P<=aeLCcMlAC&>f#;I z(fl~^80$2DG4M%I`^5nFO_TRDv+neUeR}P?FLXW-+7R01akS>pG(!o5sealKIkCGN z)SQ~CWe**l?-`<=3vYe$6(I+%(#%NE&|>tZsX;4=)-<)~dy94|{TcnIO0U3vp+y2! z`U*2}3ET9Qg9|kGYP4hclB@~e`4*DCO)mWjsq+e%-;f<)h#?7FAp(mr967|fO*Rpw z?qlvmm_H&#s6cu84f36&qO>|GO zMz@7HdBG$}B#9OAc`}~(DydzGC7zEZp7U`T9_nb&@=*l78G>g8IC~rHIx^UZ!Djy( zwi&_x6pFefV3o>Cd?HP%+nH7>=Sw_G%>x=Y8RD=)u`ARSo75AcQv#W{LPjf*%#{h5 z1KXs`k0Z)Ihm>m(<=H4MU&6 zXYgO&z!?S9(FgEJd1DTBqY5y?cLs=R4oI=ic-7t=d(UwxMgLqx3cVh2GZM z>}$5NqcXm&DSEeOD4W*6?9?o^v+m8NkU(@2X~+q@CF2C9BoqQXN9#L5{CMaV7bT>P z5txLvS`hpHxs(%l`#{MJ$JWigBu+|5h?+F}auUnEJGlUs9 zx}j8UTix$dIkjKY1?TyNC$({5%Xx3gjS$<)LYntv%;7wf_##TUxG=uY4|EU7V_Z(- z0xl*nFXIDTl5oOJ79dMVwk^|9b(5r~eZ0^am-CNP$fJfV zo0Mf&xBE`R)rJ}9tiHw@LY0JC?#u}uv>l$9+O-C@^DML_)!HJUi2PgVVLb&qyu4!n^kui@)Oi2;N{pq>Q?>vkV3wu~|Mzb9{~pxa6ju^C11y zvp9u&_?*e-NUnzaBwA;jDAZefz}}GHL#{~pA|P1dZXr%aFo}{RS&`57c;Q8=y%Z`u z6DmASgopgwY@@-?Rsg=@gQoi=`MS?&K9IR|NM>*Cu*}VX@{c~{Ns>++a;x^u{CB^d+xDixocR z>J!d9b!)76to#~haWl9E*BBwLL7tXvu@@!I1xDXzC2Ra&;|Y85&?OO(hZT5AHW7UF EFKUY%q5uE@ literal 0 HcmV?d00001 diff --git a/version5/krpc-core/target/classes/com/kama/test/serializer/HessianSerializerTest.class b/version5/krpc-core/target/classes/com/kama/test/serializer/HessianSerializerTest.class new file mode 100644 index 0000000000000000000000000000000000000000..2dc41e73ea361f490166a85a2bcfe02cafd4b108 GIT binary patch literal 1784 zcmb7EZEsU$7`~pi>uI|)R}OD7Sl2P;+OFLx^A!uo#)g};MQu~E?9;RMSk7qA!S zAB~BIAed+*0fRAt_>nBpQ48@0_&at6ze2sAw(E}DLgdRi&vQTb%XQsvfBoJ16TlgK z6o3nE2_6}J@G=Bf`E{;VxKUOwt*mNAi@|$ZH+1VPgF6x}NboTvi)OWI8fsmu>Aa$U zq1Dvt=Z?#QRA``#)_ozqMOGY&2V8qfw~@a0EjVj>)5?-Q z!-O+d&Z_A~IkiXhg1|Z=BaBgo{*vZEdpNQ@yBOVV^rEs+neQ@Q7Q>EuE$EL>})0Fro9##?xs+OMox8ER}^)))>%oZ0TyOkg`DlJf43 znsz2>&zcg8^Ja-W4yJWO%QmVjS}o64DkM?TW|3D4oMw#fU7^RiqEnKSqWxcDdQuEW z#hCTz&lsg`$`s6J*7UM&aLT9Rs7;rdbNEUOuH{)OyQ2e1=CtbaWjOM2!?5(KR?zD@ zahNd-(`x6JCNKvhW1=KQ(^~TlopqZwj~i4tBX!@xpW(#c`ZK6yxp4 zv5xm{WH%--gIPi#BZTvqBNWGo<2){qm-oq=;JDDnxY)z^fPA<~mhe9HExA4UB~M>| zsn47502T-@E3qcxzhWSxOvl&pMicL>k)t3+2uzZUMN&F&qa;mWh$eajC+Sti7-_^Y y-G&<`-7)y_0WP*vnCJoz61_CRWyp}H{c!z5rY;Hn60%?tlKvg&FOixHAN~u9rSNh9 literal 0 HcmV?d00001 diff --git a/version5/krpc-core/target/classes/com/kama/test/serializer/KryoSerializerTest.class b/version5/krpc-core/target/classes/com/kama/test/serializer/KryoSerializerTest.class new file mode 100644 index 0000000000000000000000000000000000000000..a50e04e9f348d5a1a5748e9943918d51f0607427 GIT binary patch literal 3493 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 literal 0 HcmV?d00001 diff --git a/version5/krpc-core/target/krpc-core-1.0-SNAPSHOT.jar b/version5/krpc-core/target/krpc-core-1.0-SNAPSHOT.jar new file mode 100644 index 0000000000000000000000000000000000000000..57e4489c74a986199b6696ee992ac0d369edfc7d GIT binary patch literal 78697 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 literal 0 HcmV?d00001 diff --git a/version5/krpc-core/target/maven-archiver/pom.properties b/version5/krpc-core/target/maven-archiver/pom.properties new file mode 100644 index 0000000..aa35714 --- /dev/null +++ b/version5/krpc-core/target/maven-archiver/pom.properties @@ -0,0 +1,5 @@ +#Generated by Maven +#Thu Dec 05 15:18:59 CST 2024 +groupId=com.kama +artifactId=krpc-core +version=1.0-SNAPSHOT diff --git a/version5/krpc-core/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/version5/krpc-core/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 0000000..651c8de --- /dev/null +++ b/version5/krpc-core/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -0,0 +1,55 @@ +com\kama\common\serializer\myserializer\HessianSerializer.class +com\kama\server\netty\NettyServerInitializer.class +com\kama\common\serializer\myserializer\ProtostuffSerializer.class +com\kama\server\server\impl\NettyRpcServer.class +com\kama\client\circuitbreaker\CircuitBreaker.class +com\kama\common\serializer\myserializer\Serializer.class +com\kama\server\ratelimit\RateLimit.class +com\kama\common\message\RpcRequest$RpcRequestBuilder.class +com\kama\client\circuitbreaker\CircuitBreaker$1.class +com\kama\client\servicecenter\ZKServiceCenter.class +com\kama\client\rpcclient\impl\SimpleSocketRpcClient.class +com\kama\server\serviceRegister\ServiceRegister.class +com\kama\client\retry\GuavaRetry$1.class +com\kama\RpcApplication.class +com\kama\common\serializer\mycoder\MyEncoder.class +com\kama\common\serializer\myserializer\JsonSerializer.class +com\kama\server\server\RpcServer.class +com\kama\server\server\work\WorkThread.class +com\kama\client\servicecenter\balance\impl\ConsistencyHashBalance.class +com\kama\client\netty\NettyClientHandler.class +com\kama\server\TestServer.class +com\kama\common\config\RpcConstant.class +com\kama\client\cache\ServiceCache.class +com\kama\server\provider\ServiceProvider.class +com\kama\common\message\RpcRequest.class +com\kama\server\ratelimit\impl\TokenBucketRateLimitImpl.class +com\kama\client\rpcclient\RpcClient.class +com\kama\common\serializer\mycoder\MyDecoder.class +com\kama\client\proxy\ClientProxy.class +com\kama\client\retry\GuavaRetry.class +com\kama\server\server\impl\SimpleRpcServer.class +com\kama\common\util\ConfigUtil.class +com\kama\server\ratelimit\provider\RateLimitProvider.class +com\kama\server\netty\NettyRpcServerHandler.class +com\kama\client\netty\NettyClientInitializer.class +com\kama\common\message\MessageType.class +com\kama\client\servicecenter\balance\impl\RandomLoadBalance.class +com\kama\client\servicecenter\ZKWatcher\watchZK$1.class +com\kama\client\servicecenter\balance\LoadBalance.class +com\kama\common\config\KRpcConfig.class +com\kama\common\serializer\myserializer\ObjectSerializer.class +com\kama\common\exception\SerializeException.class +com\kama\client\circuitbreaker\CircuitBreakerProvider.class +com\kama\common\message\RpcResponse$RpcResponseBuilder.class +com\kama\client\circuitbreaker\CircuitBreakerState.class +com\kama\server\serviceRegister\impl\ZKServiceRegister.class +com\kama\client\rpcclient\impl\NettyRpcClient.class +com\kama\common\spi\SpiLoader.class +com\kama\client\servicecenter\balance\impl\RoundLoadBalance.class +com\kama\client\servicecenter\ZKWatcher\watchZK.class +com\kama\common\config\KRpcConfig$KRpcConfigBuilder.class +com\kama\common\message\RpcResponse.class +com\kama\client\TestClient.class +com\kama\common\serializer\myserializer\KryoSerializer.class +com\kama\client\servicecenter\ServiceCenter.class diff --git a/version5/krpc-core/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/version5/krpc-core/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 0000000..7222e88 --- /dev/null +++ b/version5/krpc-core/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1,54 @@ +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\common\spi\SpiLoader.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\common\serializer\myserializer\ProtostuffSerializer.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\client\rpcclient\impl\SimpleSocketRpcClient.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\client\servicecenter\ZKServiceCenter.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\common\serializer\mycoder\MyEncoder.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\server\server\impl\SimpleRpcServer.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\test\balance\ConsistencyHashBalanceTest.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\server\netty\NettyRpcServerHandler.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\test\balance\RandomLoadBalanceTest.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\common\serializer\mycoder\MyDecoder.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\server\serviceRegister\ServiceRegister.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\client\netty\NettyClientInitializer.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\common\serializer\myserializer\Serializer.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\client\servicecenter\balance\impl\ConsistencyHashBalance.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\server\server\RpcServer.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\client\servicecenter\balance\impl\RoundLoadBalance.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\client\servicecenter\ZKWatcher\watchZK.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\common\message\RpcResponse.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\common\config\RpcConstant.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\RpcApplication.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\common\message\MessageType.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\client\TestClient.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\server\serviceRegister\impl\ZKServiceRegister.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\client\rpcclient\impl\NettyRpcClient.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\client\retry\GuavaRetry.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\server\server\work\WorkThread.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\common\config\KRpcConfig.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\client\circuitbreaker\CircuitBreaker.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\client\netty\NettyClientHandler.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\test\serializer\KryoSerializerTest.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\test\serializer\HessianSerializerTest.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\test\serializer\ProtostuffSerializerTest.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\client\servicecenter\balance\impl\RandomLoadBalance.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\common\serializer\myserializer\KryoSerializer.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\server\server\impl\NettyRpcServer.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\server\ratelimit\RateLimit.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\client\servicecenter\balance\LoadBalance.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\server\ratelimit\impl\TokenBucketRateLimitImpl.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\client\rpcclient\RpcClient.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\client\servicecenter\ServiceCenter.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\client\cache\ServiceCache.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\server\ratelimit\provider\RateLimitProvider.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\common\util\ConfigUtil.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\client\proxy\ClientProxy.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\common\serializer\myserializer\HessianSerializer.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\server\netty\NettyServerInitializer.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\server\TestServer.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\server\provider\ServiceProvider.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\common\serializer\myserializer\JsonSerializer.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\common\exception\SerializeException.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\common\message\RpcRequest.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\common\serializer\myserializer\ObjectSerializer.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\client\circuitbreaker\CircuitBreakerProvider.java +D:\java_stduy\version5\krpc-core\src\main\java\com\kama\test\balance\RoundLoadBalanceTest.java diff --git a/version5/krpc-provider/pom.xml b/version5/krpc-provider/pom.xml new file mode 100644 index 0000000..db9168e --- /dev/null +++ b/version5/krpc-provider/pom.xml @@ -0,0 +1,52 @@ + + + 4.0.0 + + com.kama + version5 + 1.0-SNAPSHOT + + + krpc-provider + + + 17 + 17 + UTF-8 + + + + com.kama + krpc-api + 1.0-SNAPSHOT + + + com.kama + krpc-core + 1.0-SNAPSHOT + + + com.kama + krpc-common + 1.0-SNAPSHOT + + + cn.hutool + hutool-all + 5.8.10 + + + org.springframework.boot + spring-boot-starter-web + RELEASE + compile + + + org.springframework.boot + spring-boot-starter + + + + \ No newline at end of file diff --git a/version5/krpc-provider/src/main/java/com/kama/provider/ProviderTest.java b/version5/krpc-provider/src/main/java/com/kama/provider/ProviderTest.java new file mode 100644 index 0000000..f55742b --- /dev/null +++ b/version5/krpc-provider/src/main/java/com/kama/provider/ProviderTest.java @@ -0,0 +1,35 @@ +package com.kama.provider; + +import com.kama.KRpcApplication; +import com.kama.provider.impl.UserServiceImpl; +import com.kama.server.provider.ServiceProvider; +import com.kama.server.server.RpcServer; +import com.kama.server.server.impl.NettyRpcServer; +import com.kama.service.UserService; +import lombok.extern.slf4j.Slf4j; + +/** + * @ClassName ProviderExample + * @Description 测试服务端 + * @Author Tong + * @LastChangeDate 2024-12-05 0:34 + * @Version v5.0 + */ +@Slf4j +public class ProviderTest { + + public static void main(String[] args) throws InterruptedException { + KRpcApplication.initialize(); + // 创建 UserService 实例 + UserService userService = new UserServiceImpl(); + ServiceProvider serviceProvider = new ServiceProvider("127.0.0.1", 9999); + // 发布服务接口到 ServiceProvider + serviceProvider.provideServiceInterface(userService, true); // 可以设置是否支持重试 + + // 启动 RPC 服务器并监听端口 + RpcServer rpcServer = new NettyRpcServer(serviceProvider); + rpcServer.start(9999); // 启动 Netty RPC 服务,监听 9999 端口 + log.info("RPC 服务端启动,监听端口 9999"); + } + +} diff --git a/version5/krpc-provider/src/main/java/com/kama/provider/impl/UserServiceImpl.java b/version5/krpc-provider/src/main/java/com/kama/provider/impl/UserServiceImpl.java new file mode 100644 index 0000000..64246d3 --- /dev/null +++ b/version5/krpc-provider/src/main/java/com/kama/provider/impl/UserServiceImpl.java @@ -0,0 +1,41 @@ +package com.kama.provider.impl; + +import com.kama.pojo.User; +import com.kama.service.UserService; +import lombok.extern.slf4j.Slf4j; + +import java.util.Random; +import java.util.UUID; + +/** + * @ClassName UserServiceImpl + * @Description 接口实现类 + * @Author Tong + * @LastChangeDate 2024-12-04 23:55 + * @Version v5.0 + */ +@Slf4j +public class UserServiceImpl implements UserService { + + @Override + public User getUserByUserId(Integer id) { + log.info("客户端查询了ID={}的用户", id); + // 模拟从数据库中取用户的行为 + Random random = new Random(); + User user = User.builder() + .userName(UUID.randomUUID().toString()) // 使用随机生成的用户名 + .id(id) + .gender(random.nextBoolean()) // 随机生成性别 + .build(); + log.info("返回用户信息: {}", user); + return user; + } + + @Override + public Integer insertUserId(User user) { + log.info("插入数据成功,用户名={}", user.getUserName()); + // 假设插入数据返回用户ID + return user.getId(); + } +} + diff --git a/version5/krpc-provider/src/main/resources/application.properties b/version5/krpc-provider/src/main/resources/application.properties new file mode 100644 index 0000000..9312248 --- /dev/null +++ b/version5/krpc-provider/src/main/resources/application.properties @@ -0,0 +1,7 @@ +rpc.name=krpc +rpc.version=1.0.0 +rpc.port=9999 +rpc.serializer=Hessian +rpc.host=localhost +rpc.registry=zookeeper +rpc.loadBalance=ConsistencyHash diff --git a/version5/krpc-provider/target/classes/META-INF/application.properties b/version5/krpc-provider/target/classes/META-INF/application.properties new file mode 100644 index 0000000..e69de29 diff --git a/version5/krpc-provider/target/classes/application.properties b/version5/krpc-provider/target/classes/application.properties new file mode 100644 index 0000000..9312248 --- /dev/null +++ b/version5/krpc-provider/target/classes/application.properties @@ -0,0 +1,7 @@ +rpc.name=krpc +rpc.version=1.0.0 +rpc.port=9999 +rpc.serializer=Hessian +rpc.host=localhost +rpc.registry=zookeeper +rpc.loadBalance=ConsistencyHash diff --git a/version5/krpc-provider/target/classes/com/kama/provider/ProviderTest.class b/version5/krpc-provider/target/classes/com/kama/provider/ProviderTest.class new file mode 100644 index 0000000000000000000000000000000000000000..cd2eb0aa73c6df6108cfc7b57027372f725be626 GIT binary patch literal 1479 zcmaJ>ZBGV z38qH<+$N3ji-{)2HYqB`AJG3n`PhHZ^trM-OBdR?$(?)8J0l0`c z6EUU*vedO=0N)F{vKy&t534+MrI)v5~Cyd|k@uUz%*Z)#G_OSR^?a+Z4& zmgdtbkc|M;SfhG`tE=Cr+vz}BATfFB-1r2?q=^&`=x`1S^kfUofaXKxRZ96;9YqR< zEcD{Az@fU?28T*0)w1Kt7V!3MS|d5HvK$B;+#@$SHD_?vz{eKO;S*-3 z6+Yv*VW93Z;UyVv^Ai}_X=>VcYPEcBcNp|M9V{`ise)ILbF1Y=sct!oJ|zs=b^M~E zJiV_A)U7RpwEb3Oj&VYI@V@ zrEF)1&{wK1Zsd-&_;1MnbPC}K)u>B>!CgRYoA{`r2r#eSab;Eabj`q5B-m`IS=}pY zwHnH$NJ*e$%Ju6nhB+UsDp$^U`i{FAjCHk*YbzLFg~s>}3APApl&dzb`4DkF&z;S` z;JL7W4~zk;y=YhfMx3mZr+q`SH@ zsdaQ;A3YiW9X%U3vM{=iV{>B^zs89ee#KDwHX2gFd) W!>@4qHwksTwIpeK9X|y1x%zR$85<#>w z^j7ZOsa{*({CTZ<`|jhlJC7cGk(?gCa&`0D&o*!UL@#0NL%V>8geW=)<#sa+0nJ!4 z8IH$N%?Q%Ar5X8z=f;gG5nm9{RS&jgYr1@1F>+>6L?`?r4x(GYAqj_ZgfKTlGz+E^ z8H121X*xyAa5NTA{a>P=Ct%W*Nr-p~y#o3q9L3WNtqwrP>->CjnnAQ2=k6gw_O=Q* z`o|;;;5b9jHr=QgI_i>6wQ8}7h+|N|kc1N$X6Sh`G_qGBptDL*WfhOXFRErU*8Y7&&hIL0MR;AQIQylObb+XA*OxeKsyoRKhzDTWrO5xCc# z)Tp~(rMJfqZ&kkg-tFW^4}YkBx^}wv%GDrVW9X}XeY5iU-Rd{@tJm*UuidD8dHbI~ zzj8$@H?GqR@ODf}c!O6zoL6n$S-fEUJ|*ECW(ft|Cn@=zTUw%?eGzZsEdl2xq;Y{^ z|MuaTQL?saU1kW88Fwrhj(O`ir7J5dRK@L@2>}ZXhZ{{s(D=xE$Dby1%FI!FwWTye zoh=nJs&zrh=yXGDkX7^r#nSk>b{ViQX)6pd&rm&yS*RV_YDt;A%u~bD5cguQXJNWKDD*pgplX zt#8vwrZ1y!%iucLFSPOD9a{M_5Wz)SC0788c$ZEL@8Nw~x$=+jlR04GR|t{4L%*ZV zk3Z0PaUJ{5ZQ#HLddlc8BQ{jV$rEKfyMgD+7%StIS*|cW>?jQ1Ckw9E0fcDZML-V_ z#UG?yH+pdh{Y1jUID!%MV2pT_qZ)KQ*H94#qMr{4R}@3|5Fe4n5$uJ63}Ix*B3HwF zmd-xXk8%hzgzR+(1#QkYf$-l$klR+`TkfpORcSbjI);fjp*AwvM-KR3C=%Sj+;0$g aHkl`z?PO$$?tIAO5`9H_jZ>5wLjM69UHL)) literal 0 HcmV?d00001 diff --git a/version5/krpc-provider/target/krpc-provider-1.0-SNAPSHOT.jar b/version5/krpc-provider/target/krpc-provider-1.0-SNAPSHOT.jar new file mode 100644 index 0000000000000000000000000000000000000000..95956acd740d33e4d37b9961ba3d7f5597026d4d GIT binary patch literal 5129 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 literal 0 HcmV?d00001 diff --git a/version5/krpc-provider/target/maven-archiver/pom.properties b/version5/krpc-provider/target/maven-archiver/pom.properties new file mode 100644 index 0000000..d937b1d --- /dev/null +++ b/version5/krpc-provider/target/maven-archiver/pom.properties @@ -0,0 +1,5 @@ +#Generated by Maven +#Thu Dec 05 20:00:58 CST 2024 +groupId=com.kama +artifactId=krpc-provider +version=1.0-SNAPSHOT diff --git a/version5/krpc-provider/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/version5/krpc-provider/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 0000000..af204cc --- /dev/null +++ b/version5/krpc-provider/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -0,0 +1,3 @@ +com\kama\provider\impl\UserServiceImpl.class +com\kama\provider\impl\ProviderTestConfig.class +com\kama\provider\ProviderExample.class diff --git a/version5/krpc-provider/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/version5/krpc-provider/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 0000000..7d48ad0 --- /dev/null +++ b/version5/krpc-provider/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1,3 @@ +D:\java_stduy\version5\krpc-provider\src\main\java\com\kama\provider\ProviderExample.java +D:\java_stduy\version5\krpc-provider\src\main\java\com\kama\provider\impl\ProviderTestConfig.java +D:\java_stduy\version5\krpc-provider\src\main\java\com\kama\provider\impl\UserServiceImpl.java diff --git a/version5/pom.xml b/version5/pom.xml new file mode 100644 index 0000000..ed2a11b --- /dev/null +++ b/version5/pom.xml @@ -0,0 +1,97 @@ + + + 4.0.0 + + com.kama + version5 + 1.0-SNAPSHOT + pom + + krpc-core + krpc-consumer + krpc-provider + krpc-api + krpc-common + + + + 17 + 17 + UTF-8 + + + + + org.projectlombok + lombok + 1.18.30 + compile + + + + + org.springframework.boot + spring-boot-starter + 3.3.5 + + + + + io.netty + netty-all + 4.1.51.Final + compile + + + com.alibaba + fastjson + 1.2.83 + + + com.esotericsoftware + kryo + 4.0.2 + + + com.caucho + hessian + 4.0.66 + + + io.protostuff + protostuff-core + 1.7.4 + + + io.protostuff + protostuff-runtime + 1.7.4 + + + com.github.rholder + guava-retrying + 2.0.0 + + + + + + org.springframework.boot + spring-boot-dependencies + 3.3.5 + import + pom + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + \ No newline at end of file From 2851e9aa135a257036e3eed1db3d03c5a52c0e22 Mon Sep 17 00:00:00 2001 From: xiaobaozi-web <864508127@qq.com> Date: Mon, 9 Dec 2024 10:27:18 +0800 Subject: [PATCH 5/5] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0259d90..b93a600 100644 --- a/README.md +++ b/README.md @@ -210,7 +210,7 @@ - 新增kryo、Hessian、protostuff等序列化方式 - 优化关闭方法 -![](assets/image.png) +![](README.assets/image.png)