├── base ├── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── liubs │ │ │ │ └── shadowrpc │ │ │ │ └── base │ │ │ │ ├── module │ │ │ │ ├── IModule.java │ │ │ │ └── ModulePool.java │ │ │ │ ├── config │ │ │ │ ├── ClientConfig.java │ │ │ │ ├── BaseConfig.java │ │ │ │ └── ServerConfig.java │ │ │ │ ├── constant │ │ │ │ ├── Constant.java │ │ │ │ └── SerializerEnum.java │ │ │ │ ├── exception │ │ │ │ └── ConfigFieldMissException.java │ │ │ │ ├── annotation │ │ │ │ ├── ModuleInject.java │ │ │ │ ├── ShadowModule.java │ │ │ │ ├── ShadowInterface.java │ │ │ │ ├── ShadowService.java │ │ │ │ └── ShadowServiceHolder.java │ │ │ │ └── util │ │ │ │ ├── PackageScanUtil.java │ │ │ │ ├── JsonUtil.java │ │ │ │ └── ClassScanWalker.java │ │ └── resources │ │ │ └── log4j2.xml │ └── test │ │ └── java │ │ ├── LogTest.java │ │ └── JacksonTest.java └── pom.xml ├── protocol ├── src │ ├── test │ │ ├── resources │ │ │ └── proto │ │ │ │ ├── MyMessage.proto │ │ │ │ └── PersonProto.proto │ │ └── java │ │ │ └── serializetest │ │ │ └── entity │ │ │ └── Person.java │ └── main │ │ ├── java │ │ └── com │ │ │ └── liubs │ │ │ └── shadowrpc │ │ │ └── protocol │ │ │ ├── serializer │ │ │ ├── ISerializer.java │ │ │ ├── protobuf │ │ │ │ ├── ProtobufSerializerBase.java │ │ │ │ ├── ShadowProtobufSerializer.java │ │ │ │ ├── ProtobufSerializer.java │ │ │ │ └── ParserForType.java │ │ │ ├── kryo │ │ │ │ ├── KryoFieldSerializerFactory.java │ │ │ │ ├── KryoSerializer.java │ │ │ │ ├── KryoModelParser.java │ │ │ │ └── KryoFieldSerializer.java │ │ │ ├── javaserializer │ │ │ │ ├── JavaSerializer.java │ │ │ │ └── JavaModelParser.java │ │ │ ├── SerializerManager.java │ │ │ └── SerializerStrategy.java │ │ │ ├── annotation │ │ │ ├── ShadowEntity.java │ │ │ └── ShadowField.java │ │ │ ├── entity │ │ │ ├── HeartBeatMessage.java │ │ │ ├── JavaSerializeRPCResponse.java │ │ │ ├── JavaSerializeRPCRequest.java │ │ │ ├── ShadowRPCResponse.java │ │ │ └── ShadowRPCRequest.java │ │ │ ├── model │ │ │ ├── IModelParser.java │ │ │ ├── ResponseModel.java │ │ │ └── RequestModel.java │ │ │ ├── constant │ │ │ └── ResponseCode.java │ │ │ ├── util │ │ │ └── AnnotationScanner.java │ │ │ └── SerializeModule.java │ │ └── resources │ │ └── proto │ │ ├── request.proto │ │ ├── request_any.proto │ │ └── response.proto └── pom.xml ├── client ├── src │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── liubs │ │ │ └── shadowrpc │ │ │ └── client │ │ │ ├── loadbalance │ │ │ ├── IBalance.java │ │ │ ├── PollingBalance.java │ │ │ └── LoadBalanceContext.java │ │ │ ├── connection │ │ │ ├── IConnection.java │ │ │ └── ShadowClient.java │ │ │ ├── handler │ │ │ ├── HeartBeatHandler.java │ │ │ ├── ReceiveHolder.java │ │ │ ├── ShadowChannelInitializer.java │ │ │ ├── ClientHandler.java │ │ │ └── MessageHandler.java │ │ │ ├── ClientModule.java │ │ │ └── proxy │ │ │ ├── RemoteServerProxy.java │ │ │ └── RemoteHandler.java │ └── test │ │ └── java │ │ └── rpctest │ │ ├── registry │ │ ├── IGroupService.java │ │ ├── HelloMultiGroups.java │ │ ├── HelloClientGroup.java │ │ └── HelloClientGroupProtobuf.java │ │ ├── upload │ │ ├── IUploadService.java │ │ └── UploadClient.java │ │ ├── hello │ │ ├── IHello.java │ │ ├── IHelloProto.java │ │ ├── HelloProtoClient.java │ │ └── HelloClient.java │ │ └── entity │ │ └── MyMessage.java └── pom.xml ├── client-mini ├── src │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── liubs │ │ │ └── shadowrpc │ │ │ ├── clientmini │ │ │ ├── nio │ │ │ │ ├── IMessageListener.java │ │ │ │ ├── MessageSendFuture.java │ │ │ │ └── NIOConfig.java │ │ │ ├── exception │ │ │ │ ├── ConnectTimeoutException.java │ │ │ │ ├── RemoteClosedException.java │ │ │ │ └── WriteTimeoutException.java │ │ │ ├── seriallize │ │ │ │ ├── ISerializer.java │ │ │ │ └── JavaSerializer.java │ │ │ ├── handler │ │ │ │ ├── RequestHandler.java │ │ │ │ ├── ReceiveHolder.java │ │ │ │ └── ResponseHandler.java │ │ │ ├── connection │ │ │ │ ├── HeartBeatMessage.java │ │ │ │ ├── SimpleHeartBeat.java │ │ │ │ └── ShadowClient.java │ │ │ └── logger │ │ │ │ └── LoggerHandler.java │ │ │ ├── base │ │ │ └── annotation │ │ │ │ └── ShadowInterface.java │ │ │ └── protocol │ │ │ └── entity │ │ │ ├── JavaSerializeRPCResponse.java │ │ │ └── JavaSerializeRPCRequest.java │ └── test │ │ └── java │ │ ├── rpctest │ │ ├── hello │ │ │ ├── IHello.java │ │ │ └── HelloClient.java │ │ └── entity │ │ │ └── MyMessage.java │ │ └── niotest │ │ └── NIOClientTest.java └── pom.xml ├── server ├── src │ ├── test │ │ └── java │ │ │ └── rpctest │ │ │ ├── registry │ │ │ ├── IGroupService.java │ │ │ ├── Group1Service.java │ │ │ ├── Group2Service.java │ │ │ ├── SomeServersProtobuf.java │ │ │ ├── SomeServers.java │ │ │ └── MultiGroupServers.java │ │ │ ├── upload │ │ │ ├── IUploadService.java │ │ │ └── UploadServiceImpl.java │ │ │ ├── hello │ │ │ ├── IHello.java │ │ │ ├── IHelloProto.java │ │ │ ├── HelloProtoService.java │ │ │ └── HelloService.java │ │ │ └── entity │ │ │ └── MyMessage.java │ └── main │ │ └── java │ │ └── com │ │ └── liubs │ │ └── shadowrpc │ │ └── server │ │ ├── init │ │ ├── ServerGroup.java │ │ └── ServerBuilder.java │ │ ├── service │ │ ├── ServiceTarget.java │ │ ├── ServiceLookUp.java │ │ ├── ServerManager.java │ │ └── Server.java │ │ ├── handler │ │ ├── ShadowChannelInitializer.java │ │ ├── QpsStatHandler.java │ │ ├── MessageHandler.java │ │ └── ServerHandler.java │ │ └── ServerModule.java └── pom.xml ├── registry ├── src │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── liubs │ │ │ └── shadowrpc │ │ │ └── registry │ │ │ ├── constant │ │ │ ├── ServerChangeType.java │ │ │ └── ServiceRegistryPath.java │ │ │ ├── listener │ │ │ └── ServiceListener.java │ │ │ ├── util │ │ │ └── IPUtil.java │ │ │ ├── access │ │ │ ├── ServiceRegistry.java │ │ │ └── ServiceDiscovery.java │ │ │ ├── entity │ │ │ └── ServerNode.java │ │ │ └── zk │ │ │ └── ZooKeeperClient.java │ └── test │ │ └── java │ │ └── ZooKeeperClientTest.java └── pom.xml ├── .gitignore ├── pom.xml └── README.md /base/src/main/java/com/liubs/shadowrpc/base/module/IModule.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.base.module; 2 | 3 | /** 4 | * @author Liubsyy 5 | * @date 2024/1/2 6 | */ 7 | public interface IModule { 8 | } 9 | -------------------------------------------------------------------------------- /base/src/main/java/com/liubs/shadowrpc/base/config/ClientConfig.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.base.config; 2 | 3 | /** 4 | * @author Liubsyy 5 | * @date 2024/1/15 6 | */ 7 | public class ClientConfig extends BaseConfig{ 8 | } 9 | -------------------------------------------------------------------------------- /base/src/main/java/com/liubs/shadowrpc/base/constant/Constant.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.base.constant; 2 | 3 | 4 | 5 | /** 6 | * @author Liubsyy 7 | * @date 2023/12/31 8 | **/ 9 | public class Constant { 10 | 11 | 12 | } 13 | -------------------------------------------------------------------------------- /protocol/src/test/resources/proto/MyMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package rpctest.entity; 4 | 5 | option java_outer_classname="MyMessageProto"; 6 | 7 | message MyMessage { 8 | string content = 1; 9 | int32 num = 2; 10 | } 11 | -------------------------------------------------------------------------------- /client/src/main/java/com/liubs/shadowrpc/client/loadbalance/IBalance.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.client.loadbalance; 2 | 3 | /** 4 | * @author Liubsyy 5 | * @date 2024/1/18 6 | **/ 7 | public interface IBalance { 8 | int getNextBalance(); 9 | } 10 | -------------------------------------------------------------------------------- /client-mini/src/main/java/com/liubs/shadowrpc/clientmini/nio/IMessageListener.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.clientmini.nio; 2 | 3 | /** 4 | * 接收消息处理 5 | * @author Liubsyy 6 | * @date 2024/1/20 7 | **/ 8 | public interface IMessageListener { 9 | void handleMessage(byte[] bytes); 10 | } 11 | -------------------------------------------------------------------------------- /client/src/test/java/rpctest/registry/IGroupService.java: -------------------------------------------------------------------------------- 1 | package rpctest.registry; 2 | 3 | import com.liubs.shadowrpc.base.annotation.ShadowInterface; 4 | 5 | /** 6 | * @author Liubsyy 7 | * @date 2024/1/24 8 | **/ 9 | @ShadowInterface 10 | public interface IGroupService { 11 | 12 | String getGroupName(); 13 | } 14 | -------------------------------------------------------------------------------- /protocol/src/test/resources/proto/PersonProto.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package serializetest.entity; 4 | 5 | option java_outer_classname="PersonProto"; 6 | 7 | message Person { 8 | int32 age = 1; 9 | double height = 2; 10 | float weight = 3; 11 | int64 money = 4; 12 | string name = 5; 13 | } 14 | -------------------------------------------------------------------------------- /server/src/test/java/rpctest/registry/IGroupService.java: -------------------------------------------------------------------------------- 1 | package rpctest.registry; 2 | 3 | import com.liubs.shadowrpc.base.annotation.ShadowInterface; 4 | 5 | /** 6 | * @author Liubsyy 7 | * @date 2024/1/24 8 | **/ 9 | @ShadowInterface 10 | public interface IGroupService { 11 | 12 | String getGroupName(); 13 | } 14 | -------------------------------------------------------------------------------- /registry/src/main/java/com/liubs/shadowrpc/registry/constant/ServerChangeType.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.registry.constant; 2 | 3 | /** 4 | * 服务器节点变更类型 5 | * @author Liubsyy 6 | * @date 2023/12/31 7 | **/ 8 | public enum ServerChangeType { 9 | SERVER_ADDED, 10 | SERVER_UPDATED, 11 | SERVER_REMOVED; 12 | } 13 | -------------------------------------------------------------------------------- /client/src/test/java/rpctest/upload/IUploadService.java: -------------------------------------------------------------------------------- 1 | package rpctest.upload; 2 | 3 | import com.liubs.shadowrpc.base.annotation.ShadowInterface; 4 | 5 | /** 6 | * @author Liubsyy 7 | * @date 2024/1/14 8 | **/ 9 | @ShadowInterface 10 | public interface IUploadService { 11 | boolean upload(String name,byte[] bytes); 12 | } 13 | -------------------------------------------------------------------------------- /server/src/test/java/rpctest/upload/IUploadService.java: -------------------------------------------------------------------------------- 1 | package rpctest.upload; 2 | 3 | 4 | import com.liubs.shadowrpc.base.annotation.ShadowInterface; 5 | 6 | /** 7 | * @author Liubsyy 8 | * @date 2024/1/14 9 | **/ 10 | @ShadowInterface 11 | public interface IUploadService { 12 | boolean upload(String name,byte[] bytes); 13 | } 14 | -------------------------------------------------------------------------------- /protocol/src/main/java/com/liubs/shadowrpc/protocol/serializer/ISerializer.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.protocol.serializer; 2 | 3 | 4 | /** 5 | * @author Liubsyy 6 | * @date 2023/12/18 10:42 PM 7 | **/ 8 | public interface ISerializer { 9 | 10 | byte[] serialize(Object object); 11 | 12 | T deserialize(byte[] array, Class clazz); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /client-mini/src/main/java/com/liubs/shadowrpc/clientmini/exception/ConnectTimeoutException.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.clientmini.exception; 2 | 3 | /** 4 | * 连接超时 5 | * @author Liubsyy 6 | * @date 2024/1/21 7 | **/ 8 | public class ConnectTimeoutException extends Exception{ 9 | public ConnectTimeoutException(String s) { 10 | super(s); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /client-mini/src/main/java/com/liubs/shadowrpc/clientmini/seriallize/ISerializer.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.clientmini.seriallize; 2 | 3 | 4 | /** 5 | * @author Liubsyy 6 | * @date 2023/12/18 10:42 PM 7 | **/ 8 | public interface ISerializer { 9 | 10 | byte[] serialize(Object object); 11 | 12 | T deserialize(byte[] array, Class clazz); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /base/src/main/java/com/liubs/shadowrpc/base/exception/ConfigFieldMissException.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.base.exception; 2 | 3 | /** 4 | * 配置字段不完整异常 5 | * @author Liubsyy 6 | * @date 2024/1/23 7 | **/ 8 | public class ConfigFieldMissException extends RuntimeException{ 9 | public ConfigFieldMissException(String message) { 10 | super(message); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /client/src/test/java/rpctest/hello/IHello.java: -------------------------------------------------------------------------------- 1 | package rpctest.hello; 2 | 3 | import com.liubs.shadowrpc.base.annotation.ShadowInterface; 4 | import rpctest.entity.MyMessage; 5 | 6 | /** 7 | * @author Liubsyy 8 | * @date 2023/12/18 10:59 PM 9 | **/ 10 | @ShadowInterface 11 | public interface IHello { 12 | String hello(String msg); 13 | MyMessage say(MyMessage message); 14 | } 15 | -------------------------------------------------------------------------------- /server/src/test/java/rpctest/hello/IHello.java: -------------------------------------------------------------------------------- 1 | package rpctest.hello; 2 | 3 | import com.liubs.shadowrpc.base.annotation.ShadowInterface; 4 | import rpctest.entity.MyMessage; 5 | 6 | /** 7 | * @author Liubsyy 8 | * @date 2023/12/18 10:59 PM 9 | **/ 10 | @ShadowInterface 11 | public interface IHello { 12 | String hello(String msg); 13 | MyMessage say(MyMessage message); 14 | } 15 | -------------------------------------------------------------------------------- /client-mini/src/test/java/rpctest/hello/IHello.java: -------------------------------------------------------------------------------- 1 | package rpctest.hello; 2 | 3 | import com.liubs.shadowrpc.base.annotation.ShadowInterface; 4 | import rpctest.entity.MyMessage; 5 | 6 | /** 7 | * @author Liubsyy 8 | * @date 2023/12/18 10:59 PM 9 | **/ 10 | @ShadowInterface 11 | public interface IHello { 12 | String hello(String msg); 13 | MyMessage say(MyMessage message); 14 | } 15 | -------------------------------------------------------------------------------- /client/src/test/java/rpctest/hello/IHelloProto.java: -------------------------------------------------------------------------------- 1 | package rpctest.hello; 2 | 3 | import com.liubs.shadowrpc.base.annotation.ShadowInterface; 4 | import rpctest.entity.MyMessageProto; 5 | 6 | /** 7 | * @author Liubsyy 8 | * @date 2023/12/25 12:48 AM 9 | **/ 10 | @ShadowInterface 11 | public interface IHelloProto { 12 | MyMessageProto.MyMessage say(MyMessageProto.MyMessage message); 13 | } 14 | -------------------------------------------------------------------------------- /server/src/test/java/rpctest/hello/IHelloProto.java: -------------------------------------------------------------------------------- 1 | package rpctest.hello; 2 | 3 | import com.liubs.shadowrpc.base.annotation.ShadowInterface; 4 | import rpctest.entity.MyMessageProto; 5 | 6 | /** 7 | * @author Liubsyy 8 | * @date 2023/12/25 12:48 AM 9 | **/ 10 | @ShadowInterface 11 | public interface IHelloProto { 12 | MyMessageProto.MyMessage say(MyMessageProto.MyMessage message); 13 | } 14 | -------------------------------------------------------------------------------- /client-mini/src/main/java/com/liubs/shadowrpc/clientmini/exception/RemoteClosedException.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.clientmini.exception; 2 | 3 | /** 4 | * 收到这个异常,表示本地已经关闭和远程的socket连接(一般是服务器已经断开) 5 | * @author Liubsyy 6 | * @date 2024/1/21 7 | **/ 8 | public class RemoteClosedException extends Exception{ 9 | 10 | public RemoteClosedException(String message) { 11 | super(message); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /protocol/src/main/resources/proto/request.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | 4 | package com.liubs.shadowrpc.protocol.entity; 5 | option java_outer_classname="ShadowRPCRequestProto"; 6 | 7 | message ShadowRPCRequest { 8 | string traceId = 1; 9 | string serviceName = 2; 10 | string methodName = 3; 11 | repeated string paramTypes = 4; //参数类名 12 | repeated bytes params = 5; //bytes类型充当参数 13 | 14 | } -------------------------------------------------------------------------------- /protocol/src/main/resources/proto/request_any.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | import "google/protobuf/any.proto"; 4 | 5 | package com.liubs.shadowrpc.protocol.entity; 6 | option java_outer_classname="ShadowRPCRequestAnyProto"; 7 | 8 | message ShadowRPCRequestAny { 9 | string traceId = 1; 10 | string serviceName = 2; 11 | string methodName = 3; 12 | repeated google.protobuf.Any params = 4; //any类型充当参数 13 | } -------------------------------------------------------------------------------- /protocol/src/main/resources/proto/response.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | import "google/protobuf/any.proto"; 4 | 5 | option java_package="com.liubs.shadowrpc.protocol.entity"; 6 | option java_outer_classname="ShadowRPCResponseProto"; 7 | 8 | message ShadowRPCResponse { 9 | string traceId = 1; 10 | int32 code = 2; 11 | string errorMsg = 3; 12 | 13 | string resultClass = 4; 14 | bytes result = 5; 15 | } -------------------------------------------------------------------------------- /server/src/test/java/rpctest/registry/Group1Service.java: -------------------------------------------------------------------------------- 1 | package rpctest.registry; 2 | 3 | import com.liubs.shadowrpc.base.annotation.ShadowService; 4 | 5 | /** 6 | * @author Liubsyy 7 | * @date 2024/1/24 8 | **/ 9 | @ShadowService(serviceName = "groupService") 10 | public class Group1Service implements IGroupService{ 11 | @Override 12 | public String getGroupName() { 13 | return "group1"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /server/src/test/java/rpctest/registry/Group2Service.java: -------------------------------------------------------------------------------- 1 | package rpctest.registry; 2 | 3 | import com.liubs.shadowrpc.base.annotation.ShadowService; 4 | 5 | /** 6 | * @author Liubsyy 7 | * @date 2024/1/24 8 | **/ 9 | @ShadowService(serviceName = "groupService2") 10 | public class Group2Service implements IGroupService{ 11 | @Override 12 | public String getGroupName() { 13 | return "group2"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /client/src/main/java/com/liubs/shadowrpc/client/connection/IConnection.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.client.connection; 2 | 3 | import io.netty.channel.Channel; 4 | 5 | /** 6 | * @author Liubsyy 7 | * @date 2024/1/17 8 | **/ 9 | public interface IConnection { 10 | void init(); 11 | 12 | T createRemoteProxy(Class serviceStub,String service); 13 | 14 | Channel getChannel(String group); 15 | 16 | void close(); 17 | } 18 | -------------------------------------------------------------------------------- /base/src/test/java/LogTest.java: -------------------------------------------------------------------------------- 1 | import org.junit.Test; 2 | import org.slf4j.Logger; 3 | import org.slf4j.LoggerFactory; 4 | 5 | /** 6 | * @author Liubsyy 7 | * @date 2023/12/31 8 | **/ 9 | public class LogTest { 10 | private static final Logger logger = LoggerFactory.getLogger(LogTest.class); 11 | 12 | 13 | @Test 14 | public void printLog(){ 15 | logger.info("Info message"); 16 | logger.error("Error message"); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /base/src/main/java/com/liubs/shadowrpc/base/annotation/ModuleInject.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.base.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * @author Liubsyy 10 | * @date 2024/1/15 11 | */ 12 | @Target({ElementType.FIELD}) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | public @interface ModuleInject { 15 | } 16 | -------------------------------------------------------------------------------- /base/src/main/java/com/liubs/shadowrpc/base/annotation/ShadowModule.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.base.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * @author Liubsyy 10 | * @date 2024/1/15 11 | */ 12 | @Target({ElementType.TYPE}) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | public @interface ShadowModule { 15 | } 16 | -------------------------------------------------------------------------------- /base/src/main/java/com/liubs/shadowrpc/base/annotation/ShadowInterface.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.base.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * @author Liubsyy 10 | * @date 2023/12/8 22:18 PM 11 | */ 12 | @Target({ElementType.TYPE}) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | public @interface ShadowInterface { 15 | } 16 | -------------------------------------------------------------------------------- /client-mini/src/main/java/com/liubs/shadowrpc/base/annotation/ShadowInterface.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.base.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * @author Liubsyy 10 | * @date 2023/12/8 22:18 PM 11 | */ 12 | @Target({ElementType.TYPE}) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | public @interface ShadowInterface { 15 | } 16 | -------------------------------------------------------------------------------- /protocol/src/main/java/com/liubs/shadowrpc/protocol/annotation/ShadowEntity.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.protocol.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * 传输实体 10 | * @author Liubsyy 11 | * @date 2023/12/18 10:39 PM 12 | **/ 13 | @Target({ElementType.TYPE}) 14 | @Retention(RetentionPolicy.RUNTIME) 15 | public @interface ShadowEntity { 16 | } -------------------------------------------------------------------------------- /client-mini/src/main/java/com/liubs/shadowrpc/clientmini/exception/WriteTimeoutException.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.clientmini.exception; 2 | 3 | /** 4 | * 写入channel超时异常 5 | * @author Liubsyy 6 | * @date 2024/1/21 7 | **/ 8 | public class WriteTimeoutException extends Exception{ 9 | 10 | public WriteTimeoutException(String message) { 11 | super(message); 12 | } 13 | 14 | public WriteTimeoutException(String message, Throwable cause) { 15 | super(message, cause); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /protocol/src/main/java/com/liubs/shadowrpc/protocol/annotation/ShadowField.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.protocol.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * 字段标识 10 | * @author Liubsyy 11 | * @date 2023/12/18 10:39 PM 12 | **/ 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Target(ElementType.FIELD) 15 | public @interface ShadowField { 16 | int value(); 17 | } 18 | -------------------------------------------------------------------------------- /server/src/main/java/com/liubs/shadowrpc/server/init/ServerGroup.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.server.init; 2 | 3 | import com.liubs.shadowrpc.base.config.ServerConfig; 4 | 5 | /** 6 | * @author Liubsyy 7 | * @date 2024/1/16 8 | */ 9 | public class ServerGroup { 10 | private ServerConfig serverConfig; 11 | private String[] packages; 12 | 13 | public ServerGroup(ServerConfig serverConfig, String[] packages) { 14 | this.serverConfig = serverConfig; 15 | this.packages = packages; 16 | } 17 | 18 | 19 | } 20 | -------------------------------------------------------------------------------- /registry/src/main/java/com/liubs/shadowrpc/registry/listener/ServiceListener.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.registry.listener; 2 | 3 | import com.liubs.shadowrpc.registry.constant.ServerChangeType; 4 | import com.liubs.shadowrpc.registry.entity.ServerNode; 5 | 6 | /** 7 | * @author Liubsyy 8 | * @date 2023/12/31 9 | **/ 10 | public interface ServiceListener { 11 | 12 | /** 13 | * 服务节点变更事件 14 | * @param changeType 15 | * @param serverNode 16 | */ 17 | void onServerChange(ServerChangeType changeType, ServerNode serverNode); 18 | } 19 | -------------------------------------------------------------------------------- /client-mini/src/main/java/com/liubs/shadowrpc/clientmini/handler/RequestHandler.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.clientmini.handler; 2 | 3 | import com.liubs.shadowrpc.clientmini.seriallize.ISerializer; 4 | 5 | /** 6 | * @author Liubsyy 7 | * @date 2024/1/20 8 | **/ 9 | public class RequestHandler { 10 | private ISerializer serializer; 11 | 12 | public RequestHandler(ISerializer serializer) { 13 | this.serializer = serializer; 14 | } 15 | 16 | public byte[] handleMessage(Object obj) { 17 | return serializer.serialize(obj); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /base/src/main/java/com/liubs/shadowrpc/base/util/PackageScanUtil.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.base.util; 2 | 3 | import org.reflections.Reflections; 4 | 5 | import java.lang.annotation.Annotation; 6 | import java.util.Set; 7 | 8 | /** 9 | * @author Liubsyy 10 | * @date 2024/1/15 11 | */ 12 | public class PackageScanUtil { 13 | 14 | public static Set> scanClasses(String packageName, Class annotation) { 15 | Reflections reflections = new Reflections(packageName); 16 | return reflections.getTypesAnnotatedWith(annotation); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /client-mini/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | io.github.liubsyy 8 | shadowrpc 9 | 1.0 10 | ../pom.xml 11 | 12 | 13 | shadowrpc-client-mini 14 | 1.0.0 15 | 16 | -------------------------------------------------------------------------------- /base/src/main/java/com/liubs/shadowrpc/base/annotation/ShadowService.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.base.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * 服务类注解 10 | * @author Liubsyy 11 | * @date 2023/12/18 10:40 PM 12 | **/ 13 | 14 | @Target({ElementType.TYPE}) 15 | @Retention(RetentionPolicy.RUNTIME) 16 | public @interface ShadowService { 17 | 18 | /** 19 | * 服务名字 20 | */ 21 | String serviceName(); 22 | 23 | /** 24 | * 是否同步调用 25 | */ 26 | boolean sync() default true; 27 | 28 | } -------------------------------------------------------------------------------- /registry/src/main/java/com/liubs/shadowrpc/registry/util/IPUtil.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.registry.util; 2 | 3 | import java.net.InetAddress; 4 | import java.net.UnknownHostException; 5 | 6 | /** 7 | * @author Liubsyy 8 | * @date 2023/12/14 9 | */ 10 | public class IPUtil { 11 | 12 | private static String ipAddress = getLocalIp(); 13 | 14 | public static String getLocalIp(){ 15 | InetAddress address = null; 16 | String ip = null; 17 | try { 18 | address = InetAddress.getLocalHost(); 19 | ip = address.getHostAddress(); 20 | } catch (UnknownHostException e) { 21 | e.printStackTrace(); 22 | } 23 | return ip; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /base/src/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /client/src/main/java/com/liubs/shadowrpc/client/loadbalance/PollingBalance.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.client.loadbalance; 2 | 3 | import java.util.concurrent.atomic.AtomicInteger; 4 | 5 | /** 6 | * @author Liubsyy 7 | * @date 2024/1/18 8 | **/ 9 | public class PollingBalance implements IBalance { 10 | private LoadBalanceContext loadBalance; 11 | private AtomicInteger visitIndex = new AtomicInteger(); 12 | 13 | 14 | public PollingBalance(LoadBalanceContext loadBalance) { 15 | this.loadBalance = loadBalance; 16 | } 17 | 18 | @Override 19 | public int getNextBalance(){ 20 | //原子性操作,获取下一个轮询节点 21 | return visitIndex.updateAndGet(c -> (c + 1) % loadBalance.numOfConnections()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /protocol/src/main/java/com/liubs/shadowrpc/protocol/entity/HeartBeatMessage.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.protocol.entity; 2 | 3 | /** 4 | * 心跳消息 5 | * 只用一个字节 (byte)1 即可 6 | * @author Liubsyy 7 | * @date 2023/12/15 9:42 PM 8 | **/ 9 | public class HeartBeatMessage { 10 | 11 | public static final byte PING_MESSAGE = (byte)1; 12 | public static final byte[] PING_BYTE = new byte[]{PING_MESSAGE}; 13 | 14 | public static HeartBeatMessage SINGLETON = new HeartBeatMessage(); 15 | private HeartBeatMessage(){} 16 | 17 | public static boolean isHeartBeatMsg(byte[] bytes) { 18 | return null != bytes && bytes.length == 1 && bytes[0] == PING_MESSAGE; 19 | } 20 | 21 | public static byte[] getHearBeatMsg() { 22 | return PING_BYTE; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /base/src/main/java/com/liubs/shadowrpc/base/util/JsonUtil.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.base.util; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | 6 | /** 7 | * @author Liubsyy 8 | * @date 2023/12/31 9 | **/ 10 | public class JsonUtil { 11 | 12 | private static final ObjectMapper objectMapper = new ObjectMapper(); 13 | 14 | private JsonUtil() { 15 | } 16 | 17 | public static String serialize(Object obj) throws JsonProcessingException { 18 | return objectMapper.writeValueAsString(obj); 19 | } 20 | 21 | public static T deserialize(String json, Class clazz) throws JsonProcessingException { 22 | return objectMapper.readValue(json, clazz); 23 | } 24 | 25 | 26 | } 27 | -------------------------------------------------------------------------------- /client-mini/src/main/java/com/liubs/shadowrpc/clientmini/connection/HeartBeatMessage.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.clientmini.connection; 2 | 3 | /** 4 | * 心跳消息 5 | * 只用一个字节 (byte)1 即可 6 | * @author Liubsyy 7 | * @date 2023/12/15 9:42 PM 8 | **/ 9 | public class HeartBeatMessage { 10 | 11 | public static final byte PING_MESSAGE = (byte)1; 12 | public static final byte[] PING_BYTE = new byte[]{PING_MESSAGE}; 13 | 14 | public static HeartBeatMessage SINGLETON = new HeartBeatMessage(); 15 | private HeartBeatMessage(){} 16 | 17 | public static boolean isHeartBeatMsg(byte[] bytes) { 18 | return null != bytes && bytes.length == 1 && bytes[0] == PING_MESSAGE; 19 | } 20 | 21 | public static byte[] getHearBeatMsg() { 22 | return PING_BYTE; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /protocol/src/main/java/com/liubs/shadowrpc/protocol/model/IModelParser.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.protocol.model; 2 | 3 | 4 | /** 5 | * 从协议中抽象出来的model, 用model去处理应用层面的逻辑 6 | * request,response和model的互相转换 7 | * @author Liubsyy 8 | * @date 2023/12/25 9 | */ 10 | public interface IModelParser { 11 | /** 12 | * 协议转换成RequestModel 13 | */ 14 | RequestModel fromRequest(R request) throws Exception; 15 | 16 | /** 17 | * RequestModel转换成协议 18 | */ 19 | R toRequest(RequestModel requestModel) throws Exception; 20 | 21 | /** 22 | * 协议转换成ResponseModel 23 | */ 24 | ResponseModel fromResponse(S response) throws Exception; 25 | 26 | /** 27 | * ResponseModel转换成协议 28 | */ 29 | S toResponse(ResponseModel responseModel) throws Exception; 30 | } 31 | -------------------------------------------------------------------------------- /protocol/src/main/java/com/liubs/shadowrpc/protocol/serializer/protobuf/ProtobufSerializerBase.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.protocol.serializer.protobuf; 2 | 3 | import com.google.protobuf.MessageLite; 4 | import com.liubs.shadowrpc.protocol.serializer.ISerializer; 5 | 6 | /** 7 | * @author Liubsyy 8 | * @date 2023/12/23 10:17 PM 9 | **/ 10 | public abstract class ProtobufSerializerBase implements ISerializer { 11 | 12 | @Override 13 | public byte[] serialize(Object object) { 14 | if (object instanceof MessageLite) { 15 | return ((MessageLite) object).toByteArray(); 16 | } 17 | if (object instanceof MessageLite.Builder) { 18 | return (((MessageLite.Builder) object).build().toByteArray()); 19 | } 20 | 21 | return new byte[0]; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /protocol/src/main/java/com/liubs/shadowrpc/protocol/constant/ResponseCode.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.protocol.constant; 2 | 3 | /** 4 | * @author Liubsyy 5 | * @date 2023/12/23 1:47 PM 6 | **/ 7 | public enum ResponseCode { 8 | 9 | SUCCESS(10,"成功"), 10 | FAIL(40,"失败"), 11 | 12 | ; 13 | private int code; 14 | private String message; 15 | 16 | ResponseCode(int code, String message) { 17 | this.code = code; 18 | this.message = message; 19 | } 20 | 21 | 22 | public int getCode() { 23 | return code; 24 | } 25 | 26 | public void setCode(int code) { 27 | this.code = code; 28 | } 29 | 30 | public String getMessage() { 31 | return message; 32 | } 33 | 34 | public void setMessage(String message) { 35 | this.message = message; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /server/src/main/java/com/liubs/shadowrpc/server/service/ServiceTarget.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.server.service; 2 | 3 | import java.lang.reflect.InvocationTargetException; 4 | import java.lang.reflect.Method; 5 | 6 | /** 7 | * @author Liubsyy 8 | * @date 2023/12/28 9 | **/ 10 | public class ServiceTarget { 11 | private Object targetObj; 12 | private Method method; 13 | 14 | public Object getTargetObj() { 15 | return targetObj; 16 | } 17 | 18 | public void setTargetObj(Object targetObj) { 19 | this.targetObj = targetObj; 20 | } 21 | 22 | public Method getMethod() { 23 | return method; 24 | } 25 | 26 | public void setMethod(Method method) { 27 | this.method = method; 28 | } 29 | 30 | public Object invoke(Object [] params) throws InvocationTargetException, IllegalAccessException { 31 | return method.invoke(targetObj,params); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /client-mini/src/test/java/rpctest/entity/MyMessage.java: -------------------------------------------------------------------------------- 1 | package rpctest.entity; 2 | 3 | 4 | import java.io.Serializable; 5 | 6 | /** 7 | * @author Liubsyy 8 | * @date 2023/12/18 10:58 PM 9 | **/ 10 | public class MyMessage implements Serializable { 11 | 12 | private String content; 13 | 14 | private int num; 15 | 16 | 17 | public MyMessage() { 18 | } 19 | 20 | 21 | public String getContent() { 22 | return content; 23 | } 24 | 25 | public void setContent(String content) { 26 | this.content = content; 27 | } 28 | 29 | public int getNum() { 30 | return num; 31 | } 32 | 33 | public void setNum(int num) { 34 | this.num = num; 35 | } 36 | 37 | 38 | @Override 39 | public String toString() { 40 | return "MyMessage{" + 41 | "content='" + content + '\'' + 42 | ", num=" + num + 43 | '}'; 44 | } 45 | } -------------------------------------------------------------------------------- /base/src/main/java/com/liubs/shadowrpc/base/constant/SerializerEnum.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.base.constant; 2 | 3 | /** 4 | * @author Liubsyy 5 | * @date 2024/1/15 6 | */ 7 | public enum SerializerEnum { 8 | KRYO("KRYO","kryo序列化"), 9 | PROTOBUF("PROTOBUF","protobuf序列化"), 10 | JAVA_SERIALISE("JAVA_SERIALIZE","java原生序列化"), 11 | 12 | ; 13 | 14 | private String serializeType; 15 | private String text; 16 | 17 | SerializerEnum(String serializeType, String text) { 18 | this.serializeType = serializeType; 19 | this.text = text; 20 | } 21 | 22 | public static SerializerEnum findByType(String serializeType) { 23 | for(SerializerEnum e : values()) { 24 | if(e.serializeType.equals(serializeType)) { 25 | return e; 26 | } 27 | } 28 | return null; 29 | } 30 | 31 | public String getSerializeType() { 32 | return serializeType; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /client/src/main/java/com/liubs/shadowrpc/client/handler/HeartBeatHandler.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.client.handler; 2 | 3 | import com.liubs.shadowrpc.protocol.entity.HeartBeatMessage; 4 | import io.netty.channel.ChannelHandlerContext; 5 | import io.netty.handler.timeout.IdleStateEvent; 6 | import io.netty.handler.timeout.IdleStateHandler; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | 11 | /** 12 | * @author Liubsyy 13 | * @date 2023/12/15 9:35 PM 14 | **/ 15 | 16 | public class HeartBeatHandler extends IdleStateHandler { 17 | private static final Logger logger = LoggerFactory.getLogger(HeartBeatMessage.class); 18 | 19 | public HeartBeatHandler(int heartBeatWaitSeconds) { 20 | super(0, 0, 3); 21 | } 22 | 23 | 24 | @Override 25 | protected void channelIdle(ChannelHandlerContext ctx, IdleStateEvent evt) { 26 | logger.debug("发送心跳消息..."); 27 | ctx.writeAndFlush(HeartBeatMessage.SINGLETON); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | replay_pid* 25 | 26 | 27 | target/ 28 | !.mvn/wrapper/maven-wrapper.jar 29 | !**/src/main/**/target/ 30 | !**/src/test/**/target/ 31 | 32 | ### IntelliJ IDEA ### 33 | /.idea/ 34 | *.iws 35 | *.iml 36 | *.ipr 37 | 38 | ### Eclipse ### 39 | .apt_generated 40 | .classpath 41 | .factorypath 42 | .project 43 | .settings 44 | .springBeans 45 | .sts4-cache 46 | 47 | ### NetBeans ### 48 | /nbproject/private/ 49 | /nbbuild/ 50 | /dist/ 51 | /nbdist/ 52 | /.nb-gradle/ 53 | build/ 54 | !**/src/main/**/build/ 55 | !**/src/test/**/build/ 56 | 57 | ### VS Code ### 58 | .vscode/ 59 | 60 | ### Mac OS ### 61 | .DS_Store -------------------------------------------------------------------------------- /registry/src/main/java/com/liubs/shadowrpc/registry/constant/ServiceRegistryPath.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.registry.constant; 2 | 3 | 4 | /** 5 | * @author Liubsyy 6 | * @date 2023/12/18 11:35 PM 7 | **/ 8 | public class ServiceRegistryPath { 9 | 10 | //服务基本路径 11 | public static final String BASE_PATH = "/shadowrpc/services"; 12 | 13 | 14 | /** 15 | * 服务节点目录 16 | * @param group 17 | * @param serverUniqueKey 18 | * @return 19 | */ 20 | public static String getServerNodePath(String group,String serverUniqueKey) { 21 | return String.format("%s/%s/%s",BASE_PATH,group,serverUniqueKey); 22 | } 23 | 24 | 25 | public static String uniqueKey(String host,int port) { 26 | return String.format("%s:%d",host,port); 27 | } 28 | 29 | 30 | /** 31 | * group的目录 32 | * @param group 33 | * @return 34 | */ 35 | public static String getServerGroupPath(String group) { 36 | return String.format("%s/%s",BASE_PATH,group); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /client/src/main/java/com/liubs/shadowrpc/client/ClientModule.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.client; 2 | 3 | import com.liubs.shadowrpc.base.annotation.ModuleInject; 4 | import com.liubs.shadowrpc.base.annotation.ShadowModule; 5 | import com.liubs.shadowrpc.base.config.ClientConfig; 6 | import com.liubs.shadowrpc.base.module.IModule; 7 | import com.liubs.shadowrpc.protocol.SerializeModule; 8 | 9 | import java.util.Collections; 10 | import java.util.List; 11 | 12 | /** 13 | * @author Liubsyy 14 | * @date 2024/1/16 15 | */ 16 | 17 | @ShadowModule 18 | public class ClientModule implements IModule { 19 | 20 | private ClientConfig config; 21 | 22 | @ModuleInject 23 | private SerializeModule serializeModule; 24 | 25 | public void init(ClientConfig config){ 26 | init(config, Collections.EMPTY_LIST); 27 | } 28 | public void init(ClientConfig config, List packages){ 29 | this.config = config; 30 | serializeModule.init(config,packages); 31 | } 32 | 33 | public ClientConfig getConfig() { 34 | return config; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /client/src/main/java/com/liubs/shadowrpc/client/loadbalance/LoadBalanceContext.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.client.loadbalance; 2 | 3 | import com.liubs.shadowrpc.client.connection.ShadowClient; 4 | import com.liubs.shadowrpc.client.connection.ShadowClientGroup; 5 | 6 | /** 7 | * @author Liubsyy 8 | * @date 2024/1/18 9 | **/ 10 | public class LoadBalanceContext { 11 | 12 | private String group; 13 | private PollingBalance pollingBalance; 14 | private ShadowClientGroup shadowClientGroup; 15 | 16 | public LoadBalanceContext(String group,ShadowClientGroup shadowClientGroup) { 17 | this.group = group; 18 | this.shadowClientGroup = shadowClientGroup; 19 | pollingBalance = new PollingBalance(this); 20 | } 21 | 22 | 23 | public int numOfConnections() { 24 | return shadowClientGroup.getShadowClients(group).size(); 25 | } 26 | 27 | public ShadowClient getBalanceShadowClient(){ 28 | int nextBalance = pollingBalance.getNextBalance(); 29 | return shadowClientGroup.getShadowClients(group).get(nextBalance); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /protocol/src/main/java/com/liubs/shadowrpc/protocol/model/ResponseModel.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.protocol.model; 2 | 3 | /** 4 | * 抽象出来的response实体,任何协议请求格式都转化成这个model然后处理逻辑 5 | * @author Liubsyy 6 | * @date 2023/12/25 7 | */ 8 | public class ResponseModel { 9 | private String traceId; 10 | 11 | private int code; 12 | 13 | private String errorMsg; 14 | 15 | private Object result; 16 | 17 | public String getTraceId() { 18 | return traceId; 19 | } 20 | 21 | public void setTraceId(String traceId) { 22 | this.traceId = traceId; 23 | } 24 | 25 | public int getCode() { 26 | return code; 27 | } 28 | 29 | public void setCode(int code) { 30 | this.code = code; 31 | } 32 | 33 | public String getErrorMsg() { 34 | return errorMsg; 35 | } 36 | 37 | public void setErrorMsg(String errorMsg) { 38 | this.errorMsg = errorMsg; 39 | } 40 | 41 | public Object getResult() { 42 | return result; 43 | } 44 | 45 | public void setResult(Object result) { 46 | this.result = result; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /base/src/main/java/com/liubs/shadowrpc/base/annotation/ShadowServiceHolder.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.base.annotation; 2 | 3 | import java.lang.annotation.Annotation; 4 | 5 | /** 6 | * @author Liubsyy 7 | * @date 2023/12/18 10:51 PM 8 | **/ 9 | public class ShadowServiceHolder { 10 | 11 | private T annotation; 12 | private Class classz; 13 | 14 | public ShadowServiceHolder(T annotation, Class classz) { 15 | this.annotation = annotation; 16 | this.classz = classz; 17 | } 18 | 19 | public T getAnnotation() { 20 | return annotation; 21 | } 22 | 23 | public void setAnnotation(T annotation) { 24 | this.annotation = annotation; 25 | } 26 | 27 | public Class getClassz() { 28 | return classz; 29 | } 30 | 31 | public void setClassz(Class classz) { 32 | this.classz = classz; 33 | } 34 | 35 | @Override 36 | public String toString() { 37 | return "ShadowServiceHolder{" + 38 | "annotation=" + annotation + 39 | ", classz=" + classz + 40 | '}'; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /protocol/src/main/java/com/liubs/shadowrpc/protocol/serializer/kryo/KryoFieldSerializerFactory.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.protocol.serializer.kryo; 2 | 3 | import com.esotericsoftware.kryo.Kryo; 4 | import com.esotericsoftware.kryo.SerializerFactory; 5 | import com.esotericsoftware.kryo.serializers.TaggedFieldSerializer; 6 | 7 | /** 8 | * @author Liubsyy 9 | * @date 2023/12/18 10:43 PM 10 | **/ 11 | public class KryoFieldSerializerFactory extends SerializerFactory.BaseSerializerFactory { 12 | private final TaggedFieldSerializer.TaggedFieldSerializerConfig config; 13 | 14 | public KryoFieldSerializerFactory () { 15 | this.config = new TaggedFieldSerializer.TaggedFieldSerializerConfig(); 16 | 17 | //chunkedEncoding设置为true, 写入字段长度,这样一方新增字段后,其他端序列化和反序列化都能识别并选择跳过 18 | this.config.setChunkedEncoding(true); 19 | 20 | } 21 | 22 | public TaggedFieldSerializer.TaggedFieldSerializerConfig getConfig () { 23 | return config; 24 | } 25 | 26 | public KryoFieldSerializer newSerializer (Kryo kryo, Class type) { 27 | return new KryoFieldSerializer(kryo, type, config.clone()); 28 | } 29 | } -------------------------------------------------------------------------------- /protocol/src/main/java/com/liubs/shadowrpc/protocol/entity/JavaSerializeRPCResponse.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.protocol.entity; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * @author Liubsyy 7 | * @date 2024/1/20 8 | **/ 9 | public class JavaSerializeRPCResponse implements Serializable { 10 | 11 | private String traceId; 12 | 13 | private int code; 14 | 15 | private String errorMsg; 16 | 17 | private Object result; 18 | 19 | public String getTraceId() { 20 | return traceId; 21 | } 22 | 23 | public void setTraceId(String traceId) { 24 | this.traceId = traceId; 25 | } 26 | 27 | public int getCode() { 28 | return code; 29 | } 30 | 31 | public void setCode(int code) { 32 | this.code = code; 33 | } 34 | 35 | public String getErrorMsg() { 36 | return errorMsg; 37 | } 38 | 39 | public void setErrorMsg(String errorMsg) { 40 | this.errorMsg = errorMsg; 41 | } 42 | 43 | public Object getResult() { 44 | return result; 45 | } 46 | 47 | public void setResult(Object result) { 48 | this.result = result; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /client-mini/src/main/java/com/liubs/shadowrpc/protocol/entity/JavaSerializeRPCResponse.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.protocol.entity; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * @author Liubsyy 7 | * @date 2024/1/20 8 | **/ 9 | public class JavaSerializeRPCResponse implements Serializable { 10 | 11 | private String traceId; 12 | 13 | private int code; 14 | 15 | private String errorMsg; 16 | 17 | private Object result; 18 | 19 | public String getTraceId() { 20 | return traceId; 21 | } 22 | 23 | public void setTraceId(String traceId) { 24 | this.traceId = traceId; 25 | } 26 | 27 | public int getCode() { 28 | return code; 29 | } 30 | 31 | public void setCode(int code) { 32 | this.code = code; 33 | } 34 | 35 | public String getErrorMsg() { 36 | return errorMsg; 37 | } 38 | 39 | public void setErrorMsg(String errorMsg) { 40 | this.errorMsg = errorMsg; 41 | } 42 | 43 | public Object getResult() { 44 | return result; 45 | } 46 | 47 | public void setResult(Object result) { 48 | this.result = result; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /protocol/src/main/java/com/liubs/shadowrpc/protocol/serializer/protobuf/ShadowProtobufSerializer.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.protocol.serializer.protobuf; 2 | 3 | import com.liubs.shadowrpc.protocol.entity.ShadowRPCRequestProto; 4 | import com.liubs.shadowrpc.protocol.entity.ShadowRPCResponseProto; 5 | 6 | /** 7 | * @author Liubsyy 8 | * @date 2023/12/23 9:09 PM 9 | **/ 10 | public class ShadowProtobufSerializer extends ProtobufSerializerBase { 11 | 12 | private final ProtobufSerializer requestSerializer = new ProtobufSerializer(ShadowRPCRequestProto.ShadowRPCRequest.getDefaultInstance()); 13 | private final ProtobufSerializer responseSerializer = new ProtobufSerializer(ShadowRPCResponseProto.ShadowRPCResponse.getDefaultInstance()); 14 | 15 | 16 | @Override 17 | public T deserialize(byte[] array, Class clazz) { 18 | if(clazz == ShadowRPCRequestProto.ShadowRPCRequest.class) { 19 | return requestSerializer.deserialize(array,clazz); 20 | } 21 | if(clazz == ShadowRPCResponseProto.ShadowRPCResponse.class) { 22 | return responseSerializer.deserialize(array,clazz); 23 | } 24 | return null; 25 | } 26 | 27 | 28 | } 29 | -------------------------------------------------------------------------------- /base/src/test/java/JacksonTest.java: -------------------------------------------------------------------------------- 1 | import com.fasterxml.jackson.core.JsonProcessingException; 2 | import com.liubs.shadowrpc.base.util.JsonUtil; 3 | import org.junit.Test; 4 | 5 | /** 6 | * @author Liubsyy 7 | * @date 2023/12/31 8 | **/ 9 | public class JacksonTest { 10 | 11 | 12 | @Test 13 | public void test() throws JsonProcessingException { 14 | MyObject obj = new MyObject(); 15 | obj.setField1("value1"); 16 | obj.setField2(123); 17 | 18 | String json = JsonUtil.serialize(obj); 19 | System.out.println(json); 20 | 21 | MyObject newObj = JsonUtil.deserialize(json, MyObject.class); 22 | System.out.println(newObj.getField1()); 23 | } 24 | 25 | 26 | class MyObject { 27 | private String field1; 28 | private int field2; 29 | 30 | public String getField1() { 31 | return field1; 32 | } 33 | 34 | public void setField1(String field1) { 35 | this.field1 = field1; 36 | } 37 | 38 | public int getField2() { 39 | return field2; 40 | } 41 | 42 | public void setField2(int field2) { 43 | this.field2 = field2; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /server/src/main/java/com/liubs/shadowrpc/server/init/ServerBuilder.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.server.init; 2 | 3 | import com.liubs.shadowrpc.base.config.ServerConfig; 4 | import com.liubs.shadowrpc.server.service.ServerManager; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | /** 10 | * @author Liubsyy 11 | * @date 2024/1/16 12 | */ 13 | public class ServerBuilder { 14 | 15 | private ServerConfig serverConfig; 16 | private List packages; 17 | 18 | private ServerBuilder(){ 19 | packages = new ArrayList<>(); 20 | } 21 | 22 | public static ServerBuilder newBuilder(){ 23 | return new ServerBuilder(); 24 | } 25 | 26 | 27 | public ServerBuilder addPackage(String packageName) { 28 | this.packages.add(packageName); 29 | return this; 30 | } 31 | 32 | public ServerBuilder serverConfig(ServerConfig serverConfig) { 33 | this.serverConfig = serverConfig; 34 | return this; 35 | } 36 | 37 | public ServerManager build(){ 38 | ServerManager serverManager = new ServerManager(); 39 | serverManager.setServerConfig(serverConfig); 40 | serverManager.setPackageNames(packages); 41 | return serverManager; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /client-mini/src/test/java/niotest/NIOClientTest.java: -------------------------------------------------------------------------------- 1 | package niotest; 2 | 3 | import com.liubs.shadowrpc.clientmini.nio.IMessageListener; 4 | import com.liubs.shadowrpc.clientmini.nio.NIOClient; 5 | import com.liubs.shadowrpc.clientmini.nio.NIOConfig; 6 | import org.junit.Test; 7 | 8 | /** 9 | * @author Liubsyy 10 | * @date 2024/1/20 11 | **/ 12 | public class NIOClientTest implements IMessageListener { 13 | 14 | /** 15 | * 开启服务器 16 | * @throws Exception 17 | */ 18 | @Test 19 | public void runServer() throws Exception { 20 | NIOServer server = new NIOServer(8080); 21 | server.start(); 22 | } 23 | 24 | /** 25 | * 连接服务器 26 | * @throws Exception 27 | */ 28 | @Test 29 | public void testClient() throws Exception { 30 | NIOClient client = new NIOClient("localhost", 8080,new NIOConfig(),this); 31 | client.connect(); 32 | 33 | client.sendMessage("Hello, Server!".getBytes()); 34 | 35 | Thread.sleep(3000); 36 | 37 | client.close(); 38 | } 39 | 40 | @Override 41 | public void handleMessage(byte[] bytes) { 42 | String message = new String(bytes).trim(); 43 | System.out.println("Received complete message: " + message); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /protocol/src/main/java/com/liubs/shadowrpc/protocol/util/AnnotationScanner.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.protocol.util; 2 | 3 | 4 | import com.liubs.shadowrpc.base.util.ClassScanWalker; 5 | import com.liubs.shadowrpc.base.annotation.ShadowServiceHolder; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import java.io.IOException; 10 | import java.lang.annotation.Annotation; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | 15 | /** 16 | * @author Liubsyy 17 | * @date 2023/12/12 21:54 18 | */ 19 | public class AnnotationScanner { 20 | private static final Logger logger = LoggerFactory.getLogger(AnnotationScanner.class); 21 | 22 | public static List> scanAnnotations(String packageName, Class annotation) throws IOException { 23 | 24 | List> allResults = new ArrayList<>(); 25 | ClassScanWalker.scanPackage(packageName, clazz->{ 26 | T shadowServiceAnno = clazz.getAnnotation(annotation); 27 | if (shadowServiceAnno != null) { 28 | allResults.add(new ShadowServiceHolder<>(shadowServiceAnno, clazz)); 29 | } 30 | }); 31 | 32 | logger.info("scanAnnotations="+allResults); 33 | return allResults; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /server/src/test/java/rpctest/entity/MyMessage.java: -------------------------------------------------------------------------------- 1 | package rpctest.entity; 2 | 3 | import com.liubs.shadowrpc.protocol.annotation.ShadowEntity; 4 | import com.liubs.shadowrpc.protocol.annotation.ShadowField; 5 | 6 | import java.io.Serializable; 7 | 8 | /** 9 | * @author Liubsyy 10 | * @date 2023/12/18 10:58 PM 11 | **/ 12 | @ShadowEntity 13 | public class MyMessage implements Serializable { 14 | 15 | @ShadowField(1) 16 | private String content; 17 | 18 | @ShadowField(2) 19 | private int num; 20 | 21 | /** 22 | * 客户端新增字段 23 | * request: 客户端新增字段,服务端没有这个字段,服务端会跳过这个字段进行反序列化 24 | * response: 服务端没有这个字段,客户端能缺省默认值反序列化 25 | 26 | @ShadowField(3) 27 | private String addField = "addField1"; */ 28 | 29 | public MyMessage() { 30 | } 31 | 32 | 33 | public String getContent() { 34 | return content; 35 | } 36 | 37 | public void setContent(String content) { 38 | this.content = content; 39 | } 40 | 41 | public int getNum() { 42 | return num; 43 | } 44 | 45 | public void setNum(int num) { 46 | this.num = num; 47 | } 48 | 49 | 50 | @Override 51 | public String toString() { 52 | return "MyMessage{" + 53 | "content='" + content + '\'' + 54 | ", num=" + num + 55 | '}'; 56 | } 57 | } -------------------------------------------------------------------------------- /registry/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | io.github.liubsyy 8 | shadowrpc 9 | 1.0 10 | 11 | 12 | shadowrpc-registry 13 | 1.0.0 14 | 15 | 16 | 17 | io.github.liubsyy 18 | shadowrpc-base 19 | 1.0.0 20 | compile 21 | 22 | 23 | 24 | org.apache.curator 25 | curator-framework 26 | ${curator.framework.version} 27 | 28 | 29 | org.apache.curator 30 | curator-recipes 31 | ${curator.framework.version} 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /client/src/main/java/com/liubs/shadowrpc/client/handler/ReceiveHolder.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.client.handler; 2 | 3 | 4 | import com.liubs.shadowrpc.protocol.model.ResponseModel; 5 | 6 | import java.util.Map; 7 | import java.util.concurrent.*; 8 | 9 | /** 10 | * @author Liubsyy 11 | * @date 2023/12/3 11:32 PM 12 | **/ 13 | public class ReceiveHolder { 14 | public Map> futureMap = new ConcurrentHashMap<>(); 15 | 16 | private static ReceiveHolder instance = new ReceiveHolder(); 17 | public static ReceiveHolder getInstance() { 18 | return instance; 19 | } 20 | 21 | 22 | public Future initFuture(String uuid){ 23 | CompletableFuture completableFuture = new CompletableFuture<>(); 24 | futureMap.put(uuid,completableFuture); 25 | return completableFuture; 26 | } 27 | 28 | public void deleteWait(String uuid) { 29 | CompletableFuture remove = futureMap.remove(uuid); 30 | if(null != remove) { 31 | remove.cancel(true); 32 | } 33 | } 34 | 35 | 36 | public void receiveData(ResponseModel responseModel){ 37 | CompletableFuture future = futureMap.remove(responseModel.getTraceId()); 38 | if(null != future) { 39 | future.complete(responseModel); 40 | } 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /client-mini/src/main/java/com/liubs/shadowrpc/clientmini/handler/ReceiveHolder.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.clientmini.handler; 2 | 3 | import com.liubs.shadowrpc.protocol.entity.JavaSerializeRPCResponse; 4 | 5 | import java.util.Map; 6 | import java.util.concurrent.CompletableFuture; 7 | import java.util.concurrent.ConcurrentHashMap; 8 | import java.util.concurrent.Future; 9 | 10 | /** 11 | * @author Liubsyy 12 | * @date 2023/12/3 11:32 PM 13 | **/ 14 | public class ReceiveHolder { 15 | public Map> futureMap = new ConcurrentHashMap<>(); 16 | 17 | private static ReceiveHolder instance = new ReceiveHolder(); 18 | public static ReceiveHolder getInstance() { 19 | return instance; 20 | } 21 | 22 | 23 | public Future initFuture(String uuid){ 24 | CompletableFuture completableFuture = new CompletableFuture<>(); 25 | futureMap.put(uuid,completableFuture); 26 | return completableFuture; 27 | } 28 | 29 | public void deleteWait(String uuid) { 30 | futureMap.remove(uuid); 31 | } 32 | 33 | 34 | public void receiveData(JavaSerializeRPCResponse responseModel){ 35 | CompletableFuture future = futureMap.remove(responseModel.getTraceId()); 36 | if(null != future) { 37 | future.complete(responseModel); 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /client/src/main/java/com/liubs/shadowrpc/client/handler/ShadowChannelInitializer.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.client.handler; 2 | 3 | import com.liubs.shadowrpc.base.config.ClientConfig; 4 | import io.netty.channel.ChannelInitializer; 5 | import io.netty.channel.ChannelPipeline; 6 | import io.netty.channel.socket.SocketChannel; 7 | import io.netty.handler.codec.LengthFieldBasedFrameDecoder; 8 | 9 | /** 10 | * @author Liubsyy 11 | * @date 2023/12/4 11:53 PM 12 | **/ 13 | public class ShadowChannelInitializer extends ChannelInitializer { 14 | 15 | private ClientConfig config; 16 | 17 | public ShadowChannelInitializer(ClientConfig config) { 18 | this.config = config; 19 | } 20 | 21 | @Override 22 | protected void initChannel(SocketChannel ch) throws Exception { 23 | 24 | ChannelPipeline pipeline = ch.pipeline(); 25 | 26 | //处理帧边界,解决拆包和粘包问题 27 | pipeline.addLast(new LengthFieldBasedFrameDecoder(config.getMaxFrameLength(), 28 | 0, 4, 0, 4)); 29 | 30 | //消息序列化和反序列化 31 | pipeline.addLast(new MessageHandler()); 32 | 33 | //心跳机制 34 | if(config.isHeartBeat()) { 35 | pipeline.addLast(new HeartBeatHandler(config.getHeartBeatWaitSeconds())); 36 | } 37 | 38 | 39 | //处理消息的逻辑 40 | pipeline.addLast(new ClientHandler()); 41 | 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /server/src/main/java/com/liubs/shadowrpc/server/handler/ShadowChannelInitializer.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.server.handler; 2 | 3 | 4 | import com.liubs.shadowrpc.base.config.ServerConfig; 5 | import io.netty.channel.ChannelInitializer; 6 | import io.netty.channel.ChannelPipeline; 7 | import io.netty.channel.socket.SocketChannel; 8 | import io.netty.handler.codec.LengthFieldBasedFrameDecoder; 9 | 10 | /** 11 | * @author Liubsyy 12 | * @date 2023/12/4 11:45 PM 13 | **/ 14 | public class ShadowChannelInitializer extends ChannelInitializer { 15 | 16 | private ServerConfig serverConfig; 17 | 18 | public ShadowChannelInitializer(ServerConfig serverConfig) { 19 | this.serverConfig = serverConfig; 20 | } 21 | 22 | @Override 23 | protected void initChannel(SocketChannel ch) throws Exception { 24 | ChannelPipeline pipeline = ch.pipeline(); 25 | 26 | 27 | //qps请求量统计 28 | if(serverConfig.isQpsStat()) { 29 | pipeline.addLast(new QpsStatHandler()); 30 | } 31 | 32 | //处理帧边界,解决拆包和粘包问题 33 | pipeline.addLast(new LengthFieldBasedFrameDecoder(serverConfig.getMaxFrameLength(), 34 | 0, 4, 0, 4)); 35 | 36 | //消息序列化和反序列化 37 | pipeline.addLast(new MessageHandler()); 38 | 39 | ServerHandler serverHandler = new ServerHandler(); 40 | pipeline.addLast(serverHandler); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /protocol/src/main/java/com/liubs/shadowrpc/protocol/serializer/protobuf/ProtobufSerializer.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.protocol.serializer.protobuf; 2 | 3 | import com.google.protobuf.ExtensionRegistry; 4 | import com.google.protobuf.ExtensionRegistryLite; 5 | import com.google.protobuf.MessageLite; 6 | 7 | /** 8 | * protobuf序列化和反序列化 9 | * @author Liubsyy 10 | * @date 2023/12/23 4:59 PM 11 | **/ 12 | public class ProtobufSerializer extends ProtobufSerializerBase { 13 | 14 | private MessageLite prototype; 15 | private ExtensionRegistryLite extensionRegistry; 16 | 17 | 18 | public ProtobufSerializer(MessageLite prototype) { 19 | this(prototype, null); 20 | } 21 | 22 | public ProtobufSerializer(MessageLite prototype, ExtensionRegistry extensionRegistry) { 23 | this(prototype, (ExtensionRegistryLite) extensionRegistry); 24 | } 25 | 26 | public ProtobufSerializer(MessageLite prototype, ExtensionRegistryLite extensionRegistry) { 27 | this.prototype = prototype.getDefaultInstanceForType(); 28 | this.extensionRegistry = extensionRegistry; 29 | } 30 | 31 | 32 | @Override 33 | public T deserialize(byte[] array, Class clazz) { 34 | try{ 35 | return ParserForType.parseFrom(prototype,extensionRegistry,array); 36 | }catch (Exception e) { 37 | throw new RuntimeException(e); 38 | } 39 | } 40 | 41 | 42 | } 43 | -------------------------------------------------------------------------------- /protocol/src/main/java/com/liubs/shadowrpc/protocol/model/RequestModel.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.protocol.model; 2 | 3 | 4 | /** 5 | * 抽象出来的request实体,任何协议请求格式都转化成这个model然后处理逻辑 6 | * @author Liubsyy 7 | * @date 2023/12/25 8 | */ 9 | public class RequestModel { 10 | 11 | private String traceId; 12 | 13 | private String serviceName; 14 | 15 | private String methodName; 16 | 17 | private Class[] paramTypes; 18 | 19 | private Object[] params; 20 | 21 | public String getTraceId() { 22 | return traceId; 23 | } 24 | 25 | public void setTraceId(String traceId) { 26 | this.traceId = traceId; 27 | } 28 | 29 | public String getServiceName() { 30 | return serviceName; 31 | } 32 | 33 | public void setServiceName(String serviceName) { 34 | this.serviceName = serviceName; 35 | } 36 | 37 | public String getMethodName() { 38 | return methodName; 39 | } 40 | 41 | public void setMethodName(String methodName) { 42 | this.methodName = methodName; 43 | } 44 | 45 | public Class[] getParamTypes() { 46 | return paramTypes; 47 | } 48 | 49 | public void setParamTypes(Class[] paramTypes) { 50 | this.paramTypes = paramTypes; 51 | } 52 | 53 | public Object[] getParams() { 54 | return params; 55 | } 56 | 57 | public void setParams(Object[] params) { 58 | this.params = params; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /client-mini/src/main/java/com/liubs/shadowrpc/clientmini/handler/ResponseHandler.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.clientmini.handler; 2 | 3 | import com.liubs.shadowrpc.clientmini.connection.HeartBeatMessage; 4 | import com.liubs.shadowrpc.clientmini.logger.Logger; 5 | import com.liubs.shadowrpc.clientmini.nio.IMessageListener; 6 | import com.liubs.shadowrpc.clientmini.seriallize.ISerializer; 7 | import com.liubs.shadowrpc.protocol.entity.JavaSerializeRPCResponse; 8 | 9 | /** 10 | * @author Liubsyy 11 | * @date 2024/1/20 12 | **/ 13 | 14 | public class ResponseHandler implements IMessageListener { 15 | 16 | private static final int SUCCESS = 10; 17 | 18 | private static Logger logger = Logger.getLogger(ResponseHandler.class); 19 | 20 | private ISerializer serializer; 21 | 22 | public ResponseHandler(ISerializer serializer) { 23 | this.serializer = serializer; 24 | } 25 | 26 | /** 27 | * 处理的是已经分割完的真正的数据 28 | * @param bytes 29 | */ 30 | @Override 31 | public void handleMessage(byte[] bytes) { 32 | 33 | //心跳消息 34 | if(HeartBeatMessage.isHeartBeatMsg(bytes)) { 35 | return; 36 | } 37 | 38 | JavaSerializeRPCResponse response = serializer.deserialize(bytes, JavaSerializeRPCResponse.class); 39 | if(response == null) { 40 | return; 41 | } 42 | 43 | //接收消息 44 | ReceiveHolder.getInstance().receiveData(response); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /client-mini/src/main/java/com/liubs/shadowrpc/clientmini/nio/MessageSendFuture.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.clientmini.nio; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.util.concurrent.CompletableFuture; 5 | import java.util.concurrent.ExecutionException; 6 | 7 | /** 8 | * @author Liubsyy 9 | * @date 2024/1/21 10 | **/ 11 | public class MessageSendFuture extends CompletableFuture { 12 | private ByteBuffer buffer; 13 | 14 | public MessageSendFuture(ByteBuffer buffer) { 15 | this.buffer = buffer; 16 | } 17 | 18 | public ByteBuffer getBuffer() { 19 | return buffer; 20 | } 21 | 22 | 23 | public static void main(String[] args) throws ExecutionException, InterruptedException { 24 | 25 | MessageSendFuture messageSendFuture = new MessageSendFuture(null); 26 | 27 | new Thread(){ 28 | @Override 29 | public void run() { 30 | try { 31 | Thread.sleep(2000); 32 | } catch (InterruptedException e) { 33 | throw new RuntimeException(e); 34 | } 35 | messageSendFuture.completeExceptionally(new RuntimeException("aaa")); 36 | } 37 | }.start(); 38 | 39 | try{ 40 | messageSendFuture.get(); 41 | }catch (Exception e) { 42 | e.printStackTrace(); 43 | } 44 | 45 | 46 | System.out.println(111); 47 | 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /base/src/main/java/com/liubs/shadowrpc/base/config/BaseConfig.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.base.config; 2 | 3 | import com.liubs.shadowrpc.base.constant.SerializerEnum; 4 | 5 | /** 6 | * @author Liubsyy 7 | * @date 2024/1/15 8 | */ 9 | public class BaseConfig { 10 | 11 | //序列化方式, 详见 com.liubs.shadowrpc.base.constant.SerializerEnum 12 | private String serializer = SerializerEnum.KRYO.name(); 13 | 14 | //协议包最大上限(字节) 15 | private int maxFrameLength = 65535; 16 | 17 | //心跳开关 18 | private boolean heartBeat = true; 19 | 20 | //心跳时间间隔(默认10s) 21 | private int heartBeatWaitSeconds = 10; 22 | 23 | public String getSerializer() { 24 | return serializer; 25 | } 26 | 27 | public void setSerializer(String serializer) { 28 | this.serializer = serializer; 29 | } 30 | 31 | public int getMaxFrameLength() { 32 | return maxFrameLength; 33 | } 34 | 35 | public void setMaxFrameLength(int maxFrameLength) { 36 | this.maxFrameLength = maxFrameLength; 37 | } 38 | 39 | public boolean isHeartBeat() { 40 | return heartBeat; 41 | } 42 | 43 | public void setHeartBeat(boolean heartBeat) { 44 | this.heartBeat = heartBeat; 45 | } 46 | 47 | public int getHeartBeatWaitSeconds() { 48 | return heartBeatWaitSeconds; 49 | } 50 | 51 | public void setHeartBeatWaitSeconds(int heartBeatWaitSeconds) { 52 | this.heartBeatWaitSeconds = heartBeatWaitSeconds; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /protocol/src/main/java/com/liubs/shadowrpc/protocol/entity/JavaSerializeRPCRequest.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.protocol.entity; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * @author Liubsyy 7 | * @date 2024/1/20 8 | **/ 9 | public class JavaSerializeRPCRequest implements Serializable { 10 | 11 | 12 | private String traceId; 13 | 14 | private String serviceName; 15 | 16 | private String methodName; 17 | 18 | private Class[] paramTypes; 19 | 20 | private Object[] params; 21 | 22 | public String getTraceId() { 23 | return traceId; 24 | } 25 | 26 | public void setTraceId(String traceId) { 27 | this.traceId = traceId; 28 | } 29 | 30 | public String getServiceName() { 31 | return serviceName; 32 | } 33 | 34 | public void setServiceName(String serviceName) { 35 | this.serviceName = serviceName; 36 | } 37 | 38 | public String getMethodName() { 39 | return methodName; 40 | } 41 | 42 | public void setMethodName(String methodName) { 43 | this.methodName = methodName; 44 | } 45 | 46 | public Class[] getParamTypes() { 47 | return paramTypes; 48 | } 49 | 50 | public void setParamTypes(Class[] paramTypes) { 51 | this.paramTypes = paramTypes; 52 | } 53 | 54 | public Object[] getParams() { 55 | return params; 56 | } 57 | 58 | public void setParams(Object[] params) { 59 | this.params = params; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /client-mini/src/main/java/com/liubs/shadowrpc/protocol/entity/JavaSerializeRPCRequest.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.protocol.entity; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * @author Liubsyy 7 | * @date 2024/1/20 8 | **/ 9 | public class JavaSerializeRPCRequest implements Serializable { 10 | 11 | 12 | private String traceId; 13 | 14 | private String serviceName; 15 | 16 | private String methodName; 17 | 18 | private Class[] paramTypes; 19 | 20 | private Object[] params; 21 | 22 | public String getTraceId() { 23 | return traceId; 24 | } 25 | 26 | public void setTraceId(String traceId) { 27 | this.traceId = traceId; 28 | } 29 | 30 | public String getServiceName() { 31 | return serviceName; 32 | } 33 | 34 | public void setServiceName(String serviceName) { 35 | this.serviceName = serviceName; 36 | } 37 | 38 | public String getMethodName() { 39 | return methodName; 40 | } 41 | 42 | public void setMethodName(String methodName) { 43 | this.methodName = methodName; 44 | } 45 | 46 | public Class[] getParamTypes() { 47 | return paramTypes; 48 | } 49 | 50 | public void setParamTypes(Class[] paramTypes) { 51 | this.paramTypes = paramTypes; 52 | } 53 | 54 | public Object[] getParams() { 55 | return params; 56 | } 57 | 58 | public void setParams(Object[] params) { 59 | this.params = params; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /client/src/test/java/rpctest/entity/MyMessage.java: -------------------------------------------------------------------------------- 1 | package rpctest.entity; 2 | 3 | import com.liubs.shadowrpc.protocol.annotation.ShadowEntity; 4 | import com.liubs.shadowrpc.protocol.annotation.ShadowField; 5 | 6 | /** 7 | * @author Liubsyy 8 | * @date 2023/12/18 10:58 PM 9 | **/ 10 | @ShadowEntity 11 | public class MyMessage { 12 | 13 | @ShadowField(1) 14 | private String content; 15 | 16 | @ShadowField(2) 17 | private int num; 18 | 19 | /** 20 | * 客户端新增字段 21 | * request: 客户端新增字段,服务端没有这个字段,服务端会跳过这个字段进行反序列化 22 | * response: 服务端没有这个字段,客户端能缺省默认值反序列化 23 | */ 24 | @ShadowField(3) 25 | private String addField = "addField1"; 26 | 27 | public MyMessage() { 28 | } 29 | 30 | 31 | public String getContent() { 32 | return content; 33 | } 34 | 35 | public void setContent(String content) { 36 | this.content = content; 37 | } 38 | 39 | public int getNum() { 40 | return num; 41 | } 42 | 43 | public void setNum(int num) { 44 | this.num = num; 45 | } 46 | 47 | public String getAddField() { 48 | return addField; 49 | } 50 | 51 | public void setAddField(String addField) { 52 | this.addField = addField; 53 | } 54 | 55 | @Override 56 | public String toString() { 57 | return "MyMessage{" + 58 | "content='" + content + '\'' + 59 | ", num=" + num + 60 | ", addField=" + addField + 61 | '}'; 62 | } 63 | } -------------------------------------------------------------------------------- /base/src/main/java/com/liubs/shadowrpc/base/config/ServerConfig.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.base.config; 2 | 3 | import com.liubs.shadowrpc.base.exception.ConfigFieldMissException; 4 | import org.apache.commons.lang3.StringUtils; 5 | 6 | /** 7 | * @author Liubsyy 8 | * @date 2024/1/15 9 | */ 10 | public class ServerConfig extends BaseConfig { 11 | 12 | //服务器集群群组,适用于集群模式 13 | private String group = "DefaultGroup"; 14 | 15 | //开放端口 16 | private int port = 2023; 17 | 18 | //注册中心,如果为空则为单点模式 19 | private String registryUrl; 20 | 21 | //qps统计开关 22 | private boolean qpsStat; 23 | 24 | 25 | public String getGroup() { 26 | return group; 27 | } 28 | 29 | public void setGroup(String group) { 30 | this.group = group; 31 | } 32 | 33 | public boolean isQpsStat() { 34 | return qpsStat; 35 | } 36 | 37 | public void setQpsStat(boolean qpsStat) { 38 | this.qpsStat = qpsStat; 39 | } 40 | 41 | 42 | public int getPort() { 43 | return port; 44 | } 45 | 46 | public void setPort(int port) { 47 | this.port = port; 48 | } 49 | 50 | public String getRegistryUrl() { 51 | return registryUrl; 52 | } 53 | 54 | public void setRegistryUrl(String registryUrl) { 55 | this.registryUrl = registryUrl; 56 | } 57 | 58 | public boolean checkValid(){ 59 | if(StringUtils.isEmpty(group)) { 60 | throw new ConfigFieldMissException("group未配置"); 61 | } 62 | return true; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /server/src/test/java/rpctest/hello/HelloProtoService.java: -------------------------------------------------------------------------------- 1 | package rpctest.hello; 2 | 3 | import com.liubs.shadowrpc.base.config.ServerConfig; 4 | import com.liubs.shadowrpc.base.constant.SerializerEnum; 5 | import com.liubs.shadowrpc.base.annotation.ShadowService; 6 | import com.liubs.shadowrpc.server.init.ServerBuilder; 7 | import org.junit.Test; 8 | import rpctest.entity.MyMessageProto; 9 | 10 | /** 11 | * 一个基于protobuf的helloservice服务 12 | * 13 | * @author Liubsyy 14 | * @date 2023/12/25 12:48 AM 15 | **/ 16 | @ShadowService(serviceName = "helloprotoservice") 17 | public class HelloProtoService implements IHelloProto{ 18 | 19 | @Override 20 | public MyMessageProto.MyMessage say(MyMessageProto.MyMessage message) { 21 | 22 | return MyMessageProto.MyMessage.newBuilder() 23 | .setContent("hello received "+"("+message.getContent()+")") 24 | .setNum(message.getNum()+1) 25 | .build(); 26 | 27 | } 28 | 29 | //启动服务端 30 | @Test 31 | public void helloServiceStart() { 32 | 33 | ServerConfig serverConfig = new ServerConfig(); 34 | serverConfig.setQpsStat(true); //统计qps 35 | serverConfig.setSerializer(SerializerEnum.PROTOBUF.getSerializeType()); 36 | serverConfig.setPort(2024); 37 | 38 | ServerBuilder.newBuilder() 39 | .serverConfig(serverConfig) 40 | .addPackage("rpctest.entity") 41 | .addPackage("rpctest.hello") 42 | .build() 43 | .start().keep(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /server/src/test/java/rpctest/registry/SomeServersProtobuf.java: -------------------------------------------------------------------------------- 1 | package rpctest.registry; 2 | 3 | import com.liubs.shadowrpc.base.config.ServerConfig; 4 | import com.liubs.shadowrpc.base.constant.SerializerEnum; 5 | import com.liubs.shadowrpc.server.init.ServerBuilder; 6 | import org.junit.BeforeClass; 7 | import org.junit.Test; 8 | 9 | /** 10 | * 集群模式-protobuf 11 | * @author Liubsyy 12 | * @date 2024/1/24 13 | **/ 14 | public class SomeServersProtobuf { 15 | public static final String ZK_URL = "192.168.1.5:2181"; 16 | 17 | private static ServerConfig serverConfig = new ServerConfig(); 18 | 19 | @BeforeClass 20 | public static void init(){ 21 | serverConfig.setGroup("MultiNodeGroup"); 22 | serverConfig.setRegistryUrl(ZK_URL); 23 | serverConfig.setQpsStat(true); //统计qps 24 | serverConfig.setSerializer(SerializerEnum.PROTOBUF.name()); 25 | } 26 | 27 | @Test 28 | public void server1(){ 29 | serverConfig.setPort(2023); 30 | ServerBuilder.newBuilder() 31 | .serverConfig(serverConfig) 32 | .addPackage("rpctest.hello") 33 | .build() 34 | .start() 35 | .keep(); 36 | } 37 | 38 | @Test 39 | public void server2(){ 40 | serverConfig.setPort(2024); 41 | ServerBuilder.newBuilder() 42 | .serverConfig(serverConfig) 43 | .addPackage("rpctest.hello") 44 | .build() 45 | .start() 46 | .keep(); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /client-mini/src/main/java/com/liubs/shadowrpc/clientmini/seriallize/JavaSerializer.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.clientmini.seriallize; 2 | 3 | 4 | import java.io.ByteArrayInputStream; 5 | import java.io.ByteArrayOutputStream; 6 | import java.io.ObjectInputStream; 7 | import java.io.ObjectOutputStream; 8 | 9 | /** 10 | * @author Liubsyy 11 | * @date 2024/1/20 12 | **/ 13 | public class JavaSerializer implements ISerializer { 14 | 15 | @Override 16 | public byte[] serialize(Object obj) { 17 | try{ 18 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 19 | ObjectOutputStream oos = new ObjectOutputStream(baos); 20 | oos.writeObject(obj); 21 | oos.close(); 22 | baos.close(); 23 | return baos.toByteArray(); 24 | }catch (Exception e) { 25 | e.printStackTrace(); 26 | } 27 | return null; 28 | } 29 | 30 | @Override 31 | public T deserialize(byte[] array, Class clazz) { 32 | try{ 33 | final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(array); 34 | // 将二进制字节流反序列化为对象 35 | final ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream); 36 | final T object = (T)objectInputStream.readObject(); 37 | // 关闭流 38 | objectInputStream.close(); 39 | byteArrayInputStream.close(); 40 | return object; 41 | }catch (Exception e) { 42 | e.printStackTrace(); 43 | } 44 | return null; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /server/src/main/java/com/liubs/shadowrpc/server/service/ServiceLookUp.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.server.service; 2 | 3 | import java.util.Arrays; 4 | import java.util.Objects; 5 | 6 | /** 7 | * @author Liubsyy 8 | * @date 2023/12/28 9 | **/ 10 | public class ServiceLookUp { 11 | private String serviceName; 12 | private String methodName; 13 | private Object[] paramTypes; 14 | 15 | public String getServiceName() { 16 | return serviceName; 17 | } 18 | 19 | public void setServiceName(String serviceName) { 20 | this.serviceName = serviceName; 21 | } 22 | 23 | public String getMethodName() { 24 | return methodName; 25 | } 26 | 27 | public void setMethodName(String methodName) { 28 | this.methodName = methodName; 29 | } 30 | 31 | public Object[] getParamTypes() { 32 | return paramTypes; 33 | } 34 | 35 | public void setParamTypes(Object[] paramTypes) { 36 | this.paramTypes = paramTypes; 37 | } 38 | 39 | @Override 40 | public boolean equals(Object o) { 41 | if (this == o) return true; 42 | if (o == null || getClass() != o.getClass()) return false; 43 | ServiceLookUp that = (ServiceLookUp) o; 44 | return Objects.equals(serviceName, that.serviceName) && Objects.equals(methodName, that.methodName) && Arrays.equals(paramTypes, that.paramTypes); 45 | } 46 | 47 | @Override 48 | public int hashCode() { 49 | //服务名,方法名,参数长度为一个hash 50 | int result = Objects.hash(serviceName, methodName, null == paramTypes ? 0 : paramTypes.length); 51 | return result; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /client/src/test/java/rpctest/registry/HelloMultiGroups.java: -------------------------------------------------------------------------------- 1 | package rpctest.registry; 2 | 3 | import com.liubs.shadowrpc.base.config.ClientConfig; 4 | import com.liubs.shadowrpc.base.module.ModulePool; 5 | import com.liubs.shadowrpc.client.ClientModule; 6 | import com.liubs.shadowrpc.client.connection.ShadowClientGroup; 7 | import com.liubs.shadowrpc.protocol.serializer.SerializerStrategy; 8 | import org.junit.Before; 9 | import org.junit.Test; 10 | 11 | /** 12 | * 多集群模式测试 13 | * @author Liubsyy 14 | * @date 2024/1/24 15 | **/ 16 | public class HelloMultiGroups { 17 | private static ClientConfig config; 18 | 19 | @Before 20 | public void init(){ 21 | config = new ClientConfig(); 22 | config.setSerializer(SerializerStrategy.KRYO.name()); 23 | ModulePool.getModule(ClientModule.class).init(config); 24 | } 25 | 26 | @Test 27 | public void hello(){ 28 | ShadowClientGroup shadowClientGroup = new ShadowClientGroup("localhost:2181"); 29 | shadowClientGroup.init(); 30 | 31 | IGroupService group1 = shadowClientGroup.createRemoteProxy(IGroupService.class, "shadowrpc://group1/groupService"); 32 | for(int i=0,len=shadowClientGroup.getShadowClients("group1").size(); i { 33 | int qps = perSecondsRequests.getAndSet(0); 34 | if(qps > 0) { 35 | logger.info("Current QPS: " + qps); 36 | } 37 | 38 | // 可以进一步将QPS记录到日志或监控系统 39 | }, 1, 1, TimeUnit.SECONDS); 40 | } 41 | 42 | 43 | @Override 44 | public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { 45 | ctx.write(msg, promise).addListener((f)->{ 46 | if(f.isSuccess()) { 47 | perSecondsRequests.incrementAndGet(); 48 | activeRequests.incrementAndGet(); 49 | } 50 | }); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /server/src/test/java/rpctest/upload/UploadServiceImpl.java: -------------------------------------------------------------------------------- 1 | package rpctest.upload; 2 | 3 | import com.liubs.shadowrpc.base.annotation.ShadowService; 4 | import com.liubs.shadowrpc.base.config.ServerConfig; 5 | import com.liubs.shadowrpc.server.init.ServerBuilder; 6 | import org.junit.Test; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.io.File; 11 | import java.io.IOException; 12 | import java.nio.file.Files; 13 | import java.nio.file.Path; 14 | import java.nio.file.Paths; 15 | import java.nio.file.StandardOpenOption; 16 | 17 | /** 18 | * @author Liubsyy 19 | * @date 2024/1/14 20 | **/ 21 | @ShadowService(serviceName = "uploadService") 22 | public class UploadServiceImpl implements IUploadService{ 23 | 24 | private static final Logger logger = LoggerFactory.getLogger(UploadServiceImpl.class); 25 | 26 | 27 | @Override 28 | public boolean upload(String name, byte[] bytes) { 29 | try { 30 | File uploadDir = new File("target/upload/"); 31 | if(!uploadDir.exists()) { 32 | uploadDir.mkdirs(); 33 | } 34 | Path path = Paths.get("target/upload/"+name); 35 | logger.info("upload file {},size={}",path.toFile().getName(),bytes.length); 36 | Files.write(path,bytes, 37 | StandardOpenOption.TRUNCATE_EXISTING,StandardOpenOption.CREATE,StandardOpenOption.WRITE); 38 | } catch (IOException e) { 39 | e.printStackTrace(); 40 | return false; 41 | } 42 | return true; 43 | } 44 | 45 | @Test 46 | public void startServer(){ 47 | ServerBuilder.newBuilder() 48 | .serverConfig(new ServerConfig()) 49 | .addPackage("rpctest.upload") 50 | .build() 51 | .start() 52 | .keep(); 53 | 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /client/src/test/java/rpctest/registry/HelloClientGroup.java: -------------------------------------------------------------------------------- 1 | package rpctest.registry; 2 | 3 | import com.liubs.shadowrpc.base.config.ClientConfig; 4 | import com.liubs.shadowrpc.base.module.ModulePool; 5 | import com.liubs.shadowrpc.client.ClientModule; 6 | import com.liubs.shadowrpc.client.connection.ShadowClient; 7 | import com.liubs.shadowrpc.client.connection.ShadowClientGroup; 8 | import com.liubs.shadowrpc.client.proxy.RemoteServerProxy; 9 | import com.liubs.shadowrpc.protocol.serializer.SerializerStrategy; 10 | import org.junit.Before; 11 | import org.junit.Test; 12 | import rpctest.hello.IHello; 13 | 14 | import java.util.List; 15 | import java.util.stream.Collectors; 16 | 17 | /** 18 | * @author Liubsyy 19 | * @date 2023/12/18 11:50 PM 20 | **/ 21 | public class HelloClientGroup { 22 | 23 | private static ClientConfig config; 24 | 25 | @Before 26 | public void init(){ 27 | config = new ClientConfig(); 28 | config.setSerializer(SerializerStrategy.KRYO.name()); 29 | ModulePool.getModule(ClientModule.class).init(config); 30 | } 31 | 32 | //接入注册中心,负载均衡调用rpc接口 33 | @Test 34 | public void connectRegistryForServices() { 35 | ShadowClientGroup shadowClientGroup = new ShadowClientGroup("localhost:2181"); 36 | shadowClientGroup.init(); 37 | 38 | IHello helloService = shadowClientGroup.createRemoteProxy(IHello.class, "shadowrpc://LocalGroup/helloservice"); 39 | List shadowClientList = shadowClientGroup.getShadowClients("LocalGroup"); 40 | 41 | System.out.println("所有服务器: "+shadowClientList.stream().map(c-> c.getRemoteIp()+":"+c.getRemotePort()).collect(Collectors.toList())); 42 | 43 | for(int i = 0 ;i T deserialize(byte[] array, Class clazz) { 39 | try{ 40 | final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(array); 41 | // 将二进制字节流反序列化为对象 42 | final ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream); 43 | final T object = (T)objectInputStream.readObject(); 44 | // 关闭流 45 | objectInputStream.close(); 46 | byteArrayInputStream.close(); 47 | return object; 48 | }catch (Exception e) { 49 | logger.error("deserialize err",e); 50 | } 51 | return null; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /client/src/main/java/com/liubs/shadowrpc/client/handler/ClientHandler.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.client.handler; 2 | 3 | import com.liubs.shadowrpc.base.module.ModulePool; 4 | import com.liubs.shadowrpc.protocol.SerializeModule; 5 | import com.liubs.shadowrpc.protocol.model.IModelParser; 6 | import com.liubs.shadowrpc.protocol.model.ResponseModel; 7 | import com.liubs.shadowrpc.protocol.serializer.SerializerManager; 8 | import io.netty.channel.ChannelHandlerContext; 9 | import io.netty.channel.ChannelInboundHandlerAdapter; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | /** 14 | * @author Liubsyy 15 | * @date 2023/12/3 10:29 PM 16 | **/ 17 | 18 | 19 | public class ClientHandler extends ChannelInboundHandlerAdapter{ 20 | private static final Logger logger = LoggerFactory.getLogger(ClientHandler.class); 21 | 22 | private SerializeModule serializeModule = ModulePool.getModule(SerializeModule.class); 23 | 24 | @Override 25 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 26 | logger.info("已连接远程{}",ctx.channel().remoteAddress()); 27 | super.channelActive(ctx); 28 | } 29 | 30 | @Override 31 | public void channelInactive(ChannelHandlerContext ctx) throws Exception { 32 | logger.info("远程{} 已断开",ctx.channel().remoteAddress()); 33 | super.channelInactive(ctx); 34 | } 35 | 36 | @Override 37 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 38 | IModelParser modelParser = serializeModule.getSerializer().getModelParser(); 39 | ResponseModel responseModel = modelParser.fromResponse(msg); 40 | ReceiveHolder.getInstance().receiveData(responseModel); 41 | } 42 | 43 | 44 | @Override 45 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 46 | // 在发生异常时执行 47 | logger.error("exceptionCaught err",cause); 48 | ctx.close(); 49 | } 50 | 51 | 52 | 53 | } 54 | 55 | -------------------------------------------------------------------------------- /protocol/src/test/java/serializetest/entity/Person.java: -------------------------------------------------------------------------------- 1 | package serializetest.entity; 2 | 3 | import com.liubs.shadowrpc.protocol.annotation.ShadowEntity; 4 | import com.liubs.shadowrpc.protocol.annotation.ShadowField; 5 | 6 | import java.util.Objects; 7 | 8 | 9 | /** 10 | * @author Liubsyy 11 | * @date 2023/12/23 2:07 PM 12 | */ 13 | @ShadowEntity 14 | public class Person { 15 | @ShadowField(1) 16 | private int age; 17 | @ShadowField(2) 18 | private double height; 19 | @ShadowField(3) 20 | private float weight; 21 | @ShadowField(4) 22 | private long money; 23 | @ShadowField(5) 24 | private String name; 25 | 26 | 27 | public int getAge() { 28 | return age; 29 | } 30 | 31 | public void setAge(int age) { 32 | this.age = age; 33 | } 34 | 35 | public double getHeight() { 36 | return height; 37 | } 38 | 39 | public void setHeight(double height) { 40 | this.height = height; 41 | } 42 | 43 | public float getWeight() { 44 | return weight; 45 | } 46 | 47 | public void setWeight(float weight) { 48 | this.weight = weight; 49 | } 50 | 51 | public long getMoney() { 52 | return money; 53 | } 54 | 55 | public void setMoney(long money) { 56 | this.money = money; 57 | } 58 | 59 | public String getName() { 60 | return name; 61 | } 62 | 63 | public void setName(String name) { 64 | this.name = name; 65 | } 66 | 67 | 68 | @Override 69 | public boolean equals(Object o) { 70 | if (this == o) return true; 71 | if (o == null || getClass() != o.getClass()) return false; 72 | Person person = (Person) o; 73 | return age == person.age && Double.compare(person.height, height) == 0 && Float.compare(person.weight, weight) == 0 && money == person.money && Objects.equals(name, person.name); 74 | } 75 | 76 | @Override 77 | public int hashCode() { 78 | return Objects.hash(age, height, weight, money, name); 79 | } 80 | 81 | 82 | 83 | } 84 | -------------------------------------------------------------------------------- /server/src/test/java/rpctest/registry/SomeServers.java: -------------------------------------------------------------------------------- 1 | package rpctest.registry; 2 | 3 | import com.liubs.shadowrpc.base.config.ServerConfig; 4 | import com.liubs.shadowrpc.base.constant.SerializerEnum; 5 | import com.liubs.shadowrpc.protocol.serializer.SerializerStrategy; 6 | import com.liubs.shadowrpc.protocol.serializer.SerializerManager; 7 | import com.liubs.shadowrpc.server.init.ServerBuilder; 8 | import com.liubs.shadowrpc.server.service.ServerManager; 9 | import org.junit.BeforeClass; 10 | import org.junit.Test; 11 | 12 | /** 13 | * 注册中心和负载均衡测试 14 | * @author Liubsyy 15 | * @date 2023/12/18 11:48 PM 16 | **/ 17 | public class SomeServers { 18 | 19 | public static final String ZK_URL = "localhost:2181"; 20 | 21 | private static ServerConfig serverConfig = new ServerConfig(); 22 | 23 | @BeforeClass 24 | public static void init(){ 25 | serverConfig.setGroup("LocalGroup"); 26 | serverConfig.setRegistryUrl(ZK_URL); 27 | serverConfig.setQpsStat(true); //统计qps 28 | serverConfig.setSerializer(SerializerEnum.KRYO.name()); 29 | 30 | } 31 | 32 | @Test 33 | public void server1(){ 34 | serverConfig.setPort(2023); 35 | ServerBuilder.newBuilder() 36 | .serverConfig(serverConfig) 37 | .addPackage("rpctest.hello") 38 | .build() 39 | .start() 40 | .keep(); 41 | } 42 | 43 | @Test 44 | public void server2(){ 45 | serverConfig.setPort(2024); 46 | ServerBuilder.newBuilder() 47 | .serverConfig(serverConfig) 48 | .addPackage("rpctest.hello") 49 | .build() 50 | .start() 51 | .keep(); 52 | } 53 | 54 | @Test 55 | public void server3(){ 56 | serverConfig.setPort(2025); 57 | ServerBuilder.newBuilder() 58 | .serverConfig(serverConfig) 59 | .addPackage("rpctest.hello") 60 | .build() 61 | .start() 62 | .keep(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /server/src/test/java/rpctest/hello/HelloService.java: -------------------------------------------------------------------------------- 1 | package rpctest.hello; 2 | 3 | import com.liubs.shadowrpc.base.annotation.ShadowService; 4 | import com.liubs.shadowrpc.base.config.ServerConfig; 5 | import com.liubs.shadowrpc.base.constant.SerializerEnum; 6 | import com.liubs.shadowrpc.server.init.ServerBuilder; 7 | import org.junit.Test; 8 | import rpctest.entity.MyMessage; 9 | 10 | /** 11 | * 一个helloservice服务 12 | * @author Liubsyy 13 | * @date 2023/12/18 11:01 PM 14 | **/ 15 | @ShadowService(serviceName = "helloservice") 16 | public class HelloService implements IHello { 17 | 18 | 19 | @Override 20 | public String hello(String msg) { 21 | return "Hello,"+msg; 22 | } 23 | 24 | @Override 25 | public MyMessage say(MyMessage message) { 26 | MyMessage message1 = new MyMessage(); 27 | message1.setContent("hello received "+"("+message.getContent()+")"); 28 | message1.setNum(message.getNum()+1); 29 | return message1; 30 | } 31 | 32 | 33 | //java原生序列化服务器启动服务端 34 | @Test 35 | public void helloServiceStartForJavaSerialize() { 36 | 37 | ServerConfig serverConfig = new ServerConfig(); 38 | serverConfig.setQpsStat(true); //统计qps 39 | serverConfig.setSerializer(SerializerEnum.JAVA_SERIALISE.getSerializeType()); 40 | serverConfig.setPort(2023); 41 | 42 | ServerBuilder.newBuilder() 43 | .serverConfig(serverConfig) 44 | .addPackage("rpctest.hello") 45 | .build() 46 | .start() 47 | .keep(); 48 | 49 | } 50 | 51 | //启动服务端 52 | @Test 53 | public void helloServiceStart() { 54 | 55 | ServerConfig serverConfig = new ServerConfig(); 56 | serverConfig.setQpsStat(true); //统计qps 57 | serverConfig.setPort(2023); 58 | 59 | ServerBuilder.newBuilder() 60 | .serverConfig(serverConfig) 61 | .addPackage("rpctest.hello") 62 | .build() 63 | .start() 64 | .keep(); 65 | 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /server/src/main/java/com/liubs/shadowrpc/server/handler/MessageHandler.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.server.handler; 2 | 3 | import com.liubs.shadowrpc.base.module.ModulePool; 4 | import com.liubs.shadowrpc.protocol.SerializeModule; 5 | import com.liubs.shadowrpc.protocol.entity.HeartBeatMessage; 6 | import com.liubs.shadowrpc.protocol.serializer.SerializerManager; 7 | import io.netty.buffer.ByteBuf; 8 | import io.netty.channel.ChannelHandlerContext; 9 | import io.netty.handler.codec.ByteToMessageCodec; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | import java.util.List; 14 | 15 | /** 16 | * 消息的压缩和解压缩 17 | * @author Liubsyy 18 | * @date 2023/12/15 10:01 PM 19 | **/ 20 | public class MessageHandler extends ByteToMessageCodec { 21 | private static final Logger logger = LoggerFactory.getLogger(MessageHandler.class); 22 | 23 | private SerializeModule serializeModule = ModulePool.getModule(SerializeModule.class); 24 | 25 | @Override 26 | protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception { 27 | byte[] data; 28 | if(msg instanceof HeartBeatMessage) { 29 | //心跳消息 30 | data = HeartBeatMessage.getHearBeatMsg(); 31 | }else { 32 | data = serializeModule.serialize(msg); 33 | } 34 | 35 | int dataLength = data.length; 36 | out.writeInt(dataLength); // 先写入消息长度 37 | out.writeBytes(data); // 写入序列化后的数据 38 | } 39 | 40 | @Override 41 | protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { 42 | int readableBytes = in.readableBytes(); 43 | if(readableBytes <= 0){ 44 | return; 45 | } 46 | byte[] data = new byte[readableBytes]; 47 | 48 | in.readBytes(data); 49 | 50 | if(HeartBeatMessage.isHeartBeatMsg(data)) { 51 | logger.info("收到心跳消息..."); 52 | }else { 53 | Object obj = serializeModule.deserializeRequest(data); 54 | out.add(obj); 55 | } 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /client/src/main/java/com/liubs/shadowrpc/client/handler/MessageHandler.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.client.handler; 2 | 3 | import com.liubs.shadowrpc.base.module.ModulePool; 4 | import com.liubs.shadowrpc.protocol.SerializeModule; 5 | import com.liubs.shadowrpc.protocol.entity.HeartBeatMessage; 6 | import com.liubs.shadowrpc.protocol.serializer.SerializerManager; 7 | import io.netty.buffer.ByteBuf; 8 | import io.netty.channel.ChannelHandlerContext; 9 | import io.netty.handler.codec.ByteToMessageCodec; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | import java.util.List; 14 | 15 | /** 16 | * 消息的压缩和解压缩 17 | * @author Liubsyy 18 | * @date 2023/12/15 10:01 PM 19 | **/ 20 | public class MessageHandler extends ByteToMessageCodec { 21 | 22 | private static final Logger logger = LoggerFactory.getLogger(MessageHandler.class); 23 | 24 | private SerializeModule serializeModule = ModulePool.getModule(SerializeModule.class); 25 | 26 | @Override 27 | protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception { 28 | byte[] data; 29 | if(msg instanceof HeartBeatMessage) { 30 | //心跳消息 31 | data = HeartBeatMessage.getHearBeatMsg(); 32 | }else { 33 | data = serializeModule.serialize(msg); 34 | } 35 | 36 | int dataLength = data.length; 37 | out.writeInt(dataLength); // 先写入消息长度 38 | out.writeBytes(data); // 写入序列化后的数据 39 | } 40 | 41 | @Override 42 | protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { 43 | int readableBytes = in.readableBytes(); 44 | if(readableBytes <= 0){ 45 | return; 46 | } 47 | byte[] data = new byte[readableBytes]; 48 | 49 | in.readBytes(data); 50 | 51 | if(HeartBeatMessage.isHeartBeatMsg(data)) { 52 | logger.info("收到心跳消息..."); 53 | }else { 54 | Object obj = serializeModule.deserializeResponse(data); 55 | out.add(obj); 56 | } 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /protocol/src/main/java/com/liubs/shadowrpc/protocol/serializer/kryo/KryoSerializer.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.protocol.serializer.kryo; 2 | 3 | import com.esotericsoftware.kryo.Kryo; 4 | import com.esotericsoftware.kryo.io.Input; 5 | import com.esotericsoftware.kryo.io.Output; 6 | import com.liubs.shadowrpc.protocol.entity.ShadowRPCRequest; 7 | import com.liubs.shadowrpc.protocol.entity.ShadowRPCResponse; 8 | import com.liubs.shadowrpc.protocol.serializer.ISerializer; 9 | 10 | import java.io.ByteArrayInputStream; 11 | import java.io.ByteArrayOutputStream; 12 | 13 | /** 14 | * @author Liubsyy 15 | * @date 2023/12/18 10:43 PM 16 | **/ 17 | public class KryoSerializer implements ISerializer { 18 | 19 | private static ThreadLocal kryoThreadLocal = ThreadLocal.withInitial(() -> { 20 | Kryo kryo = new Kryo(); 21 | 22 | kryo.setDefaultSerializer(new KryoFieldSerializerFactory()); 23 | 24 | kryo.setReferences(false); 25 | kryo.setRegistrationRequired(false); //不需要提前注册 26 | 27 | //注册一定会用到的,序列化可以省点空间 28 | kryo.register(Class.class); 29 | kryo.register(Class[].class); 30 | kryo.register(Object[].class); 31 | kryo.register(ShadowRPCRequest.class); 32 | kryo.register(ShadowRPCResponse.class); 33 | 34 | return kryo; 35 | }); 36 | 37 | 38 | 39 | @Override 40 | public byte[] serialize(Object object) { 41 | 42 | ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 43 | Output output = new Output(byteArrayOutputStream); 44 | kryoThreadLocal.get().writeObject(output, object); 45 | output.close(); 46 | return byteArrayOutputStream.toByteArray(); 47 | } 48 | 49 | @Override 50 | public T deserialize(byte[] array, Class clazz) { 51 | 52 | ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(array); 53 | Input input = new Input(byteArrayInputStream); 54 | T object = kryoThreadLocal.get().readObject(input, clazz); 55 | input.close(); 56 | return object; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /client-mini/src/main/java/com/liubs/shadowrpc/clientmini/nio/NIOConfig.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.clientmini.nio; 2 | 3 | import com.liubs.shadowrpc.clientmini.seriallize.ISerializer; 4 | import com.liubs.shadowrpc.clientmini.seriallize.JavaSerializer; 5 | 6 | /** 7 | * @author Liubsyy 8 | * @date 2024/1/21 9 | **/ 10 | public class NIOConfig { 11 | 12 | //序列化 & 反序列化方式 13 | private ISerializer serializer = new JavaSerializer(); 14 | 15 | //连接超时 16 | private long connectTimeout = 3000; 17 | 18 | //写入channel超时 19 | private long writeChannelTimeout = 1000; 20 | 21 | //同步调用超时 22 | private long waitTimeout = 3000; 23 | 24 | //心跳 25 | private boolean isHearBeat; 26 | 27 | //心跳间隔 28 | private long heartBeatWaitSeconds = 8000; 29 | 30 | public long getConnectTimeout() { 31 | return connectTimeout; 32 | } 33 | 34 | public void setConnectTimeout(long connectTimeout) { 35 | this.connectTimeout = connectTimeout; 36 | } 37 | 38 | public long getWaitTimeout() { 39 | return waitTimeout; 40 | } 41 | 42 | public void setWaitTimeout(long waitTimeout) { 43 | this.waitTimeout = waitTimeout; 44 | } 45 | 46 | public ISerializer getSerializer() { 47 | return serializer; 48 | } 49 | 50 | public void setSerializer(ISerializer serializer) { 51 | this.serializer = serializer; 52 | } 53 | 54 | public long getWriteChannelTimeout() { 55 | return writeChannelTimeout; 56 | } 57 | 58 | public void setWriteChannelTimeout(long writeChannelTimeout) { 59 | this.writeChannelTimeout = writeChannelTimeout; 60 | } 61 | 62 | public boolean isHearBeat() { 63 | return isHearBeat; 64 | } 65 | 66 | public void setHearBeat(boolean hearBeat) { 67 | isHearBeat = hearBeat; 68 | } 69 | 70 | public long getHeartBeatWaitSeconds() { 71 | return heartBeatWaitSeconds; 72 | } 73 | 74 | public void setHeartBeatWaitSeconds(long heartBeatWaitSeconds) { 75 | this.heartBeatWaitSeconds = heartBeatWaitSeconds; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /protocol/src/main/java/com/liubs/shadowrpc/protocol/serializer/SerializerManager.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.protocol.serializer; 2 | 3 | 4 | import com.google.protobuf.MessageLite; 5 | import com.liubs.shadowrpc.base.config.BaseConfig; 6 | import com.liubs.shadowrpc.protocol.serializer.protobuf.ParserForType; 7 | import com.liubs.shadowrpc.base.util.ClassScanWalker; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import java.io.IOException; 12 | import java.lang.reflect.InvocationTargetException; 13 | 14 | /** 15 | * @author Liubsyy 16 | * @date 2023/12/23 5:01 PM 17 | **/ 18 | public class SerializerManager { 19 | private static final Logger logger = LoggerFactory.getLogger(SerializerManager.class); 20 | 21 | private static SerializerManager instance = new SerializerManager(); 22 | 23 | private BaseConfig config; 24 | private SerializerStrategy serializer = SerializerStrategy.KRYO; 25 | 26 | public static SerializerManager getInstance() { 27 | return instance; 28 | } 29 | 30 | //初始化序列化模块 31 | public void init(String ...packageNames) { 32 | for(String packageName : packageNames) { 33 | try { 34 | ClassScanWalker.scanPackage(packageName,(classz)->{ 35 | if(MessageLite.class.isAssignableFrom(classz)) { 36 | try { 37 | MessageLite messageLite = (MessageLite)classz.getDeclaredMethod("getDefaultInstance").invoke(null); 38 | ParserForType.addMessage(classz.getName(),messageLite); 39 | 40 | } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { 41 | e.printStackTrace(); 42 | } 43 | } 44 | }); 45 | } catch (IOException e) { 46 | logger.error("序列化包初始化失败",e); 47 | } 48 | } 49 | } 50 | 51 | 52 | public void setSerializer(SerializerStrategy serializer) { 53 | this.serializer = serializer; 54 | } 55 | 56 | 57 | 58 | 59 | } 60 | -------------------------------------------------------------------------------- /protocol/src/main/java/com/liubs/shadowrpc/protocol/entity/ShadowRPCRequest.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.protocol.entity; 2 | 3 | import com.liubs.shadowrpc.protocol.annotation.ShadowEntity; 4 | import com.liubs.shadowrpc.protocol.annotation.ShadowField; 5 | 6 | import java.util.Arrays; 7 | 8 | /** 9 | * @author Liubsyy 10 | * @date 2023/12/18 10:38 PM 11 | **/ 12 | @ShadowEntity 13 | public class ShadowRPCRequest { 14 | 15 | @ShadowField(1) 16 | private String traceId; 17 | 18 | @ShadowField(2) 19 | private String serviceName; 20 | 21 | @ShadowField(3) 22 | private String methodName; 23 | 24 | @ShadowField(4) 25 | private Class[] paramTypes; 26 | 27 | @ShadowField(5) 28 | private Object[] params; 29 | 30 | public String getTraceId() { 31 | return traceId; 32 | } 33 | 34 | public void setTraceId(String traceId) { 35 | this.traceId = traceId; 36 | } 37 | 38 | public String getServiceName() { 39 | return serviceName; 40 | } 41 | 42 | public void setServiceName(String serviceName) { 43 | this.serviceName = serviceName; 44 | } 45 | 46 | public String getMethodName() { 47 | return methodName; 48 | } 49 | 50 | public void setMethodName(String methodName) { 51 | this.methodName = methodName; 52 | } 53 | 54 | public Class[] getParamTypes() { 55 | return paramTypes; 56 | } 57 | 58 | public void setParamTypes(Class[] paramTypes) { 59 | this.paramTypes = paramTypes; 60 | } 61 | 62 | public Object[] getParams() { 63 | return params; 64 | } 65 | 66 | public void setParams(Object[] params) { 67 | this.params = params; 68 | } 69 | 70 | @Override 71 | public String toString() { 72 | return "ShadowRPCRequest{" + 73 | "traceId='" + traceId + '\'' + 74 | ", serviceName='" + serviceName + '\'' + 75 | ", methodName='" + methodName + '\'' + 76 | ", paramTypes=" + Arrays.toString(paramTypes) + 77 | ", params=" + Arrays.toString(params) + 78 | '}'; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /base/src/main/java/com/liubs/shadowrpc/base/module/ModulePool.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.base.module; 2 | 3 | import com.liubs.shadowrpc.base.annotation.ModuleInject; 4 | import com.liubs.shadowrpc.base.annotation.ShadowModule; 5 | import com.liubs.shadowrpc.base.util.PackageScanUtil; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import java.lang.reflect.Field; 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | import java.util.Set; 13 | 14 | /** 15 | * @author Liubsyy 16 | * @date 2024/1/15 17 | */ 18 | public class ModulePool { 19 | private static final Logger logger = LoggerFactory.getLogger(ModulePool.class); 20 | 21 | private static Map,IModule> moduleMap = new HashMap<>(); 22 | 23 | static { 24 | iniModules(); 25 | } 26 | 27 | public static void iniModules(){ 28 | Set> classes = PackageScanUtil.scanClasses("com.liubs.shadowrpc", ShadowModule.class); 29 | classes.forEach(c->{ 30 | try { 31 | IModule o = (IModule)c.newInstance(); 32 | moduleMap.put(c,o); 33 | } catch (Exception e) { 34 | logger.error(String.format("create module %s fail",c),e); 35 | throw new RuntimeException(e); 36 | } 37 | }); 38 | 39 | for(Map.Entry, IModule> entry : moduleMap.entrySet()) { 40 | Class key = entry.getKey(); 41 | IModule value = entry.getValue(); 42 | 43 | for(Field field : key.getDeclaredFields()){ 44 | if(null == field.getAnnotation(ModuleInject.class)) { 45 | continue; 46 | } 47 | boolean accessible = field.isAccessible(); 48 | try{ 49 | field.setAccessible(true); 50 | field.set(value,moduleMap.get(field.getType())); 51 | } catch (IllegalAccessException e) { 52 | logger.error(String.format("module value {} set failed %s fail",field.getType()),e); 53 | } finally { 54 | field.setAccessible(accessible); 55 | } 56 | } 57 | } 58 | 59 | } 60 | 61 | public static T getModule(Class moduleClass) { 62 | return (T)moduleMap.get(moduleClass); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | io.github.liubsyy 8 | shadowrpc 9 | 1.0 10 | ../pom.xml 11 | 12 | 13 | shadowrpc-server 14 | 15 | 16 | 17 | 18 | 19 | io.github.liubsyy 20 | shadowrpc-base 21 | 1.0.0 22 | compile 23 | 24 | 25 | 26 | io.github.liubsyy 27 | shadowrpc-protocol 28 | 1.0.0 29 | compile 30 | 31 | 32 | io.github.liubsyy 33 | shadowrpc-registry 34 | 1.0.0 35 | compile 36 | 37 | 38 | 39 | 40 | io.netty 41 | netty-all 42 | ${netty.version} 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /client/src/main/java/com/liubs/shadowrpc/client/proxy/RemoteServerProxy.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.client.proxy; 2 | 3 | import com.liubs.shadowrpc.base.annotation.ShadowInterface; 4 | import com.liubs.shadowrpc.client.connection.IConnection; 5 | 6 | import java.lang.reflect.Proxy; 7 | import java.util.Map; 8 | import java.util.concurrent.ConcurrentHashMap; 9 | 10 | /** 11 | * @author Liubsyy 12 | * @date 2023/12/3 11:29 PM 13 | **/ 14 | public class RemoteServerProxy { 15 | 16 | /** 17 | * service(集群+服务) => 代理对象 18 | */ 19 | private static Map proxyServiceCache = new ConcurrentHashMap<>(); 20 | 21 | public static boolean hasLoaded(String service) { 22 | return proxyServiceCache.containsKey(service); 23 | } 24 | 25 | /** 26 | * 27 | * @param connection 28 | * @param serviceStub 29 | * @param service shadowrpc://group/serviceName 30 | * @return 31 | * @param 32 | */ 33 | public static T create(IConnection connection, Class serviceStub, final String service) { 34 | 35 | Object proxyInstance = proxyServiceCache.get(service); 36 | if(null == proxyInstance) { 37 | synchronized (serviceStub) { 38 | proxyInstance = proxyServiceCache.get(service); 39 | if(null == proxyInstance) { 40 | ShadowInterface shadowInterface = serviceStub.getAnnotation(ShadowInterface.class); 41 | if(null == shadowInterface) { 42 | throw new RuntimeException("服务未找到 @shadowInterface注解"); 43 | } 44 | 45 | String[] serviceArr = service.replace("shadowrpc://","").split("/"); 46 | if(serviceArr.length < 2) { 47 | throw new IllegalArgumentException("service参数不符合规范"); 48 | } 49 | String group = serviceArr[0]; 50 | String serviceName = serviceArr[1]; 51 | 52 | proxyInstance = Proxy.newProxyInstance( 53 | serviceStub.getClassLoader(), 54 | new Class[]{serviceStub}, 55 | new RemoteHandler(connection,serviceStub,group,serviceName) 56 | ); 57 | } 58 | } 59 | } 60 | return (T)proxyInstance; 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /protocol/src/main/java/com/liubs/shadowrpc/protocol/serializer/protobuf/ParserForType.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.protocol.serializer.protobuf; 2 | 3 | import com.google.protobuf.ExtensionRegistryLite; 4 | import com.google.protobuf.InvalidProtocolBufferException; 5 | import com.google.protobuf.MessageLite; 6 | 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | /** 11 | * @author Liubsyy 12 | * @date 2023/12/24 1:05 AM 13 | **/ 14 | public class ParserForType { 15 | 16 | //所有protobuf的类型,ClassName=>MessageLite对象 17 | private static final Map allProtobufMessages = new HashMap<>(); 18 | 19 | private static final boolean HAS_PARSER; 20 | 21 | static { 22 | boolean hasParser = false; 23 | try { 24 | // MessageLite.getParserForType() is not available until protobuf 2.5.0. 25 | MessageLite.class.getDeclaredMethod("getParserForType"); 26 | hasParser = true; 27 | } catch (Throwable t) { 28 | // Ignore 29 | } 30 | 31 | HAS_PARSER = hasParser; 32 | } 33 | 34 | public static void addMessage(String name,MessageLite messageLite) { 35 | allProtobufMessages.put(name,messageLite); 36 | } 37 | public static MessageLite getMessage(String name) { 38 | return allProtobufMessages.get(name); 39 | } 40 | 41 | 42 | public static T parseFrom(MessageLite prototype,byte[] array) throws InvalidProtocolBufferException { 43 | return parseFrom(prototype,null,array); 44 | } 45 | 46 | public static T parseFrom(MessageLite prototype, ExtensionRegistryLite extensionRegistry, byte[] array) throws InvalidProtocolBufferException { 47 | if (extensionRegistry == null) { 48 | if (HAS_PARSER) { 49 | return (T)prototype.getParserForType().parseFrom(array, 0, array.length); 50 | } else { 51 | return (T)prototype.newBuilderForType().mergeFrom(array, 0, array.length).build(); 52 | } 53 | } else { 54 | if (HAS_PARSER) { 55 | return (T)prototype.getParserForType().parseFrom( 56 | array, 0, array.length, extensionRegistry); 57 | } else { 58 | return (T)prototype.newBuilderForType().mergeFrom( 59 | array, 0, array.length, extensionRegistry).build(); 60 | } 61 | } 62 | 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /protocol/src/main/java/com/liubs/shadowrpc/protocol/serializer/kryo/KryoModelParser.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.protocol.serializer.kryo; 2 | 3 | import com.liubs.shadowrpc.protocol.entity.ShadowRPCRequest; 4 | import com.liubs.shadowrpc.protocol.entity.ShadowRPCResponse; 5 | import com.liubs.shadowrpc.protocol.model.RequestModel; 6 | import com.liubs.shadowrpc.protocol.model.ResponseModel; 7 | import com.liubs.shadowrpc.protocol.model.IModelParser; 8 | 9 | /** 10 | * @author Liubsyy 11 | * @date 2023/12/25 12 | */ 13 | public class KryoModelParser implements IModelParser { 14 | 15 | @Override 16 | public RequestModel fromRequest(ShadowRPCRequest request) { 17 | RequestModel requestModel = new RequestModel(); 18 | requestModel.setTraceId(request.getTraceId()); 19 | requestModel.setServiceName(request.getServiceName()); 20 | requestModel.setMethodName(request.getMethodName()); 21 | requestModel.setParamTypes(request.getParamTypes()); 22 | requestModel.setParams(request.getParams()); 23 | return requestModel; 24 | } 25 | 26 | @Override 27 | public ShadowRPCRequest toRequest(RequestModel requestModel) { 28 | ShadowRPCRequest request = new ShadowRPCRequest(); 29 | request.setTraceId(requestModel.getTraceId()); 30 | request.setServiceName(requestModel.getServiceName()); 31 | request.setMethodName(requestModel.getMethodName()); 32 | request.setParamTypes(requestModel.getParamTypes()); 33 | request.setParams(requestModel.getParams()); 34 | return request; 35 | } 36 | 37 | @Override 38 | public ResponseModel fromResponse(ShadowRPCResponse response) { 39 | ResponseModel responseModel = new ResponseModel(); 40 | responseModel.setTraceId(response.getTraceId()); 41 | responseModel.setCode(response.getCode()); 42 | responseModel.setResult(response.getResult()); 43 | responseModel.setErrorMsg(response.getErrorMsg()); 44 | return responseModel; 45 | } 46 | 47 | @Override 48 | public ShadowRPCResponse toResponse(ResponseModel responseModel) { 49 | ShadowRPCResponse response = new ShadowRPCResponse(); 50 | response.setTraceId(responseModel.getTraceId()); 51 | response.setCode(responseModel.getCode()); 52 | response.setResult(responseModel.getResult()); 53 | response.setErrorMsg(responseModel.getErrorMsg()); 54 | return response; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /client-mini/src/main/java/com/liubs/shadowrpc/clientmini/logger/LoggerHandler.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.clientmini.logger; 2 | 3 | import java.io.PrintStream; 4 | import java.io.PrintWriter; 5 | import java.io.StringWriter; 6 | import java.text.SimpleDateFormat; 7 | import java.util.Date; 8 | 9 | 10 | 11 | /** 12 | * @author Liubsyy 13 | * @date 2024/1/20 14 | **/ 15 | public class LoggerHandler { 16 | 17 | // stream to receive the log 18 | PrintStream outputStream; 19 | 20 | SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS"); 21 | 22 | /** 23 | * Setup custom stream (default is System.out). 24 | * 25 | * @param outputStream custom stream 26 | */ 27 | public void setPrintStream(PrintStream outputStream) { 28 | this.outputStream = outputStream; 29 | } 30 | 31 | // print a message to System.out and optionally to custom stream 32 | protected void printMessage(String message) { 33 | String log = "shadowrpc: " + sdf.format(new Date()) + " " + message; 34 | System.out.println(log); 35 | if (outputStream != null) 36 | outputStream.println(log); 37 | } 38 | 39 | public void print(Class clazz, Logger.Level level, String message, Throwable throwable, Object... args) { 40 | 41 | // replace {} in string with actual parameters 42 | String messageWithArgs = message; 43 | for (Object arg : args) { 44 | int index = messageWithArgs.indexOf("{}"); 45 | if (index >= 0) { 46 | messageWithArgs = messageWithArgs.substring(0, index) + String.valueOf(arg) + messageWithArgs.substring(index + 2); 47 | } 48 | } 49 | 50 | StringBuffer stringBuffer = new StringBuffer(); 51 | stringBuffer.append(level); 52 | stringBuffer.append(" ("); 53 | stringBuffer.append(clazz.getName()); 54 | stringBuffer.append(") - "); 55 | stringBuffer.append(messageWithArgs); 56 | 57 | if (throwable != null) { 58 | stringBuffer.append("\n"); 59 | stringBuffer.append(formatErrorTrace(throwable)); 60 | } 61 | 62 | printMessage(stringBuffer.toString()); 63 | } 64 | 65 | private String formatErrorTrace(Throwable throwable) { 66 | StringWriter errors = new StringWriter(); 67 | throwable.printStackTrace(new PrintWriter(errors)); 68 | return errors.toString(); 69 | } 70 | 71 | public void setDateTimeFormat(String dateTimeFormat) { 72 | sdf = new SimpleDateFormat(dateTimeFormat); 73 | } 74 | } -------------------------------------------------------------------------------- /protocol/src/main/java/com/liubs/shadowrpc/protocol/serializer/kryo/KryoFieldSerializer.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.protocol.serializer.kryo; 2 | 3 | import com.esotericsoftware.kryo.Kryo; 4 | import com.esotericsoftware.kryo.KryoException; 5 | import com.esotericsoftware.kryo.serializers.TaggedFieldSerializer; 6 | import com.esotericsoftware.kryo.util.IntMap; 7 | import com.liubs.shadowrpc.protocol.annotation.ShadowField; 8 | 9 | import java.lang.reflect.Field; 10 | import java.util.ArrayList; 11 | 12 | import static com.esotericsoftware.minlog.Log.TRACE; 13 | import static com.esotericsoftware.minlog.Log.trace; 14 | 15 | /** 16 | * 基于TaggedFieldSerializer做序列化 17 | * 使用自定义注解 @ShadowField 标记序列化的字段,能解决增加字段或者缺失字段另一端反序列化的问题 18 | * 同时性能也比CompatibleFieldSerializer高 19 | * @author Liubsyy 20 | * @date 2023/12/18 10:42 PM 21 | **/ 22 | public class KryoFieldSerializer extends TaggedFieldSerializer { 23 | 24 | public KryoFieldSerializer(Kryo kryo, Class type, TaggedFieldSerializerConfig config) { 25 | super(kryo, type, config); 26 | } 27 | 28 | @Override 29 | protected void initializeCachedFields () { 30 | CachedField[] fields = super.getFields(); 31 | // Remove untagged fields. 32 | for (int i = 0, n = fields.length; i < n; i++) { 33 | Field field = fields[i].getField(); 34 | if (field.getAnnotation(ShadowField.class) == null) { 35 | if (TRACE) trace("kryo", "Ignoring field without tag: " + fields[i]); 36 | super.removeField(fields[i]); 37 | } 38 | } 39 | fields = super.getFields(); // removeField changes cached field array. 40 | 41 | // Cache tag values. 42 | ArrayList writeTags = new ArrayList(fields.length); 43 | IntMap readTags = new IntMap((int)(fields.length / 0.8f)); 44 | super.setReadTags(readTags); 45 | for (CachedField cachedField : fields) { 46 | Field field = CachedFieldAccess.getField(cachedField); 47 | int tag = field.getAnnotation(ShadowField.class).value(); 48 | if (readTags.containsKey(tag)) 49 | throw new KryoException(String.format("Duplicate tag %d on fields: %s and %s", tag, field, writeTags.get(tag))); 50 | readTags.put(tag, cachedField); 51 | if (field.getAnnotation(Deprecated.class) == null) writeTags.add(cachedField); 52 | CachedFieldAccess.setTag(cachedField,tag); 53 | } 54 | super.setWriteTags((CachedField[])writeTags.toArray(new CachedField[writeTags.size()])); 55 | } 56 | 57 | } -------------------------------------------------------------------------------- /protocol/src/main/java/com/liubs/shadowrpc/protocol/serializer/javaserializer/JavaModelParser.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.protocol.serializer.javaserializer; 2 | 3 | import com.liubs.shadowrpc.protocol.entity.JavaSerializeRPCRequest; 4 | import com.liubs.shadowrpc.protocol.entity.JavaSerializeRPCResponse; 5 | import com.liubs.shadowrpc.protocol.model.IModelParser; 6 | import com.liubs.shadowrpc.protocol.model.RequestModel; 7 | import com.liubs.shadowrpc.protocol.model.ResponseModel; 8 | 9 | /** 10 | * @author Liubsyy 11 | * @date 2024/1/20 12 | **/ 13 | public class JavaModelParser implements IModelParser { 14 | 15 | @Override 16 | public RequestModel fromRequest(JavaSerializeRPCRequest request) { 17 | RequestModel requestModel = new RequestModel(); 18 | requestModel.setTraceId(request.getTraceId()); 19 | requestModel.setServiceName(request.getServiceName()); 20 | requestModel.setMethodName(request.getMethodName()); 21 | requestModel.setParamTypes(request.getParamTypes()); 22 | requestModel.setParams(request.getParams()); 23 | return requestModel; 24 | } 25 | 26 | @Override 27 | public JavaSerializeRPCRequest toRequest(RequestModel requestModel) { 28 | JavaSerializeRPCRequest request = new JavaSerializeRPCRequest(); 29 | request.setTraceId(requestModel.getTraceId()); 30 | request.setServiceName(requestModel.getServiceName()); 31 | request.setMethodName(requestModel.getMethodName()); 32 | request.setParamTypes(requestModel.getParamTypes()); 33 | request.setParams(requestModel.getParams()); 34 | return request; 35 | } 36 | 37 | @Override 38 | public ResponseModel fromResponse(JavaSerializeRPCResponse response) { 39 | ResponseModel responseModel = new ResponseModel(); 40 | responseModel.setTraceId(response.getTraceId()); 41 | responseModel.setCode(response.getCode()); 42 | responseModel.setResult(response.getResult()); 43 | responseModel.setErrorMsg(response.getErrorMsg()); 44 | return responseModel; 45 | } 46 | 47 | @Override 48 | public JavaSerializeRPCResponse toResponse(ResponseModel responseModel) { 49 | JavaSerializeRPCResponse response = new JavaSerializeRPCResponse(); 50 | response.setTraceId(responseModel.getTraceId()); 51 | response.setCode(responseModel.getCode()); 52 | response.setResult(responseModel.getResult()); 53 | response.setErrorMsg(responseModel.getErrorMsg()); 54 | return response; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /registry/src/main/java/com/liubs/shadowrpc/registry/access/ServiceDiscovery.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.registry.access; 2 | 3 | import com.liubs.shadowrpc.registry.constant.ServerChangeType; 4 | import com.liubs.shadowrpc.registry.constant.ServiceRegistryPath; 5 | import com.liubs.shadowrpc.registry.entity.ServerNode; 6 | import com.liubs.shadowrpc.registry.listener.ServiceListener; 7 | import com.liubs.shadowrpc.registry.zk.ZooKeeperClient; 8 | import org.apache.curator.framework.CuratorFramework; 9 | import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent; 10 | import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener; 11 | 12 | 13 | /** 14 | * 服务发现 15 | * @author Liubsyy 16 | * @date 2023/12/30 17 | **/ 18 | public class ServiceDiscovery { 19 | private String registryPath; 20 | private String zkNodePath; 21 | private ZooKeeperClient zooKeeperClient; 22 | 23 | public ServiceDiscovery(String registryPath) { 24 | this.registryPath = registryPath; 25 | this.zooKeeperClient = new ZooKeeperClient(registryPath); 26 | } 27 | 28 | 29 | public void watchService(String group,ServiceListener serviceListener) { 30 | zooKeeperClient.addChildrenListener(ServiceRegistryPath.getServerGroupPath(group), new PathChildrenCacheListener() { 31 | @Override 32 | public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent event) throws Exception { 33 | 34 | ServerNode serverNode = null; 35 | switch (event.getType()) { 36 | case CHILD_ADDED: 37 | serverNode = ServerNode.buildInstance(event.getData().getData()); 38 | serviceListener.onServerChange(ServerChangeType.SERVER_ADDED,serverNode); 39 | break; 40 | case CHILD_REMOVED: 41 | serverNode = ServerNode.buildInstance(event.getData().getData()); 42 | serviceListener.onServerChange(ServerChangeType.SERVER_REMOVED,serverNode); 43 | break; 44 | case CHILD_UPDATED: 45 | serverNode = ServerNode.buildInstance(event.getData().getData()); 46 | serviceListener.onServerChange(ServerChangeType.SERVER_UPDATED,serverNode); 47 | break; 48 | } 49 | } 50 | }); 51 | } 52 | 53 | 54 | public void close(){ 55 | if(null != zooKeeperClient) { 56 | zooKeeperClient.close(); 57 | } 58 | } 59 | 60 | 61 | } 62 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | io.github.liubsyy 8 | shadowrpc 9 | 1.0 10 | pom 11 | 12 | 13 | base 14 | protocol 15 | registry 16 | client 17 | client-mini 18 | server 19 | 20 | 21 | 22 | 23 | 24 | 1.8 25 | 1.8 26 | UTF-8 27 | 28 | 29 | 4.1.101.Final 30 | 5.5.0 31 | 5.5.0 32 | 3.25.1 33 | 4.13.2 34 | 2.16.1 35 | 2.22.1 36 | 1.7.36 37 | 0.10.2 38 | 3.14.0 39 | 40 | 41 | 42 | 3.8.1 43 | 3.2.4 44 | 1.7.1 45 | 0.6.1 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | junit 56 | junit 57 | ${junit.version} 58 | test 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | maven-compiler-plugin 67 | ${maven-compiler-plugin.version} 68 | 69 | ${maven.compiler.source} 70 | ${maven.compiler.target} 71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /base/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | io.github.liubsyy 8 | shadowrpc 9 | 1.0 10 | ../pom.xml 11 | 12 | 13 | shadowrpc-base 14 | 1.0.0 15 | 16 | 17 | 18 | 19 | 20 | com.fasterxml.jackson.core 21 | jackson-core 22 | ${jackson.version} 23 | 24 | 25 | com.fasterxml.jackson.core 26 | jackson-databind 27 | ${jackson.version} 28 | 29 | 30 | com.fasterxml.jackson.core 31 | jackson-annotations 32 | ${jackson.version} 33 | 34 | 35 | 36 | 37 | org.apache.logging.log4j 38 | log4j-core 39 | ${log4j.version} 40 | 41 | 42 | org.apache.logging.log4j 43 | log4j-api 44 | ${log4j.version} 45 | 46 | 47 | org.apache.logging.log4j 48 | log4j-slf4j-impl 49 | ${log4j.version} 50 | 51 | 52 | 53 | 54 | org.slf4j 55 | slf4j-api 56 | ${slf4j.version} 57 | 58 | 59 | 60 | 61 | 62 | org.reflections 63 | reflections 64 | ${reflections.version} 65 | 66 | 67 | 68 | 69 | org.apache.commons 70 | commons-lang3 71 | 3.14.0 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /client-mini/src/main/java/com/liubs/shadowrpc/clientmini/connection/ShadowClient.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.clientmini.connection; 2 | 3 | import com.liubs.shadowrpc.clientmini.exception.ConnectTimeoutException; 4 | import com.liubs.shadowrpc.clientmini.handler.RequestHandler; 5 | import com.liubs.shadowrpc.clientmini.handler.ResponseHandler; 6 | import com.liubs.shadowrpc.clientmini.nio.MessageSendFuture; 7 | import com.liubs.shadowrpc.clientmini.nio.NIOClient; 8 | import com.liubs.shadowrpc.clientmini.nio.NIOConfig; 9 | import com.liubs.shadowrpc.clientmini.seriallize.ISerializer; 10 | import com.liubs.shadowrpc.clientmini.seriallize.JavaSerializer; 11 | 12 | import java.io.IOException; 13 | 14 | /** 15 | * @author Liubsyy 16 | * @date 2024/1/20 17 | **/ 18 | public class ShadowClient { 19 | 20 | private String host; 21 | private int port; 22 | 23 | private NIOClient nioClient; 24 | private RequestHandler requestHandler; 25 | private ResponseHandler responseHandler; 26 | 27 | private SimpleHeartBeat simpleHeartBeat; 28 | 29 | public ShadowClient(String host, int port) { 30 | this(host,port,new NIOConfig()); 31 | } 32 | 33 | public ShadowClient(String host, int port, NIOConfig nioConfig) { 34 | this.host = host; 35 | this.port = port; 36 | this.requestHandler = new RequestHandler(nioConfig.getSerializer()); 37 | this.responseHandler = new ResponseHandler(nioConfig.getSerializer()); 38 | this.nioClient = new NIOClient(host,port,nioConfig,responseHandler); 39 | } 40 | 41 | public void connect() throws IOException, ConnectTimeoutException { 42 | nioClient.connect(); 43 | 44 | if(nioClient.getNioConfig().isHearBeat()) { 45 | this.simpleHeartBeat = new SimpleHeartBeat(this,nioClient.getNioConfig().getHeartBeatWaitSeconds()); 46 | this.simpleHeartBeat.start(); 47 | } 48 | } 49 | 50 | public NIOConfig getNIOConfig(){ 51 | return nioClient.getNioConfig(); 52 | } 53 | 54 | public T createRemoteProxy(Class serviceStub, String serviceName) { 55 | return RemoteProxy.create(this,serviceStub,serviceName); 56 | } 57 | 58 | public RequestHandler getRequestHandler() { 59 | return requestHandler; 60 | } 61 | 62 | public ResponseHandler getResponseHandler() { 63 | return responseHandler; 64 | } 65 | 66 | public MessageSendFuture sendMessage(byte[] bytes) { 67 | return nioClient.sendMessage(bytes); 68 | } 69 | 70 | public boolean isRunning(){ 71 | return nioClient.isRunning(); 72 | } 73 | 74 | public void close(){ 75 | nioClient.close(); 76 | if(null !=simpleHeartBeat && simpleHeartBeat.isAlive()) { 77 | simpleHeartBeat.interrupt(); 78 | } 79 | } 80 | 81 | 82 | } 83 | -------------------------------------------------------------------------------- /server/src/main/java/com/liubs/shadowrpc/server/service/ServerManager.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.server.service; 2 | 3 | import com.liubs.shadowrpc.base.config.ServerConfig; 4 | import com.liubs.shadowrpc.base.module.ModulePool; 5 | import com.liubs.shadowrpc.protocol.SerializeModule; 6 | import com.liubs.shadowrpc.server.ServerModule; 7 | import io.netty.util.internal.StringUtil; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import java.util.List; 12 | 13 | /** 14 | * @author Liubsyy 15 | * @date 2023/12/18 10:49 PM 16 | */ 17 | public class ServerManager { 18 | private static final Logger logger = LoggerFactory.getLogger(ServerManager.class); 19 | 20 | private ServerConfig serverConfig; 21 | 22 | private Server server; 23 | 24 | 25 | //序列化 26 | private SerializeModule serializeModule = ModulePool.getModule(SerializeModule.class); 27 | 28 | //服务 29 | private ServerModule serverModule = ModulePool.getModule(ServerModule.class); 30 | 31 | private List packageNames; 32 | 33 | 34 | public List getPackageNames() { 35 | return packageNames; 36 | } 37 | 38 | 39 | public void setPackageNames(List packageNames) { 40 | this.packageNames = packageNames; 41 | } 42 | 43 | public ServerConfig getServerConfig() { 44 | return serverConfig; 45 | } 46 | 47 | public void setServerConfig(ServerConfig serverConfig) { 48 | this.serverConfig = serverConfig; 49 | } 50 | 51 | public Server getServer() { 52 | return server; 53 | } 54 | 55 | public void setServer(Server server) { 56 | this.server = server; 57 | } 58 | 59 | public SerializeModule getSerializeModule() { 60 | return serializeModule; 61 | } 62 | 63 | public void setSerializeModule(SerializeModule serializeModule) { 64 | this.serializeModule = serializeModule; 65 | } 66 | 67 | public ServerModule getServerModule() { 68 | return serverModule; 69 | } 70 | 71 | public void setServerModule(ServerModule serverModule) { 72 | this.serverModule = serverModule; 73 | } 74 | 75 | public ServerManager() { 76 | } 77 | 78 | 79 | public Server start(){ 80 | 81 | serverConfig.checkValid(); 82 | 83 | //序列化模块初始化 84 | serializeModule.init(serverConfig,packageNames); 85 | 86 | //服务初始化 87 | serverModule.init(serverConfig,packageNames); 88 | 89 | //启动服务 90 | server = new Server(serverConfig,serverConfig.getGroup(),serverConfig.getPort()); 91 | if(!StringUtil.isNullOrEmpty(serverConfig.getRegistryUrl())) { 92 | server.setRegistry(serverConfig.getRegistryUrl()); 93 | } 94 | server.start(); 95 | 96 | return server; 97 | } 98 | 99 | 100 | 101 | } 102 | -------------------------------------------------------------------------------- /server/src/test/java/rpctest/registry/MultiGroupServers.java: -------------------------------------------------------------------------------- 1 | package rpctest.registry; 2 | 3 | import com.liubs.shadowrpc.base.config.ServerConfig; 4 | import com.liubs.shadowrpc.base.constant.SerializerEnum; 5 | import com.liubs.shadowrpc.server.init.ServerBuilder; 6 | import org.junit.Test; 7 | 8 | /** 9 | * @author Liubsyy 10 | * @date 2024/1/24 11 | * 多集群模式单测 12 | **/ 13 | public class MultiGroupServers { 14 | 15 | public static final String ZK_URL = "localhost:2181"; 16 | 17 | @Test 18 | public void group1service1(){ 19 | ServerConfig serverConfig = new ServerConfig(); 20 | serverConfig.setGroup("group1"); 21 | serverConfig.setRegistryUrl(ZK_URL); 22 | serverConfig.setQpsStat(true); //统计qps 23 | serverConfig.setSerializer(SerializerEnum.KRYO.name()); 24 | serverConfig.setPort(2023); 25 | ServerBuilder.newBuilder() 26 | .serverConfig(serverConfig) 27 | .addPackage("rpctest.registry") 28 | .build() 29 | .start() 30 | .keep(); 31 | } 32 | @Test 33 | public void group1service2(){ 34 | ServerConfig serverConfig = new ServerConfig(); 35 | serverConfig.setGroup("group1"); 36 | serverConfig.setRegistryUrl(ZK_URL); 37 | serverConfig.setQpsStat(true); //统计qps 38 | serverConfig.setSerializer(SerializerEnum.KRYO.name()); 39 | serverConfig.setPort(2024); 40 | ServerBuilder.newBuilder() 41 | .serverConfig(serverConfig) 42 | .addPackage("rpctest.registry") 43 | .build() 44 | .start() 45 | .keep(); 46 | } 47 | 48 | @Test 49 | public void group2service1(){ 50 | ServerConfig serverConfig = new ServerConfig(); 51 | serverConfig.setGroup("group2"); 52 | serverConfig.setRegistryUrl(ZK_URL); 53 | serverConfig.setQpsStat(true); //统计qps 54 | serverConfig.setSerializer(SerializerEnum.KRYO.name()); 55 | serverConfig.setPort(2025); 56 | ServerBuilder.newBuilder() 57 | .serverConfig(serverConfig) 58 | .addPackage("rpctest.registry") 59 | .build() 60 | .start() 61 | .keep(); 62 | } 63 | 64 | @Test 65 | public void group2service2(){ 66 | ServerConfig serverConfig = new ServerConfig(); 67 | serverConfig.setGroup("group2"); 68 | serverConfig.setRegistryUrl(ZK_URL); 69 | serverConfig.setQpsStat(true); //统计qps 70 | serverConfig.setSerializer(SerializerEnum.KRYO.name()); 71 | serverConfig.setPort(2026); 72 | ServerBuilder.newBuilder() 73 | .serverConfig(serverConfig) 74 | .addPackage("rpctest.registry") 75 | .build() 76 | .start() 77 | .keep(); 78 | } 79 | 80 | 81 | 82 | 83 | } 84 | -------------------------------------------------------------------------------- /protocol/src/main/java/com/liubs/shadowrpc/protocol/serializer/SerializerStrategy.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.protocol.serializer; 2 | 3 | import com.liubs.shadowrpc.base.constant.SerializerEnum; 4 | import com.liubs.shadowrpc.protocol.entity.*; 5 | import com.liubs.shadowrpc.protocol.model.IModelParser; 6 | import com.liubs.shadowrpc.protocol.serializer.javaserializer.JavaModelParser; 7 | import com.liubs.shadowrpc.protocol.serializer.javaserializer.JavaSerializer; 8 | import com.liubs.shadowrpc.protocol.serializer.kryo.KryoModelParser; 9 | import com.liubs.shadowrpc.protocol.serializer.kryo.KryoSerializer; 10 | import com.liubs.shadowrpc.protocol.serializer.protobuf.ProtobufModelParser; 11 | import com.liubs.shadowrpc.protocol.serializer.protobuf.ShadowProtobufSerializer; 12 | 13 | /** 14 | * @author Liubsyy 15 | * @date 2023/12/23 6:36 PM 16 | **/ 17 | public enum SerializerStrategy { 18 | 19 | KRYO(SerializerEnum.KRYO, 20 | new KryoSerializer(), 21 | ShadowRPCRequest.class, 22 | ShadowRPCResponse.class, 23 | new KryoModelParser() 24 | ), 25 | 26 | PROTOBUF(SerializerEnum.PROTOBUF, 27 | new ShadowProtobufSerializer(), 28 | ShadowRPCRequestProto.ShadowRPCRequest.class, 29 | ShadowRPCResponseProto.ShadowRPCResponse.class, 30 | new ProtobufModelParser() 31 | ), 32 | 33 | JAVA_SERIALISE(SerializerEnum.JAVA_SERIALISE, 34 | new JavaSerializer(), 35 | JavaSerializeRPCRequest.class, 36 | JavaSerializeRPCResponse.class, 37 | new JavaModelParser() 38 | ), 39 | 40 | ; 41 | 42 | private SerializerEnum serializerEnum; 43 | private ISerializer serializer; 44 | private Class requestClass; 45 | private Class responseClass; 46 | 47 | private IModelParser modelParser; 48 | 49 | 50 | SerializerStrategy(SerializerEnum serializerEnum,ISerializer serializer, Class requestClass, Class responseClass, IModelParser modelParser) { 51 | this.serializerEnum = serializerEnum; 52 | this.serializer = serializer; 53 | this.requestClass = requestClass; 54 | this.responseClass = responseClass; 55 | this.modelParser = modelParser; 56 | } 57 | 58 | 59 | public ISerializer getSerializer() { 60 | return serializer; 61 | } 62 | 63 | public Class getRequestClass() { 64 | return requestClass; 65 | } 66 | 67 | public Class getResponseClass() { 68 | return responseClass; 69 | } 70 | 71 | public IModelParser getModelParser() { 72 | return modelParser; 73 | } 74 | 75 | 76 | 77 | public static SerializerStrategy findBySerializer(SerializerEnum serializerEnum) { 78 | for(SerializerStrategy e : values()) { 79 | if(e.serializerEnum == serializerEnum) { 80 | return e; 81 | } 82 | } 83 | return null; 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /protocol/src/main/java/com/liubs/shadowrpc/protocol/SerializeModule.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.protocol; 2 | 3 | import com.google.protobuf.MessageLite; 4 | import com.liubs.shadowrpc.base.annotation.ShadowModule; 5 | import com.liubs.shadowrpc.base.config.BaseConfig; 6 | import com.liubs.shadowrpc.base.constant.SerializerEnum; 7 | import com.liubs.shadowrpc.base.module.IModule; 8 | import com.liubs.shadowrpc.base.util.ClassScanWalker; 9 | import com.liubs.shadowrpc.protocol.serializer.SerializerStrategy; 10 | import com.liubs.shadowrpc.protocol.serializer.protobuf.ParserForType; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | 14 | import java.io.IOException; 15 | import java.lang.reflect.InvocationTargetException; 16 | import java.util.List; 17 | 18 | /** 19 | * @author Liubsyy 20 | * @date 2024/1/15 21 | */ 22 | 23 | @ShadowModule 24 | public class SerializeModule implements IModule { 25 | private static final Logger logger = LoggerFactory.getLogger(SerializeModule.class); 26 | 27 | 28 | //配置 29 | private BaseConfig config; 30 | 31 | //序列化方式 32 | private SerializerStrategy serializer; 33 | 34 | private void init(BaseConfig baseConfig) { 35 | this.config = baseConfig; 36 | SerializerEnum serializerEnum = SerializerEnum.findByType(baseConfig.getSerializer()); 37 | if(null == serializerEnum) { 38 | throw new RuntimeException("Cannot find serializer:"+baseConfig.getSerializer()); 39 | } 40 | this.serializer = SerializerStrategy.findBySerializer(serializerEnum); 41 | } 42 | 43 | 44 | public void init(BaseConfig baseConfig, List packages) { 45 | init(baseConfig); 46 | 47 | for(String packageName : packages) { 48 | try { 49 | ClassScanWalker.scanPackage(packageName,(classz)->{ 50 | if(MessageLite.class.isAssignableFrom(classz)) { 51 | try { 52 | MessageLite messageLite = (MessageLite)classz.getDeclaredMethod("getDefaultInstance").invoke(null); 53 | ParserForType.addMessage(classz.getName(),messageLite); 54 | 55 | } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { 56 | e.printStackTrace(); 57 | } 58 | } 59 | }); 60 | } catch (IOException e) { 61 | logger.error("序列化包初始化失败",e); 62 | } 63 | } 64 | } 65 | 66 | public SerializerStrategy getSerializer() { 67 | return serializer; 68 | } 69 | 70 | public byte[] serialize(Object obj) { 71 | return serializer.getSerializer().serialize(obj); 72 | } 73 | 74 | public T deserializeRequest(byte[] bytes) { 75 | return (T)serializer.getSerializer().deserialize(bytes, serializer.getRequestClass()); 76 | } 77 | 78 | public Object deserializeResponse(byte[] bytes) { 79 | return (T)serializer.getSerializer().deserialize(bytes,serializer.getResponseClass()); 80 | } 81 | 82 | 83 | 84 | 85 | } 86 | -------------------------------------------------------------------------------- /client/src/test/java/rpctest/upload/UploadClient.java: -------------------------------------------------------------------------------- 1 | package rpctest.upload; 2 | 3 | import com.liubs.shadowrpc.base.config.ClientConfig; 4 | import com.liubs.shadowrpc.base.module.ModulePool; 5 | import com.liubs.shadowrpc.client.ClientModule; 6 | import com.liubs.shadowrpc.client.connection.ShadowClient; 7 | import com.liubs.shadowrpc.client.proxy.RemoteServerProxy; 8 | import com.liubs.shadowrpc.protocol.serializer.SerializerStrategy; 9 | import org.junit.Assert; 10 | import org.junit.Before; 11 | import org.junit.Test; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | import java.io.File; 16 | import java.io.IOException; 17 | import java.nio.file.Files; 18 | import java.nio.file.Paths; 19 | import java.nio.file.StandardOpenOption; 20 | import java.util.UUID; 21 | 22 | /** 23 | * @author Liubsyy 24 | * @date 2024/1/14 25 | **/ 26 | public class UploadClient { 27 | private static final Logger logger = LoggerFactory.getLogger(UploadClient.class); 28 | 29 | @Before 30 | public void init(){ 31 | ClientConfig clientConfig = new ClientConfig(); 32 | clientConfig.setSerializer(SerializerStrategy.KRYO.name()); 33 | ModulePool.getModule(ClientModule.class).init(clientConfig); 34 | } 35 | 36 | @Test 37 | public void uploadFileTest() throws IOException { 38 | 39 | ShadowClient shadowClient = new ShadowClient("127.0.0.1",2023); 40 | shadowClient.init(); 41 | 42 | IUploadService uploadService = shadowClient.createRemoteProxy(IUploadService.class,"shadowrpc://DefaultGroup/uploadService"); 43 | 44 | byte[] bytes = Files.readAllBytes(Paths.get("/Users/liubs/IdeaProjects/ShadowRPC/protocol/src/test/resources/proto/MyMessage.proto")); 45 | boolean upload = uploadService.upload("upload_MyMessage.proto", bytes); 46 | Assert.assertTrue(upload); 47 | 48 | shadowClient.close(); 49 | } 50 | 51 | @Test 52 | public void uploadManyFilesTest() throws IOException { 53 | ShadowClient shadowClient = new ShadowClient("127.0.0.1",2023); 54 | shadowClient.init(); 55 | IUploadService uploadService = shadowClient.createRemoteProxy(IUploadService.class,"uploadService"); 56 | 57 | File uploadDir = new File("target/tmp/"); 58 | if(!uploadDir.exists()) { 59 | uploadDir.mkdirs(); 60 | } 61 | 62 | //生成100个文件 63 | int n = 100; 64 | for(int i = 0;i children = client.getChildren(path); 62 | assertEquals(children.size(),n); 63 | } 64 | 65 | @Test 66 | public void testChildrenListener() throws Exception { 67 | String path = "/test/childrenListen"; 68 | 69 | client.addChildrenListener(path, (client, event) -> { 70 | switch (event.getType()) { 71 | case CHILD_ADDED: 72 | System.out.println("Child added: " + event.getData().getPath()); 73 | break; 74 | case CHILD_REMOVED: 75 | System.out.println("Child removed: " + event.getData().getPath()); 76 | break; 77 | case CHILD_UPDATED: 78 | System.out.println("Child updated: " + event.getData().getPath()); 79 | break; 80 | } 81 | }); 82 | 83 | int n = 10; 84 | List allPaths = new ArrayList<>(); 85 | for(int i =1;i<=n;i++) { 86 | allPaths.add(client.create(path+"/node"+i, ("/node"+i).getBytes())); 87 | } 88 | client.delete(allPaths.get(1)); 89 | client.delete(allPaths.get(2)); 90 | client.delete(allPaths.get(3)); 91 | 92 | client.update(allPaths.get(5), ("/newnode5").getBytes()); 93 | 94 | Thread.sleep(5000); 95 | 96 | } 97 | 98 | 99 | @AfterClass 100 | public static void tearDown() throws Exception { 101 | client.close(); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /client/src/main/java/com/liubs/shadowrpc/client/connection/ShadowClient.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.client.connection; 2 | 3 | import com.liubs.shadowrpc.base.config.ClientConfig; 4 | import com.liubs.shadowrpc.base.module.ModulePool; 5 | import com.liubs.shadowrpc.client.ClientModule; 6 | import com.liubs.shadowrpc.client.handler.ShadowChannelInitializer; 7 | import com.liubs.shadowrpc.client.proxy.RemoteServerProxy; 8 | import io.netty.bootstrap.Bootstrap; 9 | import io.netty.channel.Channel; 10 | import io.netty.channel.ChannelFuture; 11 | import io.netty.channel.EventLoopGroup; 12 | import io.netty.channel.nio.NioEventLoopGroup; 13 | import io.netty.channel.socket.nio.NioSocketChannel; 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | 17 | /** 18 | * 一个远程连接源,每一个远程服务器节点,即为一个ShadowClient实例 19 | * @author Liubsyy 20 | * @date 2023/12/18 10:56 PM 21 | **/ 22 | 23 | public class ShadowClient implements IConnection{ 24 | private static final Logger logger = LoggerFactory.getLogger(ShadowClientGroup.class); 25 | 26 | private EventLoopGroup group; 27 | private Channel channel; 28 | 29 | private String remoteIp; 30 | private int remotePort; 31 | 32 | private ClientConfig config = ModulePool.getModule(ClientModule.class).getConfig(); 33 | 34 | public ShadowClient(String host, int port) { 35 | this(host,port,new NioEventLoopGroup()); 36 | } 37 | public ShadowClient(String host, int port,EventLoopGroup group) { 38 | this.remoteIp = host; 39 | this.remotePort = port; 40 | this.group = group; 41 | } 42 | 43 | @Override 44 | public void init(){ 45 | 46 | try { 47 | Bootstrap bootstrap = new Bootstrap(); 48 | bootstrap.group(group) 49 | .channel(NioSocketChannel.class) 50 | .handler(new ShadowChannelInitializer(config)); 51 | 52 | // 连接到服务器 53 | ChannelFuture future = bootstrap.connect(remoteIp, remotePort).sync(); 54 | channel = future.channel(); 55 | 56 | System.out.printf("连接 %s:%d 成功\n",remoteIp,remotePort); 57 | 58 | Runtime.getRuntime().addShutdownHook(new Thread(this::close)); 59 | } catch (InterruptedException e) { 60 | e.printStackTrace(); 61 | close(); 62 | } 63 | } 64 | 65 | @Override 66 | public T createRemoteProxy(Class serviceStub, String service) { 67 | return RemoteServerProxy.create(this,serviceStub,service); 68 | } 69 | 70 | 71 | public String getRemoteIp() { 72 | return remoteIp; 73 | } 74 | 75 | public void setRemoteIp(String remoteIp) { 76 | this.remoteIp = remoteIp; 77 | } 78 | 79 | public int getRemotePort() { 80 | return remotePort; 81 | } 82 | 83 | public void setRemotePort(int remotePort) { 84 | this.remotePort = remotePort; 85 | } 86 | 87 | @Override 88 | public Channel getChannel(String group) { 89 | return channel; 90 | } 91 | 92 | 93 | public void keep(){ 94 | try{ 95 | channel.closeFuture().sync(); 96 | } catch (InterruptedException e) { 97 | e.printStackTrace(); 98 | } 99 | } 100 | 101 | @Override 102 | public void close(){ 103 | try{ 104 | 105 | if(null != channel) { 106 | channel.close(); 107 | } 108 | } finally { 109 | group.shutdownGracefully(); 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /server/src/main/java/com/liubs/shadowrpc/server/ServerModule.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.server; 2 | 3 | import com.liubs.shadowrpc.base.annotation.ModuleInject; 4 | import com.liubs.shadowrpc.base.annotation.ShadowModule; 5 | import com.liubs.shadowrpc.base.annotation.ShadowService; 6 | import com.liubs.shadowrpc.base.annotation.ShadowServiceHolder; 7 | import com.liubs.shadowrpc.base.config.ServerConfig; 8 | import com.liubs.shadowrpc.base.module.IModule; 9 | import com.liubs.shadowrpc.protocol.SerializeModule; 10 | import com.liubs.shadowrpc.protocol.util.AnnotationScanner; 11 | import com.liubs.shadowrpc.server.service.ServiceLookUp; 12 | import com.liubs.shadowrpc.server.service.ServiceTarget; 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | 16 | import java.io.IOException; 17 | import java.lang.reflect.Method; 18 | import java.lang.reflect.Modifier; 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | import java.util.Map; 22 | import java.util.concurrent.ConcurrentHashMap; 23 | 24 | /** 25 | * @author Liubsyy 26 | * @date 2024/1/16 27 | */ 28 | @ShadowModule 29 | public class ServerModule implements IModule { 30 | private static final Logger logger = LoggerFactory.getLogger(ServerModule.class); 31 | 32 | @ModuleInject 33 | private SerializeModule serializeModule; 34 | 35 | private ServerConfig serverConfig; 36 | 37 | //所有服务 38 | private Map allRPC = new ConcurrentHashMap<>(); 39 | 40 | public void init(ServerConfig serverConfig,List packages) { 41 | this.serverConfig = serverConfig; 42 | //初始化服务 43 | List> shadowServices = new ArrayList<>(); 44 | 45 | for(String packageName : packages) { 46 | try { 47 | shadowServices.addAll(AnnotationScanner.scanAnnotations(packageName, ShadowService.class)); 48 | } catch (IOException e) { 49 | logger.error("scanService err",e); 50 | } 51 | } 52 | 53 | for(ShadowServiceHolder ShadowServiceHolder : shadowServices) { 54 | ShadowService serviceAnnotation = ShadowServiceHolder.getAnnotation(); 55 | Class serviceClass = ShadowServiceHolder.getClassz(); 56 | try { 57 | Object o = serviceClass.newInstance(); 58 | 59 | 60 | for(Method method : serviceClass.getMethods()) { 61 | 62 | if(Modifier.isStatic(method.getModifiers()) || !Modifier.isPublic(method.getModifiers())){ 63 | continue; 64 | } 65 | 66 | ServiceLookUp serviceLookUp = new ServiceLookUp(); 67 | serviceLookUp.setServiceName(serviceAnnotation.serviceName()); 68 | serviceLookUp.setMethodName(method.getName()); 69 | serviceLookUp.setParamTypes(method.getParameterTypes()); 70 | 71 | ServiceTarget serviceTarget = new ServiceTarget(); 72 | serviceTarget.setTargetObj(o); 73 | serviceTarget.setMethod(method); 74 | addRPCInterface(serviceLookUp,serviceTarget); 75 | } 76 | 77 | } catch (InstantiationException | IllegalAccessException e) { 78 | throw new RuntimeException(e); 79 | } 80 | } 81 | } 82 | 83 | public void addRPCInterface(ServiceLookUp lookUp,ServiceTarget obj) { 84 | allRPC.put(lookUp,obj); 85 | } 86 | 87 | public ServiceTarget getRPC(ServiceLookUp lookUp) { 88 | return allRPC.get(lookUp); 89 | } 90 | 91 | 92 | 93 | } 94 | -------------------------------------------------------------------------------- /server/src/main/java/com/liubs/shadowrpc/server/handler/ServerHandler.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.server.handler; 2 | 3 | 4 | import com.liubs.shadowrpc.base.module.ModulePool; 5 | import com.liubs.shadowrpc.protocol.SerializeModule; 6 | import com.liubs.shadowrpc.protocol.constant.ResponseCode; 7 | import com.liubs.shadowrpc.protocol.model.IModelParser; 8 | import com.liubs.shadowrpc.protocol.model.RequestModel; 9 | import com.liubs.shadowrpc.protocol.model.ResponseModel; 10 | import com.liubs.shadowrpc.server.ServerModule; 11 | import com.liubs.shadowrpc.server.service.ServiceLookUp; 12 | import com.liubs.shadowrpc.server.service.ServiceTarget; 13 | import io.netty.channel.ChannelHandlerContext; 14 | import io.netty.channel.ChannelInboundHandlerAdapter; 15 | import org.slf4j.Logger; 16 | import org.slf4j.LoggerFactory; 17 | 18 | import java.util.concurrent.ExecutorService; 19 | import java.util.concurrent.Executors; 20 | 21 | /** 22 | * @author Liubsyy 23 | * @date 2023/12/3 10:23 PM 24 | **/ 25 | public class ServerHandler extends ChannelInboundHandlerAdapter { 26 | private static final Logger logger = LoggerFactory.getLogger(ServerHandler.class); 27 | 28 | private static ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); 29 | 30 | private SerializeModule serializeModule = ModulePool.getModule(SerializeModule.class); 31 | private ServerModule serverModule = ModulePool.getModule(ServerModule.class); 32 | 33 | @Override 34 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 35 | logger.info("客户端{} 已连接",ctx.channel().remoteAddress()); 36 | super.channelActive(ctx); 37 | } 38 | 39 | @Override 40 | public void channelInactive(ChannelHandlerContext ctx) throws Exception { 41 | logger.info("客户端{} 断开连接",ctx.channel().remoteAddress()); 42 | super.channelInactive(ctx); 43 | } 44 | 45 | @Override 46 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 47 | 48 | // 打印验证影响速度,压测时去掉 49 | //logger.info("Server received: " + msg); 50 | 51 | IModelParser modelParser = serializeModule.getSerializer().getModelParser(); 52 | 53 | RequestModel requestModel = modelParser.fromRequest(msg); 54 | 55 | //System.out.println("Server received: " + requestModel.getParams()[0]); 56 | executorService.execute(()->{ 57 | try { 58 | 59 | ServiceLookUp serviceLookUp = new ServiceLookUp(); 60 | serviceLookUp.setServiceName(requestModel.getServiceName()); 61 | serviceLookUp.setMethodName(requestModel.getMethodName()); 62 | serviceLookUp.setParamTypes(requestModel.getParamTypes()); 63 | ServiceTarget targetRPC = serverModule.getRPC(serviceLookUp); 64 | 65 | Object result = targetRPC.invoke(requestModel.getParams()); 66 | 67 | ResponseModel responseModel = new ResponseModel(); 68 | responseModel.setTraceId(requestModel.getTraceId()); 69 | responseModel.setCode(ResponseCode.SUCCESS.getCode()); 70 | responseModel.setResult(result); 71 | 72 | // 响应客户端 73 | ctx.writeAndFlush(modelParser.toResponse(responseModel)); 74 | } catch (Exception e) { 75 | e.printStackTrace(); 76 | } 77 | 78 | }); 79 | 80 | } 81 | 82 | 83 | @Override 84 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 85 | // 异常处理 86 | logger.error("exceptionCaught",cause); 87 | ctx.close(); 88 | } 89 | 90 | 91 | } 92 | -------------------------------------------------------------------------------- /base/src/main/java/com/liubs/shadowrpc/base/util/ClassScanWalker.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.base.util; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.net.URL; 6 | import java.util.Enumeration; 7 | import java.util.function.Consumer; 8 | import java.util.jar.JarEntry; 9 | import java.util.jar.JarFile; 10 | 11 | /** 12 | * @author Liubsyy 13 | * @date 2023/12/24 1:18 AM 14 | **/ 15 | public class ClassScanWalker { 16 | public static void scanPackage(String packageName, Consumer> consumer) throws IOException { 17 | ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 18 | scanPackage(classLoader,packageName,consumer); 19 | } 20 | 21 | private static void scanPackage(ClassLoader classLoader, String packageName, Consumer> consumer) throws IOException { 22 | String packagePath = packageName.replace('.', '/'); 23 | Enumeration resources = classLoader.getResources(packagePath); 24 | 25 | 26 | while (resources.hasMoreElements()) { 27 | URL resource = resources.nextElement(); 28 | if (resource.getProtocol().equals("jar")) { 29 | String jarPath = resource.getPath().substring(5, resource.getPath().indexOf("!")); 30 | scanFromJar(classLoader,jarPath,packagePath,consumer ); 31 | } else { 32 | scanFromDir(classLoader,new File(resource.getFile()),packageName,consumer); 33 | } 34 | } 35 | 36 | } 37 | 38 | private static void scanFromJar(ClassLoader classLoader,String jarPath,String packagePath , Consumer> consumer) throws IOException { 39 | JarFile jar = new JarFile(jarPath); 40 | 41 | // 遍历JAR文件中的条目 42 | Enumeration entries = jar.entries(); 43 | while (entries.hasMoreElements()) { 44 | JarEntry entry = entries.nextElement(); 45 | String name = entry.getName(); 46 | 47 | // 检查条目是否为指定包路径下的类 48 | if (name.startsWith(packagePath) && name.endsWith(".class")) { 49 | String className = name.replace('/', '.').substring(0, name.length() - 6); 50 | 51 | Class clazz = null; 52 | try { 53 | clazz = classLoader.loadClass(className); 54 | } catch (ClassNotFoundException e) { 55 | e.printStackTrace(); 56 | } 57 | if(null != clazz) { 58 | consumer.accept(clazz); 59 | } 60 | } 61 | } 62 | jar.close(); 63 | } 64 | 65 | private static void scanFromDir(ClassLoader classLoader,File directory, String packageName, Consumer> consumer) { 66 | if (!directory.exists()) { 67 | return; 68 | } 69 | File[] files = directory.listFiles(); 70 | if (files != null) { 71 | for (File file : files) { 72 | if (file.isDirectory()) { 73 | scanFromDir(classLoader,file, packageName + "." + file.getName(),consumer); 74 | } else if (file.getName().endsWith(".class")) { 75 | Class clazz = null; 76 | try { 77 | clazz = classLoader.loadClass(packageName + '.' + 78 | file.getName().substring(0, file.getName().length() - 6)); 79 | } catch (ClassNotFoundException e) { 80 | e.printStackTrace(); 81 | } 82 | if(null != clazz) { 83 | consumer.accept(clazz); 84 | } 85 | } 86 | } 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /client/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | io.github.liubsyy 8 | shadowrpc 9 | 1.0 10 | ../pom.xml 11 | 12 | 13 | shadowrpc-client 14 | 1.0.0 15 | 16 | 17 | 18 | 19 | io.github.liubsyy 20 | shadowrpc-base 21 | 1.0.0 22 | compile 23 | 24 | 25 | 26 | io.github.liubsyy 27 | shadowrpc-protocol 28 | 1.0.0 29 | 30 | 31 | 32 | io.github.liubsyy 33 | shadowrpc-registry 34 | 1.0.0 35 | compile 36 | 37 | 38 | 39 | io.netty 40 | netty-handler 41 | 42 | 43 | io.netty 44 | netty-transport-native-epoll 45 | 46 | 47 | 48 | 49 | 71 | 72 | 73 | io.netty 74 | netty-all 75 | ${netty.version} 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 101 | 102 | 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /client/src/main/java/com/liubs/shadowrpc/client/proxy/RemoteHandler.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.client.proxy; 2 | 3 | import com.liubs.shadowrpc.base.module.ModulePool; 4 | import com.liubs.shadowrpc.client.connection.IConnection; 5 | import com.liubs.shadowrpc.client.handler.ReceiveHolder; 6 | import com.liubs.shadowrpc.protocol.SerializeModule; 7 | import com.liubs.shadowrpc.protocol.model.IModelParser; 8 | import com.liubs.shadowrpc.protocol.model.RequestModel; 9 | import com.liubs.shadowrpc.protocol.model.ResponseModel; 10 | import io.netty.channel.Channel; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | 14 | import java.lang.reflect.InvocationHandler; 15 | import java.lang.reflect.Method; 16 | import java.util.UUID; 17 | import java.util.concurrent.Future; 18 | import java.util.concurrent.TimeUnit; 19 | import java.util.concurrent.TimeoutException; 20 | 21 | /** 22 | * @author Liubsyy 23 | * @date 2023/12/31 24 | **/ 25 | public class RemoteHandler implements InvocationHandler { 26 | private static final Logger logger = LoggerFactory.getLogger(RemoteHandler.class); 27 | 28 | /** 29 | * 如果不使用注册中心,则必须有ShadowClient 30 | */ 31 | private IConnection clientConnection; 32 | 33 | /** 34 | * 远程接口stub 35 | */ 36 | private Class serviceStub; 37 | 38 | 39 | /** 40 | * 集群 41 | */ 42 | private String group; 43 | 44 | /** 45 | * 服务名 46 | */ 47 | private String serviceName; 48 | 49 | 50 | private SerializeModule serializeModule = ModulePool.getModule(SerializeModule.class); 51 | 52 | public RemoteHandler(IConnection client, Class serviceStub, String group,String serviceName) { 53 | this.clientConnection = client; 54 | this.serviceStub = serviceStub; 55 | this.group = group; 56 | this.serviceName = serviceName; 57 | } 58 | 59 | @Override 60 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 61 | 62 | try{ 63 | RequestModel requestModel = new RequestModel(); 64 | String traceId = UUID.randomUUID().toString(); 65 | requestModel.setTraceId(traceId); 66 | requestModel.setServiceName(serviceName); 67 | requestModel.setMethodName(method.getName()); 68 | requestModel.setParamTypes(method.getParameterTypes()); 69 | requestModel.setParams(args); 70 | 71 | IModelParser modelParser = serializeModule.getSerializer().getModelParser(); 72 | 73 | Channel channel = clientConnection.getChannel(group); 74 | 75 | if(!channel.isOpen()) { 76 | logger.error("服务器已关闭,发送消息抛弃..."); 77 | return null; 78 | } 79 | 80 | Future future = ReceiveHolder.getInstance().initFuture(traceId); 81 | try{ 82 | channel.writeAndFlush(modelParser.toRequest(requestModel)).sync(); 83 | }catch (Exception e) { 84 | logger.error("发送请求{}失败",traceId); 85 | ReceiveHolder.getInstance().deleteWait(traceId); 86 | return null; 87 | } 88 | 89 | try{ 90 | ResponseModel responseModel = (ResponseModel)future.get(3, TimeUnit.SECONDS); 91 | return responseModel.getResult(); 92 | }catch (TimeoutException timeoutException) { 93 | logger.error("超时请求,抛弃消息{}",traceId); 94 | ReceiveHolder.getInstance().deleteWait(traceId); 95 | return null; 96 | } 97 | 98 | }catch (Throwable e) { 99 | logger.error("invoke err",e); 100 | } 101 | 102 | return null; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /client/src/test/java/rpctest/hello/HelloProtoClient.java: -------------------------------------------------------------------------------- 1 | package rpctest.hello; 2 | 3 | import com.liubs.shadowrpc.base.config.ClientConfig; 4 | import com.liubs.shadowrpc.base.module.ModulePool; 5 | import com.liubs.shadowrpc.client.ClientModule; 6 | import com.liubs.shadowrpc.client.connection.ShadowClient; 7 | import com.liubs.shadowrpc.protocol.serializer.SerializerStrategy; 8 | import com.liubs.shadowrpc.client.proxy.RemoteServerProxy; 9 | import org.junit.Before; 10 | import org.junit.Test; 11 | import rpctest.entity.MyMessageProto; 12 | 13 | import java.util.ArrayList; 14 | import java.util.Collections; 15 | import java.util.List; 16 | import java.util.concurrent.Callable; 17 | import java.util.concurrent.ExecutorService; 18 | import java.util.concurrent.Executors; 19 | 20 | /** 21 | * 基于protobuf服务的调用 22 | * @author Liubsyy 23 | * @date 2023/12/25 12:55 AM 24 | **/ 25 | public class HelloProtoClient { 26 | 27 | private static ClientConfig clientConfig = new ClientConfig(); 28 | 29 | @Before 30 | public void init(){ 31 | clientConfig.setSerializer(SerializerStrategy.PROTOBUF.name()); 32 | ModulePool.getModule(ClientModule.class).init(clientConfig,Collections.singletonList("rpctest.entity")); 33 | } 34 | 35 | @Test 36 | public void helloClient() { 37 | 38 | ShadowClient shadowClient = new ShadowClient("127.0.0.1",2024); 39 | shadowClient.init(); 40 | 41 | IHelloProto helloService = shadowClient.createRemoteProxy(IHelloProto.class,"shadowrpc://DefaultGroup/helloprotoservice"); 42 | MyMessageProto.MyMessage message = MyMessageProto.MyMessage.newBuilder() 43 | .setNum(100) 44 | .setContent("Hello, Server!") 45 | .build(); 46 | 47 | System.out.printf("发送请求 : %s\n",message); 48 | MyMessageProto.MyMessage response = helloService.say(message); 49 | System.out.printf("接收服务端消息 : %s\n",response); 50 | shadowClient.close(); 51 | 52 | } 53 | 54 | /** 55 | * 并发调用,测试拆包和粘包的可靠性 56 | */ 57 | @Test 58 | public void helloConcurrent() throws InterruptedException { 59 | 60 | ShadowClient shadowClient = new ShadowClient("127.0.0.1",2024); 61 | shadowClient.init(); 62 | 63 | //调用远程RPC接口 64 | IHelloProto helloService = shadowClient.createRemoteProxy(IHelloProto.class,"shadowrpc://DefaultGroup/helloprotoservice"); 65 | 66 | 67 | MyMessageProto.MyMessage requestMsg = MyMessageProto.MyMessage.newBuilder() 68 | .setNum(100) 69 | .setContent("Hello, Server!") 70 | .build(); 71 | 72 | System.out.printf("发送请求 : %s\n",requestMsg); 73 | MyMessageProto.MyMessage responseMsg = helloService.say(requestMsg); 74 | System.out.printf("接收服务端消息 : %s\n",responseMsg); 75 | 76 | 77 | long time1 = System.currentTimeMillis(); 78 | List> futureTaskList = new ArrayList<>(); 79 | 80 | 81 | //100w个请求,25s 82 | final int n = 1000000; 83 | for(int i = 1;i<=n;i++) { 84 | final int j = i; 85 | futureTaskList.add(() -> { 86 | MyMessageProto.MyMessage message = MyMessageProto.MyMessage.newBuilder().setNum(j).setContent("Hello, Server!").build(); 87 | 88 | //打印消息影响速度,去掉打印至少快一倍 89 | //System.out.printf("发送请求%d \n",j); 90 | MyMessageProto.MyMessage response = helloService.say(message); 91 | // System.out.printf("接收服务端消息%d \n",j); 92 | 93 | return "success"; 94 | }); 95 | } 96 | 97 | ExecutorService executorService = Executors.newFixedThreadPool(50); 98 | executorService.invokeAll(futureTaskList); 99 | long time2 = System.currentTimeMillis(); 100 | 101 | long useMs = (time2-time1); 102 | System.out.println("总共用时: "+useMs+" ms"); 103 | 104 | executorService.shutdownNow(); 105 | shadowClient.close(); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /registry/src/main/java/com/liubs/shadowrpc/registry/zk/ZooKeeperClient.java: -------------------------------------------------------------------------------- 1 | package com.liubs.shadowrpc.registry.zk; 2 | 3 | import org.apache.curator.framework.CuratorFramework; 4 | import org.apache.curator.framework.CuratorFrameworkFactory; 5 | import org.apache.curator.framework.recipes.cache.CuratorCache; 6 | import org.apache.curator.framework.recipes.cache.CuratorCacheListener; 7 | import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener; 8 | import org.apache.curator.retry.ExponentialBackoffRetry; 9 | import org.apache.zookeeper.CreateMode; 10 | 11 | import java.util.HashMap; 12 | import java.util.List; 13 | import java.util.Map; 14 | import java.util.concurrent.CountDownLatch; 15 | import java.util.concurrent.TimeUnit; 16 | 17 | /** 18 | * 对zookeeper的连接,增删改查,获取字节点,监听子节点 19 | * @author Liubsyy 20 | * @date 2023/12/18 11:36 PM 21 | **/ 22 | public class ZooKeeperClient { 23 | 24 | private CuratorFramework client; 25 | private Map curatorCacheMap = new HashMap<>(); 26 | 27 | public ZooKeeperClient(String connectionString) { 28 | this.client = CuratorFrameworkFactory.newClient( 29 | connectionString, 30 | new ExponentialBackoffRetry(1000, 3)); 31 | this.client.start(); 32 | } 33 | 34 | public String create(String path) throws Exception { 35 | //临时顺序节点,断开自动删除 36 | return client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(path); 37 | } 38 | 39 | public String create(String path, byte[] payload) throws Exception { 40 | //临时顺序节点,断开自动删除 41 | return client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(path,payload); 42 | } 43 | 44 | public byte[] read(String path) throws Exception { 45 | return client.getData().forPath(path); 46 | } 47 | 48 | public void update(String path, byte[] payload) throws Exception { 49 | client.setData().forPath(path, payload); 50 | 51 | } 52 | 53 | public void delete(String path) throws Exception { 54 | client.delete().forPath(path); 55 | } 56 | 57 | public void deleteChildren(String path) throws Exception { 58 | client.delete().deletingChildrenIfNeeded().forPath(path); 59 | } 60 | 61 | public boolean exist(String path) throws Exception { 62 | return null != client.checkExists().forPath(path); 63 | } 64 | 65 | public List getChildren(String path) throws Exception { 66 | return client.getChildren().forPath(path); 67 | } 68 | 69 | /** 70 | * 添加节点监听事件时,会先初始化CHILD_ADDED事件,这里处理成同步阻塞初始化完,再处理后续逻辑 71 | * @param path 72 | * @param pathChildrenCacheListener 73 | */ 74 | public void addChildrenListener(String path, PathChildrenCacheListener pathChildrenCacheListener) { 75 | CuratorCache curatorCache = curatorCacheMap.get(path); 76 | if(null == curatorCache) { 77 | curatorCache = CuratorCache.builder(client, path).build(); 78 | curatorCache.start(); 79 | curatorCacheMap.put(path,curatorCache); 80 | } 81 | 82 | CountDownLatch countDownLatch = new CountDownLatch(1); 83 | CuratorCacheListener cacheListener = CuratorCacheListener.builder().forInitialized(new Runnable() { 84 | @Override 85 | public void run() { 86 | countDownLatch.countDown(); 87 | } 88 | }).forPathChildrenCache(path, client, pathChildrenCacheListener).build(); 89 | curatorCache.listenable().addListener(cacheListener); 90 | 91 | 92 | 93 | try { 94 | countDownLatch.await(); 95 | 96 | //等待初始化结束,先暂定5s吧,超时了就不等了 97 | // countDownLatch.await(5, TimeUnit.SECONDS); 98 | 99 | } catch (InterruptedException e) { 100 | e.printStackTrace(); 101 | } 102 | } 103 | 104 | 105 | public void close() { 106 | curatorCacheMap.values().forEach(CuratorCache::close); 107 | if (client != null) { 108 | this.client.close(); 109 | } 110 | } 111 | } -------------------------------------------------------------------------------- /protocol/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | io.github.liubsyy 8 | shadowrpc 9 | 1.0 10 | 11 | 12 | shadowrpc-protocol 13 | 1.0.0 14 | 15 | 16 | 17 | 18 | io.github.liubsyy 19 | shadowrpc-base 20 | 1.0.0 21 | compile 22 | 23 | 24 | 25 | 26 | com.esotericsoftware 27 | kryo 28 | ${kryo.version} 29 | 30 | 31 | 32 | com.google.protobuf 33 | protobuf-java 34 | ${protobuf.java.version} 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | kr.motd.maven 45 | os-maven-plugin 46 | ${os-maven.version} 47 | 48 | 49 | initialize 50 | 51 | detect 52 | 53 | 54 | 55 | 56 | 57 | 58 | org.xolstice.maven.plugins 59 | protobuf-maven-plugin 60 | ${protobuf-plugin.version} 61 | true 62 | 63 | 64 | false 65 | 66 | com.google.protobuf:protoc:${protobuf.java.version}:exe:${os.detected.classifier} 67 | 68 | 69 | 70 | 71 | compile-main-protos 72 | compile 73 | 74 | compile 75 | 76 | 77 | 78 | src/main/resources/proto 79 | 80 | ${project.build.sourceDirectory}/ 81 | 82 | 83 | 84 | compile-test-protos 85 | test-compile 86 | 87 | compile 88 | 89 | 90 | 91 | src/test/resources/proto 92 | 93 | ${project.build.testSourceDirectory}/ 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /client/src/test/java/rpctest/hello/HelloClient.java: -------------------------------------------------------------------------------- 1 | package rpctest.hello; 2 | 3 | import com.liubs.shadowrpc.base.config.ClientConfig; 4 | import com.liubs.shadowrpc.base.module.ModulePool; 5 | import com.liubs.shadowrpc.client.ClientModule; 6 | import com.liubs.shadowrpc.client.connection.ShadowClient; 7 | import com.liubs.shadowrpc.client.proxy.RemoteServerProxy; 8 | import org.junit.BeforeClass; 9 | import org.junit.Test; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | import rpctest.entity.MyMessage; 13 | 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | import java.util.concurrent.Callable; 17 | import java.util.concurrent.ExecutorService; 18 | import java.util.concurrent.Executors; 19 | 20 | /** 21 | * 这个例子证明了MyMessage新增字段,服务端能正常反序列化,然后服务端缺省了字段客户端也能反序列化 22 | * @author Liubsyy 23 | * @date 2023/12/18 10:59 PM 24 | **/ 25 | 26 | public class HelloClient { 27 | 28 | private static final Logger logger = LoggerFactory.getLogger(HelloClient.class); 29 | 30 | @BeforeClass 31 | public static void setUp() throws Exception { 32 | ModulePool.getModule(ClientModule.class).init(new ClientConfig()); 33 | } 34 | 35 | 36 | /** 37 | * 调用hello方法 38 | */ 39 | @Test 40 | public void helloClient() { 41 | ShadowClient shadowClient = new ShadowClient("127.0.0.1",2023); 42 | shadowClient.init(); 43 | 44 | 45 | IHello helloService = shadowClient.createRemoteProxy(IHello.class,"shadowrpc://DefaultGroup/helloservice"); 46 | 47 | logger.info("发送 hello 消息"); 48 | String helloResponse = helloService.hello("Tom"); 49 | logger.info("hello 服务端响应:"+helloResponse); 50 | 51 | MyMessage message = new MyMessage(); 52 | message.setNum(100); 53 | message.setContent("Hello, Server!"); 54 | 55 | System.out.printf("发送请求 : %s\n",message); 56 | MyMessage response = helloService.say(message); 57 | System.out.printf("接收服务端消息 : %s\n",response); 58 | } 59 | 60 | /** 61 | * 并发调用,测试拆包和粘包的可靠性 62 | */ 63 | @Test 64 | public void helloConcurrent() throws InterruptedException { 65 | ShadowClient shadowClient = new ShadowClient("127.0.0.1",2023); 66 | shadowClient.init(); 67 | 68 | //调用远程RPC接口 69 | IHello helloService = shadowClient.createRemoteProxy(IHello.class,"shadowrpc://DefaultGroup/helloservice"); 70 | 71 | System.out.println("发送 hello 消息"); 72 | String helloResponse = helloService.hello("Tom"); 73 | System.out.println("hello 服务端响应:"+helloResponse); 74 | 75 | long time1 = System.currentTimeMillis(); 76 | List> futureTaskList = new ArrayList<>(); 77 | 78 | //100w个请求,27s 79 | final int n = 1000000; 80 | for(int i = 1;i<=n;i++) { 81 | final int j = i; 82 | futureTaskList.add(() -> { 83 | MyMessage message = new MyMessage(); 84 | message.setNum(j); 85 | message.setContent("Hello, Server!"); 86 | 87 | 88 | //打印消息影响速度,去掉打印至少快一倍 89 | //System.out.printf("发送请求%d \n",j); 90 | MyMessage response = helloService.say(message); 91 | //System.out.printf("接收服务端消息%d \n",j); 92 | 93 | return "success"; 94 | }); 95 | } 96 | 97 | ExecutorService executorService = Executors.newFixedThreadPool(50); 98 | executorService.invokeAll(futureTaskList); 99 | long time2 = System.currentTimeMillis(); 100 | 101 | long useMs = (time2-time1); 102 | System.out.println("总共用时: "+useMs+" ms"); 103 | 104 | executorService.shutdownNow(); 105 | shadowClient.close(); 106 | } 107 | 108 | //测试心跳 109 | @Test 110 | public void testHeartBeat() throws InterruptedException { 111 | ClientConfig config = ModulePool.getModule(ClientModule.class).getConfig(); 112 | config.setHeartBeat(true); 113 | config.setHeartBeatWaitSeconds(3); 114 | 115 | ShadowClient shadowClient = new ShadowClient("127.0.0.1",2023); 116 | shadowClient.init(); 117 | while(true){ 118 | Thread.sleep(1000); 119 | } 120 | } 121 | 122 | 123 | } 124 | -------------------------------------------------------------------------------- /client-mini/src/test/java/rpctest/hello/HelloClient.java: -------------------------------------------------------------------------------- 1 | package rpctest.hello; 2 | 3 | import com.liubs.shadowrpc.clientmini.connection.ShadowClient; 4 | import com.liubs.shadowrpc.clientmini.exception.ConnectTimeoutException; 5 | import com.liubs.shadowrpc.clientmini.exception.RemoteClosedException; 6 | import com.liubs.shadowrpc.clientmini.nio.NIOConfig; 7 | import org.junit.Test; 8 | import rpctest.entity.MyMessage; 9 | 10 | import java.io.IOException; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | import java.util.concurrent.Callable; 14 | import java.util.concurrent.ExecutorService; 15 | import java.util.concurrent.Executors; 16 | 17 | /** 18 | * @author Liubsyy 19 | * @date 2024/1/20 20 | **/ 21 | public class HelloClient { 22 | 23 | /** 24 | * 调用hello方法 25 | */ 26 | @Test 27 | public void helloClient() throws IOException, ConnectTimeoutException { 28 | ShadowClient shadowClient = new ShadowClient("127.0.0.1",2023); 29 | shadowClient.connect(); 30 | 31 | IHello helloService = shadowClient.createRemoteProxy(IHello.class,"helloservice"); 32 | 33 | System.out.println("发送 hello 消息"); 34 | String helloResponse = helloService.hello("Tom"); 35 | System.out.println("hello 服务端响应:"+helloResponse); 36 | 37 | MyMessage message = new MyMessage(); 38 | message.setNum(100); 39 | message.setContent("Hello, Server!"); 40 | 41 | System.out.printf("发送请求 : %s\n",message); 42 | MyMessage response = helloService.say(message); 43 | System.out.printf("接收服务端消息 : %s\n",response); 44 | 45 | shadowClient.close(); 46 | } 47 | 48 | 49 | 50 | /** 51 | * 并发调用,测试拆包和粘包的可靠性 52 | */ 53 | @Test 54 | public void helloConcurrent() throws InterruptedException, IOException, ConnectTimeoutException { 55 | ShadowClient shadowClient = new ShadowClient("127.0.0.1",2023); 56 | shadowClient.connect(); 57 | 58 | //调用远程RPC接口 59 | IHello helloService = shadowClient.createRemoteProxy(IHello.class,"helloservice"); 60 | 61 | System.out.println("发送 hello 消息"); 62 | String helloResponse = helloService.hello("Tom"); 63 | System.out.println("hello 服务端响应:"+helloResponse); 64 | 65 | long time1 = System.currentTimeMillis(); 66 | List> futureTaskList = new ArrayList<>(); 67 | 68 | ExecutorService executorService = Executors.newFixedThreadPool(50); 69 | 70 | //100w个请求,32s 71 | final int n = 1000000; 72 | for(int i = 1;i<=n;i++) { 73 | final int j = i; 74 | futureTaskList.add(() -> { 75 | 76 | try{ 77 | MyMessage message = new MyMessage(); 78 | message.setNum(j); 79 | message.setContent("Hello, Server!"); 80 | 81 | //打印消息影响速度,去掉打印至少快一倍 82 | //System.out.printf("发送请求%d \n",j); 83 | MyMessage response = helloService.say(message); 84 | System.out.printf("接收服务端消息%s \n",response); 85 | }catch (Throwable e) { 86 | e.printStackTrace(); 87 | if(e.getCause() instanceof RemoteClosedException) { 88 | System.exit(1); 89 | } 90 | } 91 | return "success"; 92 | }); 93 | 94 | } 95 | 96 | 97 | executorService.invokeAll(futureTaskList); 98 | long time2 = System.currentTimeMillis(); 99 | 100 | long useMs = (time2-time1); 101 | System.out.println("总共用时: "+useMs+" ms"); 102 | 103 | executorService.shutdownNow(); 104 | shadowClient.close(); 105 | } 106 | 107 | 108 | 109 | //测试心跳 110 | @Test 111 | public void testHeartBeat() throws InterruptedException, IOException, ConnectTimeoutException { 112 | NIOConfig config = new NIOConfig(); 113 | config.setHearBeat(true); 114 | config.setHeartBeatWaitSeconds(1000); 115 | 116 | ShadowClient shadowClient = new ShadowClient("127.0.0.1",2023,config); 117 | shadowClient.connect(); 118 | 119 | while(shadowClient.isRunning()){ 120 | Thread.sleep(1000); 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /client/src/test/java/rpctest/registry/HelloClientGroupProtobuf.java: -------------------------------------------------------------------------------- 1 | package rpctest.registry; 2 | 3 | import com.liubs.shadowrpc.base.config.ClientConfig; 4 | import com.liubs.shadowrpc.base.module.ModulePool; 5 | import com.liubs.shadowrpc.client.ClientModule; 6 | import com.liubs.shadowrpc.client.connection.ShadowClient; 7 | import com.liubs.shadowrpc.client.connection.ShadowClientGroup; 8 | import com.liubs.shadowrpc.protocol.serializer.SerializerStrategy; 9 | import org.junit.Before; 10 | import org.junit.Test; 11 | import rpctest.entity.MyMessageProto; 12 | import rpctest.hello.IHelloProto; 13 | 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | import java.util.concurrent.Callable; 17 | import java.util.concurrent.ExecutorService; 18 | import java.util.concurrent.Executors; 19 | import java.util.stream.Collectors; 20 | 21 | /** 22 | * @author Liubsyy 23 | * @date 2024/1/24 24 | **/ 25 | public class HelloClientGroupProtobuf { 26 | 27 | private static final String ZK_URL = "192.168.1.5:2181"; 28 | private static ClientConfig config; 29 | 30 | @Before 31 | public void init(){ 32 | config = new ClientConfig(); 33 | config.setSerializer(SerializerStrategy.PROTOBUF.name()); 34 | ModulePool.getModule(ClientModule.class).init(config); 35 | } 36 | 37 | //接入注册中心,负载均衡调用rpc接口 38 | @Test 39 | public void connectRegistryForServices() { 40 | ShadowClientGroup shadowClientGroup = new ShadowClientGroup(ZK_URL); 41 | shadowClientGroup.init(); 42 | 43 | IHelloProto helloService = shadowClientGroup.createRemoteProxy(IHelloProto.class, "shadowrpc://MultiNodeGroup/helloprotoservice"); 44 | List shadowClientList = shadowClientGroup.getShadowClients("MultiNodeGroup"); 45 | 46 | System.out.println("所有服务器: "+shadowClientList.stream().map(c-> c.getRemoteIp()+":"+c.getRemotePort()).collect(Collectors.toList())); 47 | 48 | MyMessageProto.MyMessage message = MyMessageProto.MyMessage.newBuilder() 49 | .setNum(100) 50 | .setContent("Hello, Server!") 51 | .build(); 52 | 53 | for(int i = 0 ;i c.getRemoteIp()+":"+c.getRemotePort()).collect(Collectors.toList())); 72 | 73 | MyMessageProto.MyMessage requestMsg = MyMessageProto.MyMessage.newBuilder() 74 | .setNum(100) 75 | .setContent("Hello, Server!") 76 | .build(); 77 | 78 | System.out.printf("发送请求 : %s\n",requestMsg); 79 | MyMessageProto.MyMessage responseMsg = helloProtoService.say(requestMsg); 80 | System.out.printf("接收服务端消息 : %s\n",responseMsg); 81 | 82 | 83 | long time1 = System.currentTimeMillis(); 84 | List> futureTaskList = new ArrayList<>(); 85 | 86 | 87 | //100w个请求,25s 88 | final int n = 1000000; 89 | for(int i = 1;i<=n;i++) { 90 | final int j = i; 91 | futureTaskList.add(() -> { 92 | MyMessageProto.MyMessage message = MyMessageProto.MyMessage.newBuilder().setNum(j).setContent("Hello, Server!").build(); 93 | //打印消息影响速度,去掉打印至少快一倍 94 | MyMessageProto.MyMessage response = helloProtoService.say(message); 95 | return "success"; 96 | }); 97 | } 98 | 99 | ExecutorService executorService = Executors.newFixedThreadPool(50); 100 | executorService.invokeAll(futureTaskList); 101 | long time2 = System.currentTimeMillis(); 102 | 103 | long useMs = (time2-time1); 104 | System.out.println("总共用时: "+useMs+" ms"); 105 | 106 | executorService.shutdownNow(); 107 | 108 | shadowClientGroup.close(); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## ShadowRPC 2 | 一个基于netty的rpc开源框架,简单易用,零配置,可同步和异步调用,不断更新完善中...

3 | 4 | > 精简版点到点全双工通信rpc框架详见[shadowrpc-fly](https://github.com/Liubsyy/shadowrpc-fly),基于本ShadowRPC框架改造 5 | 6 | 7 | ### 主要模块 8 | - protocol : 协议层,包含应用层通信协议,以及序列化/反序列化,支持kryo和protobuf 9 | - registry : 注册模块,基于zookeeper作为注册中心,包含注册服务和服务发现 10 | - server : 服务端 11 | - client : 客户端 12 | - client-mini : 不依赖任何包的客户端,基于NIO 13 | 14 | ### 使用步骤 15 | 16 | ``` 17 | 注: 所有使用案例都在client和server的单测里面 18 | ``` 19 | 20 |
21 | 22 | 1.定义实体, 加上注解 23 | ```java 24 | @ShadowEntity 25 | public class MyMessage { 26 | @ShadowField(1) 27 | private String content; 28 | 29 | @ShadowField(2) 30 | private int num; 31 | } 32 | ``` 33 | 34 | 如果是protobuf方式,编写proto文件 35 | ```proto 36 | message MyMessage { 37 | string content = 1; 38 | int32 num = 2; 39 | } 40 | ``` 41 | 然后直接用maven插件protobuf-maven-plugin生成实体 42 | 43 |
44 | 45 | 2.编写接口加上@ShadowInterface注解 46 | 47 | ```java 48 | @ShadowInterface 49 | public interface IHello { 50 | String hello(String msg); 51 | MyMessage say(MyMessage message); 52 | } 53 | ``` 54 | 55 | 如果采用protobuf作为通信协议,由于可实现跨语言,所以所有函数参数类型和返回类型都必须是proto文件定义的类型 56 | ```java 57 | @ShadowInterface 58 | public interface IHelloProto { 59 | MyMessageProto.MyMessage say(MyMessageProto.MyMessage message); 60 | } 61 | ``` 62 | 63 | 64 | 然后编写服务实现类 65 | ```java 66 | @ShadowService(serviceName = "helloservice") 67 | public class HelloService implements IHello { 68 | @Override 69 | public String hello(String msg) { 70 | return "Hello,"+msg; 71 | } 72 | @Override 73 | public MyMessage say(MyMessage message) { 74 | MyMessage message1 = new MyMessage(); 75 | message1.setContent("hello received "+"("+message.getContent()+")"); 76 | message1.setNum(message.getNum()+1); 77 | return message1; 78 | } 79 | } 80 | ``` 81 | 82 |
83 | 84 | 3.指定序列化类型和端口,启动服务端
85 | 86 | 87 | 单点启动模式如下: 88 | ```java 89 | ServerConfig serverConfig = new ServerConfig(); 90 | serverConfig.setQpsStat(true); //统计qps 91 | serverConfig.setPort(2023); 92 | 93 | ServerBuilder.newBuilder() 94 | .serverConfig(serverConfig) 95 | .addPackage("rpctest.hello") 96 | .build() 97 | .start(); 98 | ``` 99 | 100 | 使用zk作为集群模式启动 101 | ```java 102 | String ZK_URL = "localhost:2181"; 103 | ServerConfig serverConfig = new ServerConfig(); 104 | serverConfig.setGroup("DefaultGroup"); 105 | serverConfig.setPort(2023); 106 | serverConfig.setRegistryUrl(ZK_URL); 107 | serverConfig.setQpsStat(true); //统计qps 108 | serverConfig.setSerializer(SerializerEnum.KRYO.name()); 109 | ServerBuilder.newBuilder() 110 | .serverConfig(serverConfig) 111 | .addPackage("rpctest.hello") 112 | .build() 113 | .start(); 114 | ``` 115 | 116 | 117 | 118 | 4.客户端用调用远程rpc接口 119 | 120 | ```java 121 | ModulePool.getModule(ClientModule.class).init(new ClientConfig()); 122 | 123 | ShadowClient shadowClient = new ShadowClient("127.0.0.1",2023); 124 | shadowClient.init(); 125 | 126 | IHello helloService = shadowClient.createRemoteProxy(IHello.class,"shadowrpc://DefaultGroup/helloservice"); 127 | 128 | logger.info("发送 hello 消息"); 129 | String helloResponse = helloService.hello("Tom"); 130 | logger.info("hello 服务端响应:"+helloResponse); 131 | 132 | MyMessage message = new MyMessage(); 133 | message.setNum(100); 134 | message.setContent("Hello, Server!"); 135 | 136 | System.out.printf("发送请求 : %s\n",message); 137 | MyMessage response = helloService.say(message); 138 | System.out.printf("接收服务端消息 : %s\n",response); 139 | ``` 140 | 141 |
142 | 143 | 使用zk作为服务发现负载均衡调用各个服务器: 144 | ```java 145 | ClientConfig config = new ClientConfig(); 146 | config.setSerializer(SerializerStrategy.KRYO.name()); 147 | ModulePool.getModule(ClientModule.class).init(config); 148 | String ZK_URL="localhost:2181"; 149 | ShadowClientGroup shadowClientGroup = new ShadowClientGroup(ZK_URL); 150 | shadowClientGroup.init(); 151 | 152 | IHello helloService = shadowClientGroup.createRemoteProxy(IHello.class, "shadowrpc://DefaultGroup/helloservice"); 153 | List shadowClientList = shadowClientGroup.getShadowClients("DefaultGroup"); 154 | 155 | System.out.println("所有服务器: "+shadowClientList.stream().map(c-> c.getRemoteIp()+":"+c.getRemotePort()).collect(Collectors.toList())); 156 | 157 | for(int i = 0 ;i