├── .gitignore ├── .idea ├── $CACHE_FILE$ ├── .gitignore ├── compiler.xml ├── encodings.xml ├── inspectionProfiles │ └── Project_Default.xml ├── jarRepositories.xml ├── misc.xml ├── uiDesigner.xml └── vcs.xml ├── Panda-Rpc-Framework.iml ├── README.md ├── pom.xml ├── rpc-api ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── panda │ └── rpc │ └── api │ ├── ByeService.java │ ├── HelloObject.java │ └── HelloService.java ├── rpc-common ├── pom.xml ├── src │ └── main │ │ └── java │ │ └── com │ │ └── panda │ │ └── rpc │ │ ├── entity │ │ ├── RpcRequest.java │ │ └── RpcResponse.java │ │ ├── enumeration │ │ ├── PackageType.java │ │ ├── ResponseCode.java │ │ ├── RpcError.java │ │ └── SerializerCode.java │ │ ├── exception │ │ ├── RpcException.java │ │ └── SerializeException.java │ │ ├── factory │ │ ├── SingletonFactory.java │ │ └── ThreadPoolFactory.java │ │ └── util │ │ ├── NacosUtil.java │ │ ├── ReflectUtil.java │ │ └── RpcMessageChecker.java └── target │ └── classes │ └── META-INF │ └── rpc-common.kotlin_module ├── rpc-core ├── pom.xml ├── src │ └── main │ │ └── java │ │ └── com │ │ └── panda │ │ └── rpc │ │ ├── annotation │ │ ├── Service.java │ │ └── ServiceScan.java │ │ ├── codec │ │ ├── CommonDecoder.java │ │ └── CommonEncoder.java │ │ ├── handler │ │ └── RequestHandler.java │ │ ├── hook │ │ └── ShutdownHook.java │ │ ├── loadbalancer │ │ ├── LoadBalancer.java │ │ ├── RandomLoadBalancer.java │ │ └── RoundRobinLoadBalancer.java │ │ ├── provider │ │ ├── ServiceProvider.java │ │ └── ServiceProviderImpl.java │ │ ├── register │ │ ├── NacosServiceDiscovery.java │ │ ├── NacosServiceRegistry.java │ │ ├── ServiceDiscovery.java │ │ └── ServiceRegistry.java │ │ ├── serializer │ │ ├── CommonSerializer.java │ │ ├── HessianSerializer.java │ │ ├── JsonSerializer.java │ │ ├── KryoSerializer.java │ │ └── ProtostuffSerializer.java │ │ └── transport │ │ ├── AbstractRpcServer.java │ │ ├── RpcClient.java │ │ ├── RpcClientProxy.java │ │ ├── RpcServer.java │ │ ├── netty │ │ ├── client │ │ │ ├── ChannelProvider.java │ │ │ ├── NettyClient.java │ │ │ ├── NettyClientHandler.java │ │ │ └── UnprocessedRequests.java │ │ └── server │ │ │ ├── NettyServer.java │ │ │ └── NettyServerHandler.java │ │ └── socket │ │ ├── client │ │ └── SocketClient.java │ │ ├── server │ │ ├── SocketRequestHandlerThread.java │ │ └── SocketServer.java │ │ └── util │ │ ├── ObjectReader.java │ │ └── ObjectWriter.java └── target │ └── classes │ └── META-INF │ └── rpc-core.kotlin_module ├── rpc.jpg ├── test-client ├── pom.xml ├── src │ └── main │ │ └── java │ │ └── com │ │ └── panda │ │ └── test │ │ ├── NettyTestClient.java │ │ └── SocketTestClient.java └── target │ └── classes │ └── META-INF │ └── test-client.kotlin_module └── test-server ├── pom.xml ├── src └── main │ ├── java │ └── com │ │ └── panda │ │ └── rpc │ │ └── test │ │ ├── ByeServiceImpl.java │ │ ├── HelloServiceImpl.java │ │ ├── NettyTestServer.java │ │ └── SocketTestServer.java │ └── resources │ └── log4j.properties └── target └── classes ├── META-INF └── test-server.kotlin_module └── log4j.properties /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Java template 3 | # Compiled class file 4 | *.class 5 | 6 | # Log file 7 | *.log 8 | 9 | # BlueJ files 10 | *.ctxt 11 | 12 | # Mobile Tools for Java (J2ME) 13 | .mtj.tmp/ 14 | 15 | # Package Files # 16 | *.jar 17 | *.war 18 | *.nar 19 | *.ear 20 | *.zip 21 | *.tar.gz 22 | *.rar 23 | 24 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 25 | hs_err_pid* 26 | 27 | -------------------------------------------------------------------------------- /.idea/$CACHE_FILE$: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | FlexUnit 10 | 11 | 12 | JUnitJava 13 | 14 | 15 | Java 16 | 17 | 18 | 19 | 20 | Ali-Check 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Datasource local storage ignored files 5 | /dataSources/ 6 | /dataSources.local.xml 7 | # Editor-based HTTP Client requests 8 | /httpRequests/ 9 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 36 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.idea/uiDesigner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Panda-Rpc-Framework.iml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Panda-RPC-Framework 2 | 3 | Panda-RPC-Framework 是一款基于 Nacos 实现的 RPC 框架。网络传输实现了基于 Java 原生 Socket 与 Netty 版本,并且实现了多种序列化与负载均衡算法。 4 | 5 | ## 架构 6 | 7 | ![系统架构](./rpc.jpg) 8 | 9 | 消费者调用提供者的方式取决于消费者的客户端选择,如选用原生 Socket 则该步调用使用 BIO,如选用 Netty 方式则该步调用使用 NIO。如该调用有返回值,则提供者向消费者发送返回值的方式同理。 10 | 11 | ## 特性 12 | 13 | - 实现了基于 Java 原生 Socket 传输与 Netty 传输两种网络传输方式 14 | - 实现了四种序列化算法,Json 方式、Kryo 算法、Hessian 算法与 Google Protobuf 方式(默认采用 Kryo方式序列化) 15 | - 实现了两种负载均衡算法:随机算法与轮转算法 16 | - 使用 Nacos 作为注册中心,管理服务提供者信息 17 | - 消费端如采用 Netty 方式,会复用 Channel 避免多次连接 18 | - 如消费端和提供者都采用 Netty 方式,会采用 Netty 的心跳机制,保证连接 19 | - 接口抽象良好,模块耦合度低,网络传输、序列化器、负载均衡算法可配置 20 | - 实现自定义的通信协议 21 | - 服务提供侧自动注册服务 22 | 23 | ## 项目模块概览 24 | 25 | - **roc-api** —— 通用接口 26 | - **rpc-common** —— 实体对象、工具类等公用类 27 | - **rpc-core** —— 框架的核心实现 28 | - **test-client** —— 测试用消费侧 29 | - **test-server** —— 测试用提供侧 30 | 31 | ## 传输协议(MRF协议) 32 | 33 | 调用参数与返回值的传输采用了如下 MRF 协议( My-RPC-Framework 首字母)以防止粘包: 34 | 35 | ``` 36 | +---------------+---------------+-----------------+-------------+ 37 | | Magic Number | Package Type | Serializer Type | Data Length | 38 | | 4 bytes | 4 bytes | 4 bytes | 4 bytes | 39 | +---------------+---------------+-----------------+-------------+ 40 | | Data Bytes | 41 | | Length: ${Data Length} | 42 | +---------------------------------------------------------------+ 43 | ``` 44 | 45 | | 字段 | 解释 | 46 | | :-------------- | :----------------------------------------------------------- | 47 | | Magic Number | 魔数,表识一个 MRF 协议包,0xCAFEBABE | 48 | | Package Type | 包类型,标明这是一个调用请求还是调用响应 | 49 | | Serializer Type | 序列化器类型,标明这个包的数据的序列化方式 | 50 | | Data Length | 数据字节的长度 | 51 | | Data Bytes | 传输的对象,通常是一个`RpcRequest`或`RpcClient`对象,取决于`Package Type`字段,对象的序列化方式取决于`Serializer Type`字段。 | 52 | 53 | ## 使用 54 | 55 | ### 定义调用接口 56 | 57 | ```java 58 | package top.panda.rpc.api; 59 | 60 | public interface HelloService { 61 | String hello(String name); 62 | } 63 | ``` 64 | 65 | ### 在服务提供侧实现该接口 66 | 67 | ```java 68 | package top.panda.test; 69 | 70 | import top.panda.rpc.api.HelloService; 71 | 72 | @Service 73 | public class HelloServiceImpl implements HelloService { 74 | @Override 75 | public String hello(String name) { 76 | return "Hello, " + name; 77 | } 78 | } 79 | ``` 80 | 81 | ### 编写服务提供者 82 | 83 | ```java 84 | package top.panda.test; 85 | 86 | import top.panda.rpc.api.HelloService; 87 | import top.panda.rpc.serializer.CommonSerializer; 88 | import top.panda.rpc.transport.netty.server.NettyServer; 89 | 90 | @ServiceScan 91 | public class NettyTestServer { 92 | public static void main(String[] args) { 93 | NettyServer server = new NettyServer("127.0.0.1", 9999, CommonSerializer.PROTOSTUFF_SERIALIZER); 94 | server.start(); 95 | } 96 | } 97 | ``` 98 | 99 | 这里选用 Netty 传输方式,并且指定序列化方式为 Google Protostuff 方式。 100 | 101 | ### 在服务消费侧远程调用 102 | 103 | ```java 104 | package top.panda.test; 105 | 106 | import top.panda.rpc.api.HelloService; 107 | import top.panda.rpc.serializer.CommonSerializer; 108 | import top.panda.rpc.transport.RpcClient; 109 | import top.panda.rpc.transport.RpcClientProxy; 110 | import top.panda.rpc.transport.netty.client.NettyClient; 111 | 112 | public class NettyTestClient { 113 | 114 | public static void main(String[] args) { 115 | RpcClient client = new NettyClient(CommonSerializer.KRYO_SERIALIZER, new RoundRobinLoadBalancer()); 116 | RpcClientProxy rpcClientProxy = new RpcClientProxy(client); 117 | HelloService helloService = rpcClientProxy.getProxy(HelloService.class); 118 | String res = helloService.hello("panda"); 119 | System.out.println(res); 120 | } 121 | } 122 | ``` 123 | 124 | 这里客户端也选用了 Netty 的传输方式,序列化方式采用 Kryo 方式,负载均衡策略指定为轮转方式。 125 | 126 | ### 启动 127 | 128 | 在此之前请确保 Nacos 运行在本地 `8848` 端口。 129 | 130 | 首先启动服务提供者,再启动消费者,在消费侧会输出`Hello, panda`。 131 | 132 | ## TODO 133 | 134 | - 配置文件 135 | 136 | ## LICENSE 137 | 138 | Panda-RPC-Framework is under the MIT license. See the [LICENSE](https://gitee.com/panda-code/Panda-Rpc-Framework) file for details. 139 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.panda 8 | Panda-Rpc-Framework 9 | 3.0-SNAPSHOT 10 | 11 | pom 12 | 13 | 14 | 15 | rpc-api 16 | rpc-common 17 | rpc-core 18 | test-client 19 | test-server 20 | 21 | 22 | 23 | 1.8 24 | 1.8 25 | UTF-8 26 | 4.1.50.Final 27 | 29.0-jre 28 | 29 | 30 | 31 | 32 | 33 | org.projectlombok 34 | lombok 35 | 1.18.12 36 | 37 | 38 | 39 | org.slf4j 40 | slf4j-api 41 | 1.7.30 42 | 43 | 44 | 45 | org.slf4j 46 | slf4j-simple 47 | 1.7.30 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /rpc-api/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | Panda-Rpc-Framework 7 | com.panda 8 | 3.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | rpc-api 13 | 14 | 15 | -------------------------------------------------------------------------------- /rpc-api/src/main/java/com/panda/rpc/api/ByeService.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.api; 2 | 3 | /** 4 | * @author [PANDA] 1843047930@qq.com 5 | * @date [2021-03-16 12:47] 6 | * @description 7 | */ 8 | public interface ByeService { 9 | 10 | String bye(String name); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /rpc-api/src/main/java/com/panda/rpc/api/HelloObject.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.api; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.io.Serializable; 8 | 9 | /** 10 | * @author [PANDA] 1843047930@qq.com 11 | * @date [2021-02-03 14:15] 12 | * @description 定义客户端传给服务端的接口的参数 13 | */ 14 | 15 | //自动加上所有属性的get set toString hashCode equals方法 16 | @Data 17 | @NoArgsConstructor 18 | //添加一个含有所有已声明字段属性参数的构造函数 19 | @AllArgsConstructor 20 | 21 | public class HelloObject implements Serializable { 22 | //Serializable序列化接口没有任何方法或者字段,只是用于标识可序列化的语义。 23 | //实现了Serializable接口的类可以被ObjectOutputStream转换为字节流。 24 | //同时也可以通过ObjectInputStream再将其解析为对象。 25 | //序列化是指把对象转换为字节序列的过程;反序列化则是把持久化的字节文件数据恢复为对象的过程。 26 | 27 | private Integer id; 28 | private String message; 29 | 30 | } 31 | -------------------------------------------------------------------------------- /rpc-api/src/main/java/com/panda/rpc/api/HelloService.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.api; 2 | 3 | /** 4 | * @author [PANDA] 1843047930@qq.com 5 | * @date [2021-02-03 12:44] 6 | * @description 通用接口 7 | */ 8 | public interface HelloService { 9 | /** 10 | * @description 接口方法 11 | * @param [object] 12 | * @return [java.lang.String] 13 | * @date [2021-02-03 15:28] 14 | */ 15 | String hello(HelloObject object); 16 | } 17 | -------------------------------------------------------------------------------- /rpc-common/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | Panda-Rpc-Framework 7 | com.panda 8 | 3.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | rpc-common 13 | 14 | 15 | 16 | com.google.guava 17 | guava 18 | ${guava.version} 19 | 20 | 21 | com.alibaba.nacos 22 | nacos-client 23 | 1.3.0 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /rpc-common/src/main/java/com/panda/rpc/entity/RpcRequest.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.entity; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.io.Serializable; 8 | 9 | /** 10 | * @author [PANDA] 1843047930@qq.com 11 | * @date [2021-02-03 16:16] 12 | * @description 传输格式(传输协议):客户端向服务端传输的对象 13 | */ 14 | @Data 15 | @NoArgsConstructor 16 | @AllArgsConstructor 17 | public class RpcRequest implements Serializable { 18 | 19 | /** 20 | * 请求号 21 | */ 22 | private String requestId; 23 | /** 24 | * 待调用接口名称 25 | */ 26 | private String interfaceName; 27 | /** 28 | * 待调用方法名称 29 | */ 30 | private String methodName; 31 | /** 32 | * 待调用方法的参数 33 | */ 34 | private Object[] parameters; 35 | /** 36 | * 待调用方法的参数类型 37 | */ 38 | private Class[] paramTypes; 39 | /** 40 | * 是否是心跳包 41 | */ 42 | private Boolean heartBeat; 43 | } 44 | -------------------------------------------------------------------------------- /rpc-common/src/main/java/com/panda/rpc/entity/RpcResponse.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.entity; 2 | 3 | import com.panda.rpc.enumeration.ResponseCode; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.io.Serializable; 8 | 9 | /** 10 | * @author [PANDA] 1843047930@qq.com 11 | * @date [2021-02-03 17:20] 12 | * @description 服务端处理完后,向客户端返回的对象 13 | */ 14 | @Data 15 | @NoArgsConstructor 16 | public class RpcResponse implements Serializable { 17 | 18 | /** 19 | * 响应对应的请求号 20 | */ 21 | private String requestId; 22 | /** 23 | *响应状态码 24 | */ 25 | private Integer statusCode; 26 | /** 27 | *响应状态码对应的信息 28 | */ 29 | private String message; 30 | /** 31 | *成功时的响应数据 32 | */ 33 | private T data; 34 | 35 | /** 36 | * @description 成功时服务端返回的对象 37 | * @param [data] 38 | * @return [com.panda.rpc.entity.RpcResponse] 39 | * @date [2021-02-03 17:31] 40 | */ 41 | public static RpcResponse success(T data, String requestId){ 42 | RpcResponse response = new RpcResponse<>(); 43 | response.setRequestId(requestId); 44 | response.setStatusCode(ResponseCode.SUCCESS.getCode()); 45 | response.setData(data); 46 | return response; 47 | } 48 | 49 | /** 50 | * @description 失败时服务端返回的对象 51 | * @param [code] 52 | * @return [com.panda.rpc.entity.RpcResponse] 53 | * @date [2021-02-03 17:42] 54 | */ 55 | public static RpcResponse fail(ResponseCode code, String requestId){ 56 | RpcResponse response = new RpcResponse<>(); 57 | response.setRequestId(requestId); 58 | response.setStatusCode(code.getCode()); 59 | response.setMessage(code.getMessage()); 60 | return response; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /rpc-common/src/main/java/com/panda/rpc/enumeration/PackageType.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.enumeration; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | /** 7 | * @author [PANDA] 1843047930@qq.com 8 | * @date [2021-02-18 18:25] 9 | * @description 10 | */ 11 | @Getter 12 | @AllArgsConstructor 13 | public enum PackageType { 14 | 15 | REQUEST_PACK(0), 16 | RESPONSE_PACK(1); 17 | 18 | private final int code; 19 | } 20 | -------------------------------------------------------------------------------- /rpc-common/src/main/java/com/panda/rpc/enumeration/ResponseCode.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.enumeration; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | /** 7 | * @author [PANDA] 1843047930@qq.com 8 | * @date [2021-02-03 17:38] 9 | * @description 响应状态码对象 10 | */ 11 | @AllArgsConstructor 12 | @Getter 13 | public enum ResponseCode { 14 | 15 | SUCCESS(200, "调用方法成功"), 16 | FAIL(500, "调用方法失败"), 17 | METHOD_NOT_FOUND(500, "未找到指定方法"), 18 | ClASS_NOT_FOUND(500, "未找到指定类"); 19 | 20 | private final int code; 21 | private final String message; 22 | } 23 | -------------------------------------------------------------------------------- /rpc-common/src/main/java/com/panda/rpc/enumeration/RpcError.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.enumeration; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | /** 7 | * @author [PANDA] 1843047930@qq.com 8 | * @date [2021-02-07 18:48] 9 | * @description Rpc调用过程中出现的错误 10 | */ 11 | @AllArgsConstructor 12 | @Getter 13 | public enum RpcError { 14 | 15 | UNKNOWN_ERROR("出现未知错误"), 16 | SERVICE_SCAN_PACKAGE_NOT_FOUND("启动类ServiceScan注解缺失"), 17 | CLIENT_CONNECT_SERVER_FAILURE("客户端连接服务端失败"), 18 | SERVICE_INVOCATION_FAILURE("服务调用出现失败"), 19 | SERVICE_NOT_FOUND("找不到对应的服务"), 20 | SERVICE_NOT_IMPLEMENT_ANY_INTERFACE("注册的服务未实现接口"), 21 | UNKNOWN_PROTOCOL("不识别的协议包"), 22 | UNKNOWN_SERIALIZER("不识别的(反)序列化器"), 23 | UNKNOWN_PACKAGE_TYPE("不识别的数据包类型"), 24 | SERIALIZER_NOT_FOUND("找不到序列化器"), 25 | RESPONSE_NOT_MATCH("响应与请求号不匹配"), 26 | FAILED_TO_CONNECT_TO_SERVICE_REGISTRY("连接注册中心失败"), 27 | REGISTER_SERVICE_FAILED("注册服务失败"); 28 | 29 | 30 | private final String message; 31 | } 32 | -------------------------------------------------------------------------------- /rpc-common/src/main/java/com/panda/rpc/enumeration/SerializerCode.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.enumeration; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | /** 7 | * @author [PANDA] 1843047930@qq.com 8 | * @date [2021-02-22 15:03] 9 | * @description 字节流中标识序列化和反序列化器 10 | */ 11 | @AllArgsConstructor 12 | @Getter 13 | public enum SerializerCode { 14 | 15 | KRYO(0), 16 | JSON(1), 17 | HESSIAN(2), 18 | PROTOBUF(3); 19 | 20 | private final int code; 21 | } 22 | -------------------------------------------------------------------------------- /rpc-common/src/main/java/com/panda/rpc/exception/RpcException.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.exception; 2 | 3 | import com.panda.rpc.enumeration.RpcError; 4 | 5 | /** 6 | * @author [PANDA] 1843047930@qq.com 7 | * @date [2021-02-07 18:51] 8 | * @description Rpc调用异常 9 | */ 10 | public class RpcException extends RuntimeException{ 11 | 12 | public RpcException(RpcError error, String detail){ 13 | super(error.getMessage() + ":" + detail); 14 | } 15 | 16 | public RpcException(String message, Throwable cause){ 17 | super(message, cause); 18 | } 19 | 20 | public RpcException(RpcError error){ 21 | super(error.getMessage()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /rpc-common/src/main/java/com/panda/rpc/exception/SerializeException.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.exception; 2 | 3 | /** 4 | * @author [PANDA] 1843047930@qq.com 5 | * @date [2021-03-09 17:18] 6 | * @description 序列化异常 7 | */ 8 | public class SerializeException extends RuntimeException{ 9 | public SerializeException(String msg){ 10 | super(msg); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /rpc-common/src/main/java/com/panda/rpc/factory/SingletonFactory.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.factory; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * @author [PANDA] 1843047930@qq.com 8 | * @date [2021-03-14 11:14] 9 | * @description 单例工厂,保证一个类只有一个实例,节约资源,保证线程安全 10 | */ 11 | public class SingletonFactory { 12 | 13 | private static Map objectMap = new HashMap<>(); 14 | 15 | private SingletonFactory(){} 16 | 17 | public static T getInstance(Class clazz) { 18 | Object instance = objectMap.get(clazz); 19 | //锁住类保证线程安全 20 | synchronized (clazz){ 21 | if(instance == null){ 22 | try { 23 | instance = clazz.newInstance(); 24 | objectMap.put(clazz, instance); 25 | }catch (IllegalAccessException | InstantiationException e) { 26 | throw new RuntimeException(e.getMessage(), e); 27 | } 28 | } 29 | } 30 | return clazz.cast(instance); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /rpc-common/src/main/java/com/panda/rpc/factory/ThreadPoolFactory.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.factory; 2 | 3 | import com.google.common.util.concurrent.ThreadFactoryBuilder; 4 | import lombok.NoArgsConstructor; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | import java.util.Map; 9 | import java.util.concurrent.*; 10 | 11 | /** 12 | * @author [PANDA] 1843047930@qq.com 13 | * @date [2021-03-10 17:12] 14 | * @description 创建ThreadPool(线程池)工具类 15 | */ 16 | @NoArgsConstructor 17 | public class ThreadPoolFactory { 18 | /** 19 | * 线程池参数 20 | */ 21 | private static final int CORE_POOL_SIZE = 10; 22 | private static final int MAXIMUM_POOL_SIZE = 100; 23 | private static final int KEEP_ALIVE_TIME = 1; 24 | private static final int BLOCKING_QUEUE_CAPACITY = 100; 25 | 26 | private static final Logger logger = LoggerFactory.getLogger(ThreadPoolFactory.class); 27 | /** 28 | * 利用Map管理多个线程池 29 | */ 30 | private static Map threadPoolMap = new ConcurrentHashMap<>(); 31 | 32 | public static ExecutorService createDefaultThreadPool(String threadNamePrefix){ 33 | return createDefaultThreadPool(threadNamePrefix, false); 34 | } 35 | 36 | public static ExecutorService createDefaultThreadPool(String threadNamePrefix, Boolean daemon){ 37 | //computeIfAbsent():如果key对应的value存在,则直接返回value,如果不存在则使用第二个参数(函数)计算的值作为value返回,并保存为该key的value 38 | ExecutorService pool = threadPoolMap.computeIfAbsent(threadNamePrefix, k -> createThreadPool(threadNamePrefix, daemon)); 39 | //isShutdown():当调用shutdown()或shutdownNow()方法后返回为true 40 | //isTerminated():当调用shutdown()方法后,并且所有提交的任务完成后返回为true;当调用shutdownNow()方法后,成功停止后返回为true; 41 | if(pool.isShutdown() || pool.isTerminated()){ 42 | threadPoolMap.remove(threadNamePrefix); 43 | //重新构建一个线程池并存入Map中 44 | pool = createThreadPool(threadNamePrefix, daemon); 45 | threadPoolMap.put(threadNamePrefix, pool); 46 | } 47 | return pool; 48 | } 49 | 50 | public static void shutDownAll(){ 51 | logger.info("关闭所有线程池……"); 52 | //利用parallelStream()并行关闭所有线程池 53 | threadPoolMap.entrySet().parallelStream().forEach(entry -> { 54 | ExecutorService executorService = entry.getValue(); 55 | executorService.shutdown(); 56 | logger.info("关闭线程池 [{}] [{}]", entry.getKey(), executorService.isTerminated()); 57 | try { 58 | //阻塞直到关闭请求后所有任务执行完,或者发生超时,或者当前线程被中断(以先发生者为准)。 59 | executorService.awaitTermination(10, TimeUnit.SECONDS); 60 | }catch (InterruptedException e){ 61 | logger.error("关闭线程池失败"); 62 | //直接关闭不等任务执行完了 63 | executorService.shutdownNow(); 64 | } 65 | }); 66 | } 67 | 68 | private static ExecutorService createThreadPool(String threadNamePrefix, Boolean daemon){ 69 | /** 70 | * 设置上限为100个线程的阻塞队列 71 | */ 72 | BlockingQueue workQueue = new ArrayBlockingQueue<>(BLOCKING_QUEUE_CAPACITY); 73 | ThreadFactory threadFactory = createThreadFactory(threadNamePrefix, daemon); 74 | //创建线程池 75 | return new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_TIME, TimeUnit.MINUTES, workQueue, threadFactory); 76 | } 77 | 78 | /** 79 | * @description 创建ThreadFactory,如果threadNamePrefix不为空则使用自建ThreadFactory,否则使用defaultThreadFactory 80 | * @param threadNamePrefix 作为创建的线程名字的前缀,指定有意义的线程名称,方便出错时回溯 81 | * @param daemon 指定是否为Daemon Thread(守护线程),当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程 82 | * @return [java.util.concurrent.ThreadFactory] 83 | * @date [2021-03-10 17:50] 84 | */ 85 | private static ThreadFactory createThreadFactory(String threadNamePrefix, Boolean daemon) { 86 | if (threadNamePrefix != null) { 87 | if (daemon != null) { 88 | //利用guava中的ThreadFactoryBuilder自定义创建线程工厂 89 | return new ThreadFactoryBuilder().setNameFormat(threadNamePrefix + "-%d").setDaemon(daemon).build(); 90 | } else { 91 | return new ThreadFactoryBuilder().setNameFormat(threadNamePrefix + "-%d").build(); 92 | } 93 | } 94 | return Executors.defaultThreadFactory(); 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /rpc-common/src/main/java/com/panda/rpc/util/NacosUtil.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.util; 2 | 3 | import com.alibaba.nacos.api.exception.NacosException; 4 | import com.alibaba.nacos.api.naming.NamingFactory; 5 | import com.alibaba.nacos.api.naming.NamingService; 6 | import com.alibaba.nacos.api.naming.pojo.Instance; 7 | import com.panda.rpc.enumeration.RpcError; 8 | import com.panda.rpc.exception.RpcException; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | import java.net.InetSocketAddress; 13 | import java.util.HashSet; 14 | import java.util.Iterator; 15 | import java.util.List; 16 | import java.util.Set; 17 | 18 | /** 19 | * @author [PANDA] 1843047930@qq.com 20 | * @date [2021-03-14 9:24] 21 | * @description 管理Nacos连接等工具类 22 | */ 23 | public class NacosUtil { 24 | 25 | private static final Logger logger = LoggerFactory.getLogger(NacosUtil.class); 26 | 27 | private static final NamingService namingService; 28 | private static final Set serviceNames = new HashSet<>(); 29 | private static InetSocketAddress address; 30 | private static final String SERVER_ADDR = "127.0.0.1:8848"; 31 | 32 | static { 33 | namingService = getNacosNamingService(); 34 | } 35 | 36 | /** 37 | * @description 连接到Nacos创建命名空间 38 | * @param [] 39 | * @return [com.alibaba.nacos.api.naming.NamingService] 40 | * @date [2021-03-14 9:33] 41 | */ 42 | public static NamingService getNacosNamingService() { 43 | try { 44 | return NamingFactory.createNamingService(SERVER_ADDR); 45 | }catch (NacosException e) { 46 | logger.error("连接到Nacos时有错误发生:", e); 47 | throw new RpcException(RpcError.FAILED_TO_CONNECT_TO_SERVICE_REGISTRY); 48 | } 49 | } 50 | 51 | /** 52 | * @description 注册服务到Nacos 53 | * @param [namingService, serviceName, inetSocketAddress] 54 | * @return [void] 55 | * @date [2021-03-14 9:34] 56 | */ 57 | public static void registerService(String serviceName, InetSocketAddress address) throws NacosException { 58 | namingService.registerInstance(serviceName, address.getHostName(), address.getPort()); 59 | NacosUtil.address = address; 60 | //保存注册的服务名 61 | serviceNames.add(serviceName); 62 | } 63 | 64 | /** 65 | * @description 获取所有提供该服务的服务端地址 66 | * @param [namingService, serviceName] 67 | * @return [java.util.List] 68 | * @date [2021-03-14 9:39] 69 | */ 70 | public static List getAllInstance(String serviceName) throws NacosException { 71 | return namingService.getAllInstances(serviceName); 72 | } 73 | 74 | /** 75 | * @description 注销服务 76 | * @param [] 77 | * @return [void] 78 | * @date [2021-03-14 13:37] 79 | */ 80 | public static void clearRegistry() { 81 | if(!serviceNames.isEmpty() && address != null) { 82 | String host = address.getHostName(); 83 | int port = address.getPort(); 84 | //利用迭代器迭代注销 85 | Iterator iterator = serviceNames.iterator(); 86 | while (iterator.hasNext()) { 87 | String serviceName = iterator.next(); 88 | try { 89 | //注销服务 90 | namingService.deregisterInstance(serviceName, host, port); 91 | }catch (NacosException e) { 92 | logger.error("注销服务{}失败", serviceName, e); 93 | } 94 | } 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /rpc-common/src/main/java/com/panda/rpc/util/ReflectUtil.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.util; 2 | 3 | import java.io.File; 4 | import java.io.FileFilter; 5 | import java.io.IOException; 6 | import java.net.JarURLConnection; 7 | import java.net.URL; 8 | import java.net.URLDecoder; 9 | import java.util.Enumeration; 10 | import java.util.LinkedHashSet; 11 | import java.util.Set; 12 | import java.util.jar.JarEntry; 13 | import java.util.jar.JarFile; 14 | 15 | /** 16 | * @author [PANDA] 1843047930@qq.com 17 | * @date [2021-03-16 10:32] 18 | * @description 扫描包及其子包下所有的类,并将其Class对象放入一个Set中返回 19 | */ 20 | public class ReflectUtil { 21 | 22 | //从栈底获取main()方法所在的启动类 23 | public static String getStackTrace() { 24 | StackTraceElement[] stack = new Throwable().getStackTrace(); 25 | return stack[stack.length - 1].getClassName(); 26 | } 27 | 28 | public static Set> getClasses(String packageName) { 29 | Set> classes = new LinkedHashSet<>(); 30 | boolean recursive = true; 31 | String packageDirName = packageName.replace('.', '/'); 32 | Enumeration dirs; 33 | try { 34 | dirs = Thread.currentThread().getContextClassLoader().getResources( 35 | packageDirName); 36 | // 循环迭代下去 37 | while (dirs.hasMoreElements()) { 38 | // 获取下一个元素 39 | URL url = dirs.nextElement(); 40 | // 得到协议的名称 41 | String protocol = url.getProtocol(); 42 | // 如果是以文件的形式保存在服务器上 43 | if ("file".equals(protocol)) { 44 | // 获取包的物理路径 45 | String filePath = URLDecoder.decode(url.getFile(), "UTF-8"); 46 | // 以文件的方式扫描整个包下的文件 并添加到集合中 47 | findAndAddClassesInPackageByFile(packageName, filePath, 48 | recursive, classes); 49 | } else if ("jar".equals(protocol)) { 50 | // 如果是jar包文件 51 | // 定义一个JarFile 52 | JarFile jar; 53 | try { 54 | // 获取jar 55 | jar = ((JarURLConnection) url.openConnection()) 56 | .getJarFile(); 57 | // 从此jar包 得到一个枚举类 58 | Enumeration entries = jar.entries(); 59 | // 同样的进行循环迭代 60 | while (entries.hasMoreElements()) { 61 | // 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件 62 | JarEntry entry = entries.nextElement(); 63 | String name = entry.getName(); 64 | // 如果是以/开头的 65 | if (name.charAt(0) == '/') { 66 | // 获取后面的字符串 67 | name = name.substring(1); 68 | } 69 | // 如果前半部分和定义的包名相同 70 | if (name.startsWith(packageDirName)) { 71 | int idx = name.lastIndexOf('/'); 72 | // 如果以"/"结尾 是一个包 73 | if (idx != -1) { 74 | // 获取包名 把"/"替换成"." 75 | packageName = name.substring(0, idx) 76 | .replace('/', '.'); 77 | } 78 | // 如果可以迭代下去 并且是一个包 79 | if ((idx != -1) || recursive) { 80 | // 如果是一个.class文件 而且不是目录 81 | if (name.endsWith(".class") 82 | && !entry.isDirectory()) { 83 | // 去掉后面的".class" 获取真正的类名 84 | String className = name.substring( 85 | packageName.length() + 1, name 86 | .length() - 6); 87 | try { 88 | // 添加到classes 89 | classes.add(Class 90 | .forName(packageName + '.' 91 | + className)); 92 | } catch (ClassNotFoundException e) { 93 | // log 94 | // .error("添加用户自定义视图类错误 找不到此类的.class文件") 95 | e.printStackTrace(); 96 | } 97 | } 98 | } 99 | } 100 | } 101 | } catch (IOException e) { 102 | // log.error("在扫描用户定义视图时从jar包获取文件出错") 103 | e.printStackTrace(); 104 | } 105 | } 106 | } 107 | } catch (IOException e) { 108 | e.printStackTrace(); 109 | } 110 | return classes; 111 | } 112 | 113 | private static void findAndAddClassesInPackageByFile(String packageName, 114 | String packagePath, final boolean recursive, Set> classes) { 115 | // 获取此包的目录 建立一个File 116 | File dir = new File(packagePath); 117 | // 如果不存在或者 也不是目录就直接返回 118 | if (!dir.exists() || !dir.isDirectory()) { 119 | // log.warn("用户定义包名 " + packageName + " 下没有任何文件") 120 | return; 121 | } 122 | // 如果存在 就获取包下的所有文件 包括目录 123 | File[] dirfiles = dir.listFiles(new FileFilter() { 124 | // 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件) 125 | @Override 126 | public boolean accept(File file) { 127 | return (recursive && file.isDirectory()) 128 | || (file.getName().endsWith(".class")); 129 | } 130 | }); 131 | // 循环所有文件 132 | for (File file : dirfiles) { 133 | // 如果是目录 则继续扫描 134 | if (file.isDirectory()) { 135 | findAndAddClassesInPackageByFile(packageName + "." 136 | + file.getName(), file.getAbsolutePath(), recursive, 137 | classes); 138 | } else { 139 | // 如果是java类文件 去掉后面的.class 只留下类名 140 | String className = file.getName().substring(0, 141 | file.getName().length() - 6); 142 | try { 143 | // 添加到集合中去 144 | //classes.add(Class.forName(packageName + '.' + className)) 145 | //这里用forName有一些不好,会触发static方法,没有使用classLoader的load干净 146 | classes.add(Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className)); 147 | } catch (ClassNotFoundException e) { 148 | // log.error("添加用户自定义视图类错误 找不到此类的.class文件") 149 | e.printStackTrace(); 150 | } 151 | } 152 | } 153 | } 154 | 155 | } 156 | 157 | -------------------------------------------------------------------------------- /rpc-common/src/main/java/com/panda/rpc/util/RpcMessageChecker.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.util; 2 | 3 | import com.panda.rpc.entity.RpcRequest; 4 | import com.panda.rpc.entity.RpcResponse; 5 | import com.panda.rpc.enumeration.ResponseCode; 6 | import com.panda.rpc.enumeration.RpcError; 7 | import com.panda.rpc.exception.RpcException; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | /** 12 | * @author [PANDA] 1843047930@qq.com 13 | * @date [2021-03-11 11:12] 14 | * @description 检查响应和请求 15 | */ 16 | public class RpcMessageChecker { 17 | 18 | private static final String INTERFACE_NAME = "interfaceName"; 19 | private static final Logger logger = LoggerFactory.getLogger(RpcMessageChecker.class); 20 | 21 | private RpcMessageChecker(){ 22 | } 23 | 24 | public static void check(RpcRequest rpcRequest, RpcResponse rpcResponse){ 25 | if(rpcResponse == null) { 26 | logger.error("调用服务失败,serviceName:{}", rpcRequest.getInterfaceName()); 27 | throw new RpcException(RpcError.SERVICE_INVOCATION_FAILURE, INTERFACE_NAME + ":" + rpcRequest.getInterfaceName()); 28 | } 29 | //响应与请求的请求号不同 30 | if(!rpcRequest.getRequestId().equals(rpcResponse.getRequestId())) { 31 | throw new RpcException(RpcError.RESPONSE_NOT_MATCH, INTERFACE_NAME + ":" + rpcRequest.getInterfaceName()); 32 | } 33 | //调用失败 34 | if(rpcResponse.getStatusCode() == null || !rpcResponse.getStatusCode().equals(ResponseCode.SUCCESS.getCode())){ 35 | logger.error("调用服务失败,serviceName:{},RpcResponse:{}", rpcRequest.getInterfaceName(), rpcResponse); 36 | throw new RpcException(RpcError.SERVICE_INVOCATION_FAILURE, INTERFACE_NAME + ":" + rpcRequest.getInterfaceName()); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /rpc-common/target/classes/META-INF/rpc-common.kotlin_module: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /rpc-core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | Panda-Rpc-Framework 7 | com.panda 8 | 3.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | rpc-core 13 | 14 | 15 | 16 | com.panda 17 | rpc-common 18 | ${project.version} 19 | 20 | 21 | io.netty 22 | netty-all 23 | ${netty-version} 24 | 25 | 26 | com.fasterxml.jackson.core 27 | jackson-core 28 | 2.11.0 29 | 30 | 31 | com.fasterxml.jackson.core 32 | jackson-databind 33 | 2.11.0 34 | 35 | 36 | com.fasterxml.jackson.core 37 | jackson-annotations 38 | 2.11.0 39 | 40 | 41 | com.esotericsoftware 42 | kryo 43 | 4.0.2 44 | 45 | 46 | com.caucho 47 | hessian 48 | 4.0.63 49 | 50 | 51 | io.protostuff 52 | protostuff-core 53 | 1.7.2 54 | 55 | 56 | io.protostuff 57 | protostuff-runtime 58 | 1.7.2 59 | 60 | 61 | com.alibaba.nacos 62 | nacos-client 63 | 1.3.0 64 | 65 | 66 | -------------------------------------------------------------------------------- /rpc-core/src/main/java/com/panda/rpc/annotation/Service.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.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 [PANDA] 1843047930@qq.com 10 | * @date [2021-03-16 9:58] 11 | * @description 用来标识一个服务提供类,注解放在接口的实现类上 12 | */ 13 | //表示注解的作用目标为接口、类、枚举类型 14 | @Target(ElementType.TYPE) 15 | //表示在运行时可以动态获取注解信息 16 | @Retention(RetentionPolicy.RUNTIME) 17 | public @interface Service { 18 | 19 | public String name() default ""; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /rpc-core/src/main/java/com/panda/rpc/annotation/ServiceScan.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.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 [PANDA] 1843047930@qq.com 10 | * @date [2021-03-16 10:07] 11 | * @description 标识服务的扫描的包的范围,即扫描范围的根包 12 | */ 13 | @Target(ElementType.TYPE) 14 | @Retention(RetentionPolicy.RUNTIME) 15 | public @interface ServiceScan { 16 | 17 | public String value() default ""; 18 | 19 | } 20 | -------------------------------------------------------------------------------- /rpc-core/src/main/java/com/panda/rpc/codec/CommonDecoder.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.codec; 2 | 3 | import com.panda.rpc.entity.RpcRequest; 4 | import com.panda.rpc.entity.RpcResponse; 5 | import com.panda.rpc.enumeration.PackageType; 6 | import com.panda.rpc.enumeration.RpcError; 7 | import com.panda.rpc.exception.RpcException; 8 | import com.panda.rpc.serializer.CommonSerializer; 9 | import io.netty.buffer.ByteBuf; 10 | import io.netty.channel.ChannelHandlerContext; 11 | import io.netty.handler.codec.ReplayingDecoder; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | import java.util.List; 16 | 17 | /** 18 | * @author [PANDA] 1843047930@qq.com 19 | * @date [2021-02-22 12:35] 20 | * @description 通用的解码拦截器 21 | */ 22 | public class CommonDecoder extends ReplayingDecoder { 23 | 24 | private static final Logger logger = LoggerFactory.getLogger(CommonDecoder.class); 25 | private static final int MAGIC_NUMBER = 0xCAFEBABE; 26 | 27 | @Override 28 | protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { 29 | //从缓冲区中读取数据 30 | int magic = in.readInt(); 31 | if(magic != MAGIC_NUMBER){ 32 | logger.error("不识别的协议包:{}", magic); 33 | throw new RpcException(RpcError.UNKNOWN_PROTOCOL); 34 | } 35 | int packageCode = in.readInt(); 36 | Class packageClass; 37 | if(packageCode == PackageType.REQUEST_PACK.getCode()){ 38 | packageClass = RpcRequest.class; 39 | }else if (packageCode == PackageType.RESPONSE_PACK.getCode()){ 40 | packageClass = RpcResponse.class; 41 | }else { 42 | logger.error("不识别的数据包:{}", packageCode); 43 | throw new RpcException(RpcError.UNKNOWN_PACKAGE_TYPE); 44 | } 45 | int serializerCode = in.readInt(); 46 | CommonSerializer serializer = CommonSerializer.getByCode(serializerCode); 47 | if(serializer == null){ 48 | logger.error("不识别的反序列化器:{}", serializerCode); 49 | throw new RpcException(RpcError.UNKNOWN_SERIALIZER); 50 | } 51 | int length = in.readInt(); 52 | byte[] bytes = new byte[length]; 53 | in.readBytes(bytes); 54 | Object obj = serializer.deserialize(bytes, packageClass); 55 | //添加到对象列表 56 | out.add(obj); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /rpc-core/src/main/java/com/panda/rpc/codec/CommonEncoder.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.codec; 2 | 3 | import com.panda.rpc.entity.RpcRequest; 4 | import com.panda.rpc.enumeration.PackageType; 5 | import com.panda.rpc.serializer.CommonSerializer; 6 | import io.netty.buffer.ByteBuf; 7 | import io.netty.channel.ChannelHandlerContext; 8 | import io.netty.handler.codec.MessageToByteEncoder; 9 | 10 | /** 11 | * @author [PANDA] 1843047930@qq.com 12 | * @date [2021-02-22 12:20] 13 | * @description 通用编码拦截器 14 | */ 15 | public class CommonEncoder extends MessageToByteEncoder { 16 | 17 | private static final int MAGIC_NUMBER = 0xCAFEBABE; 18 | 19 | private final CommonSerializer serializer; 20 | 21 | public CommonEncoder(CommonSerializer serializer){ 22 | this.serializer = serializer; 23 | } 24 | 25 | @Override 26 | protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception { 27 | //数据写到缓冲区 28 | out.writeInt(MAGIC_NUMBER); 29 | if(msg instanceof RpcRequest){ 30 | out.writeInt(PackageType.REQUEST_PACK.getCode()); 31 | }else { 32 | out.writeInt(PackageType.RESPONSE_PACK.getCode()); 33 | } 34 | out.writeInt(serializer.getCode()); 35 | byte[] bytes = serializer.serialize(msg); 36 | out.writeInt(bytes.length); 37 | out.writeBytes(bytes); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /rpc-core/src/main/java/com/panda/rpc/handler/RequestHandler.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.handler; 2 | 3 | import com.panda.rpc.entity.RpcRequest; 4 | import com.panda.rpc.entity.RpcResponse; 5 | import com.panda.rpc.enumeration.ResponseCode; 6 | import com.panda.rpc.provider.ServiceProvider; 7 | import com.panda.rpc.provider.ServiceProviderImpl; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import java.lang.reflect.InvocationTargetException; 12 | import java.lang.reflect.Method; 13 | 14 | /** 15 | * @author [PANDA] 1843047930@qq.com 16 | * @date [2021-02-05 12:13] 17 | * @description 实际执行方法调用的处理器 18 | */ 19 | public class RequestHandler{ 20 | 21 | private static final Logger logger = LoggerFactory.getLogger(RequestHandler.class); 22 | private static final ServiceProvider serviceProvider; 23 | 24 | static { 25 | serviceProvider = new ServiceProviderImpl(); 26 | } 27 | 28 | public Object handle(RpcRequest rpcRequest){ 29 | //从服务端本地注册表中获取服务实体 30 | Object service = serviceProvider.getServiceProvider(rpcRequest.getInterfaceName()); 31 | return invokeTargetMethod(rpcRequest, service); 32 | } 33 | 34 | private Object invokeTargetMethod(RpcRequest rpcRequest,Object service) { 35 | Object result; 36 | try{ 37 | //getClass()获取的是实例对象的类型 38 | Method method = service.getClass().getMethod(rpcRequest.getMethodName(), rpcRequest.getParamTypes()); 39 | result = method.invoke(service, rpcRequest.getParameters()); 40 | logger.info("服务:{}成功调用方法:{}", rpcRequest.getInterfaceName(), rpcRequest.getMethodName()); 41 | }catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e){ 42 | //方法调用失败 43 | return RpcResponse.fail(ResponseCode.METHOD_NOT_FOUND, rpcRequest.getRequestId()); 44 | } 45 | //方法调用成功 46 | return RpcResponse.success(result, rpcRequest.getRequestId()); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /rpc-core/src/main/java/com/panda/rpc/hook/ShutdownHook.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.hook; 2 | 3 | import com.panda.rpc.factory.ThreadPoolFactory; 4 | import com.panda.rpc.util.NacosUtil; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | /** 9 | * @author [PANDA] 1843047930@qq.com 10 | * @date [2021-03-14 13:45] 11 | * @description 12 | */ 13 | public class ShutdownHook { 14 | 15 | private static final Logger logger = LoggerFactory.getLogger(ShutdownHook.class); 16 | 17 | /** 18 | *单例模式创建钩子,保证全局只有这一个钩子 19 | */ 20 | private static final ShutdownHook shutdownHook = new ShutdownHook(); 21 | 22 | public static ShutdownHook getShutdownHook(){ 23 | return shutdownHook; 24 | } 25 | 26 | //注销服务的钩子 27 | public void addClearAllHook() { 28 | logger.info("服务端关闭前将自动注销所有服务"); 29 | Runtime.getRuntime().addShutdownHook(new Thread(() -> { 30 | NacosUtil.clearRegistry(); 31 | //关闭所有线程池 32 | ThreadPoolFactory.shutDownAll(); 33 | })); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /rpc-core/src/main/java/com/panda/rpc/loadbalancer/LoadBalancer.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.loadbalancer; 2 | 3 | import com.alibaba.nacos.api.naming.pojo.Instance; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * @author [PANDA] 1843047930@qq.com 9 | * @date [2021-03-15 15:53] 10 | * @description 负载均衡接口 11 | */ 12 | public interface LoadBalancer { 13 | 14 | /** 15 | * @description 从一系列Instance中选择一个 16 | * @param [instances] 17 | * @return [com.alibaba.nacos.api.naming.pojo.Instance] 18 | * @date [2021-03-15 16:00] 19 | */ 20 | Instance select(List instances); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /rpc-core/src/main/java/com/panda/rpc/loadbalancer/RandomLoadBalancer.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.loadbalancer; 2 | 3 | import com.alibaba.nacos.api.naming.pojo.Instance; 4 | 5 | import java.util.List; 6 | import java.util.Random; 7 | 8 | /** 9 | * @author [PANDA] 1843047930@qq.com 10 | * @date [2021-03-15 15:59] 11 | * @description 随机选择一个 12 | */ 13 | public class RandomLoadBalancer implements LoadBalancer{ 14 | 15 | @Override 16 | public Instance select(List instances) { 17 | return instances.get(new Random().nextInt(instances.size())); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /rpc-core/src/main/java/com/panda/rpc/loadbalancer/RoundRobinLoadBalancer.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.loadbalancer; 2 | 3 | import com.alibaba.nacos.api.naming.pojo.Instance; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * @author [PANDA] 1843047930@qq.com 9 | * @date [2021-03-15 16:05] 10 | * @description 轮转算法,按顺序来 11 | */ 12 | public class RoundRobinLoadBalancer implements LoadBalancer{ 13 | 14 | /** 15 | * index表示当前选到了第几个服务器,并且每次选择后都会自增一 16 | */ 17 | private int index = 0; 18 | 19 | @Override 20 | public Instance select(List instances) { 21 | if(index >= instances.size()){ 22 | index %= instances.size(); 23 | } 24 | return instances.get(index++); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /rpc-core/src/main/java/com/panda/rpc/provider/ServiceProvider.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.provider; 2 | 3 | /** 4 | * @author [PANDA] 1843047930@qq.com 5 | * @date [2021-03-13 14:35] 6 | * @description 保存和提供服务实例对象 7 | */ 8 | public interface ServiceProvider { 9 | 10 | void addServiceProvider(T service, String serviceName); 11 | 12 | Object getServiceProvider(String serviceName); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /rpc-core/src/main/java/com/panda/rpc/provider/ServiceProviderImpl.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.provider; 2 | 3 | import com.panda.rpc.enumeration.RpcError; 4 | import com.panda.rpc.exception.RpcException; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | import java.util.Map; 9 | import java.util.Set; 10 | import java.util.concurrent.ConcurrentHashMap; 11 | 12 | /** 13 | * @author [PANDA] 1843047930@qq.com 14 | * @date [2021-02-07 17:14] 15 | * @description 默认的服务注册表,保存服务端本地服务 16 | */ 17 | public class ServiceProviderImpl implements ServiceProvider { 18 | 19 | private static final Logger logger = LoggerFactory.getLogger(ServiceProviderImpl.class); 20 | /** 21 | * key = 服务名称(即接口名), value = 服务实体(即实现类的实例对象) 22 | */ 23 | private static final Map serviceMap = new ConcurrentHashMap<>(); 24 | 25 | /** 26 | * 用来存放服务名称(即接口名) 27 | */ 28 | private static final Set registeredService = ConcurrentHashMap.newKeySet(); 29 | 30 | /** 31 | * @description 保存服务到本地服务注册表 32 | * @param [service, serviceClass] 服务的实现类对象,服务类(接口) 33 | * @return [void] 34 | * @date [2021-03-14 9:51] 35 | */ 36 | @Override 37 | public void addServiceProvider(T service, String serviceName) { 38 | if(registeredService.contains(serviceName)){ 39 | return; 40 | } 41 | registeredService.add(serviceName); 42 | serviceMap.put(serviceName, service); 43 | logger.info("向接口:{} 注册服务:{}", service.getClass().getInterfaces(), serviceName); 44 | } 45 | 46 | @Override 47 | public Object getServiceProvider(String serviceName) { 48 | Object service = serviceMap.get(serviceName); 49 | if(service == null){ 50 | throw new RpcException(RpcError.SERVICE_NOT_FOUND); 51 | } 52 | return service; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /rpc-core/src/main/java/com/panda/rpc/register/NacosServiceDiscovery.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.register; 2 | 3 | import com.alibaba.nacos.api.exception.NacosException; 4 | import com.alibaba.nacos.api.naming.pojo.Instance; 5 | import com.panda.rpc.enumeration.RpcError; 6 | import com.panda.rpc.exception.RpcException; 7 | import com.panda.rpc.loadbalancer.LoadBalancer; 8 | import com.panda.rpc.loadbalancer.RandomLoadBalancer; 9 | import com.panda.rpc.util.NacosUtil; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | import java.net.InetSocketAddress; 14 | import java.util.List; 15 | 16 | /** 17 | * @author [PANDA] 1843047930@qq.com 18 | * @date [2021-03-14 9:54] 19 | * @description 服务发现 20 | */ 21 | public class NacosServiceDiscovery implements ServiceDiscovery{ 22 | 23 | private static final Logger logger = LoggerFactory.getLogger(NacosServiceDiscovery.class); 24 | 25 | private final LoadBalancer loadBalancer; 26 | 27 | public NacosServiceDiscovery(LoadBalancer loadBalancer){ 28 | if (loadBalancer == null){ 29 | this.loadBalancer = new RandomLoadBalancer(); 30 | }else { 31 | this.loadBalancer = loadBalancer; 32 | } 33 | } 34 | /** 35 | * @description 根据服务名称从注册中心获取到一个服务提供者的地址 36 | * @param [serviceName] 37 | * @return [java.net.InetSocketAddress] 38 | * @date [2021-03-14 10:19] 39 | */ 40 | @Override 41 | public InetSocketAddress lookupService(String serviceName) { 42 | try { 43 | //利用列表获取某个服务的所有提供者 44 | List instances = NacosUtil.getAllInstance(serviceName); 45 | if(instances.size() == 0){ 46 | logger.error("找不到对应服务:" + serviceName); 47 | throw new RpcException(RpcError.SERVICE_NOT_FOUND); 48 | } 49 | //负载均衡获取一个服务实体 50 | Instance instance = loadBalancer.select(instances); 51 | return new InetSocketAddress(instance.getIp(), instance.getPort()); 52 | }catch (NacosException e) { 53 | logger.error("获取服务时有错误发生", e); 54 | } 55 | return null; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /rpc-core/src/main/java/com/panda/rpc/register/NacosServiceRegistry.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.register; 2 | 3 | import com.alibaba.nacos.api.exception.NacosException; 4 | import com.panda.rpc.enumeration.RpcError; 5 | import com.panda.rpc.exception.RpcException; 6 | import com.panda.rpc.util.NacosUtil; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.net.InetSocketAddress; 11 | 12 | /** 13 | * @author [PANDA] 1843047930@qq.com 14 | * @date [2021-03-13 15:05] 15 | * @description Nacos服务注册中心 16 | */ 17 | public class NacosServiceRegistry implements ServiceRegistry{ 18 | 19 | private static final Logger logger = LoggerFactory.getLogger(NacosServiceRegistry.class); 20 | 21 | /** 22 | * @description 将服务的名称和地址注册进服务注册中心 23 | * @param [serviceName, inetSocketAddress] 24 | * @return [void] 25 | * @date [2021-03-13 15:40] 26 | */ 27 | @Override 28 | public void register(String serviceName, InetSocketAddress inetSocketAddress) { 29 | try { 30 | //向Nacos注册服务 31 | NacosUtil.registerService(serviceName, inetSocketAddress); 32 | }catch (NacosException e) { 33 | logger.error("注册服务时有错误发生" + e); 34 | throw new RpcException(RpcError.REGISTER_SERVICE_FAILED); 35 | } 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /rpc-core/src/main/java/com/panda/rpc/register/ServiceDiscovery.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.register; 2 | 3 | import java.net.InetSocketAddress; 4 | 5 | /** 6 | * @author [PANDA] 1843047930@qq.com 7 | * @date [2021-03-14 9:56] 8 | * @description 服务发现接口 9 | */ 10 | public interface ServiceDiscovery { 11 | /** 12 | * @description 根据服务名称查找服务端地址 13 | * @param [serviceName] 14 | * @return [java.net.InetSocketAddress] 15 | * @date [2021-03-14 10:09] 16 | */ 17 | InetSocketAddress lookupService(String serviceName); 18 | } 19 | -------------------------------------------------------------------------------- /rpc-core/src/main/java/com/panda/rpc/register/ServiceRegistry.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.register; 2 | 3 | import java.net.InetSocketAddress; 4 | 5 | /** 6 | * @author [PANDA] 1843047930@qq.com 7 | * @date [2021-02-07 16:55] 8 | * @description 服务注册接口 9 | */ 10 | public interface ServiceRegistry { 11 | 12 | /** 13 | * @description 将一个服务注册到注册表 14 | * @param [ServiceName, inetSocketAddress] 服务名称,提供服务的地址 15 | * @return [void] 16 | * @date [2021-03-13 14:44] 17 | */ 18 | void register(String serviceName, InetSocketAddress inetSocketAddress); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /rpc-core/src/main/java/com/panda/rpc/serializer/CommonSerializer.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.serializer; 2 | 3 | /** 4 | * @author [PANDA] 1843047930@qq.com 5 | * @date [2021-02-22 14:18] 6 | * @description 通用的序列化反序列化接口 7 | */ 8 | public interface CommonSerializer { 9 | 10 | Integer KRYO_SERIALIZER = 0; 11 | Integer JSON_SERIALIZER = 1; 12 | Integer HESSIAN_SERIALIZER = 2; 13 | Integer PROTOBUF_SERIALIZER = 3; 14 | 15 | Integer DEFAULT_SERIALIZER = KRYO_SERIALIZER; 16 | 17 | static CommonSerializer getByCode(int code){ 18 | switch (code){ 19 | case 0: 20 | return new KryoSerializer(); 21 | case 1: 22 | return new JsonSerializer(); 23 | case 2: 24 | return new HessianSerializer(); 25 | case 3: 26 | return new ProtostuffSerializer(); 27 | default: 28 | return null; 29 | } 30 | } 31 | 32 | byte[] serialize(Object obj); 33 | 34 | Object deserialize(byte[] bytes, Class clazz); 35 | 36 | int getCode(); 37 | 38 | } 39 | -------------------------------------------------------------------------------- /rpc-core/src/main/java/com/panda/rpc/serializer/HessianSerializer.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.serializer; 2 | 3 | import com.caucho.hessian.io.HessianInput; 4 | import com.caucho.hessian.io.HessianOutput; 5 | import com.panda.rpc.enumeration.SerializerCode; 6 | import com.panda.rpc.exception.SerializeException; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.io.ByteArrayInputStream; 11 | import java.io.ByteArrayOutputStream; 12 | import java.io.IOException; 13 | 14 | /** 15 | * @author [PANDA] 1843047930@qq.com 16 | * @date [2021-03-10 10:24] 17 | * @description 基于Hessian协议的序列化器 18 | */ 19 | public class HessianSerializer implements CommonSerializer{ 20 | 21 | private static final Logger logger = LoggerFactory.getLogger(HessianSerializer.class); 22 | 23 | @Override 24 | public byte[] serialize(Object obj) { 25 | HessianOutput hessianOutput = null; 26 | try(ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()){ 27 | hessianOutput = new HessianOutput(byteArrayOutputStream); 28 | hessianOutput.writeObject(obj); 29 | return byteArrayOutputStream.toByteArray(); 30 | }catch (IOException e){ 31 | logger.error("序列化时有错误发生" + e); 32 | throw new SerializeException("序列化时有错误发生"); 33 | }finally { 34 | if(hessianOutput != null){ 35 | try { 36 | hessianOutput.close(); 37 | }catch (IOException e){ 38 | logger.error("关闭output流时有错误发生" + e); 39 | } 40 | } 41 | } 42 | } 43 | 44 | @Override 45 | public Object deserialize(byte[] bytes, Class clazz) { 46 | HessianInput hessianInput = null; 47 | try(ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes)){ 48 | hessianInput = new HessianInput(byteArrayInputStream); 49 | return hessianInput.readObject(); 50 | }catch (IOException e){ 51 | logger.error("反序列化时有错误发生" + e); 52 | throw new SerializeException("反序列化时有错误发生"); 53 | }finally { 54 | if(hessianInput != null) { 55 | hessianInput.close(); 56 | } 57 | } 58 | } 59 | 60 | @Override 61 | public int getCode() { 62 | return SerializerCode.valueOf("HESSIAN").getCode(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /rpc-core/src/main/java/com/panda/rpc/serializer/JsonSerializer.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.serializer; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.panda.rpc.entity.RpcRequest; 6 | import com.panda.rpc.enumeration.SerializerCode; 7 | import com.panda.rpc.exception.SerializeException; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import java.io.IOException; 12 | 13 | /** 14 | * @author [PANDA] 1843047930@qq.com 15 | * @date [2021-02-22 14:33] 16 | * @description 使用Json格式的序列化器 17 | */ 18 | public class JsonSerializer implements CommonSerializer{ 19 | 20 | private static final Logger logger = LoggerFactory.getLogger(JsonSerializer.class); 21 | //ObjectMapper支持线程安全 22 | private ObjectMapper objectMapper = new ObjectMapper(); 23 | 24 | @Override 25 | public byte[] serialize(Object obj) { 26 | try{ 27 | return objectMapper.writeValueAsBytes(obj); 28 | }catch (JsonProcessingException e){ 29 | logger.error("序列化时有错误发生:{}", e.getMessage()); 30 | throw new SerializeException("序列化时有错误发生"); 31 | } 32 | } 33 | 34 | @Override 35 | public Object deserialize(byte[] bytes, Class clazz) { 36 | try{ 37 | Object obj = objectMapper.readValue(bytes, clazz); 38 | if(obj instanceof RpcRequest){ 39 | obj = handleRequest(obj); 40 | } 41 | return obj; 42 | }catch (IOException e){ 43 | logger.error("反序列化时有错误发生:{}", e.getMessage()); 44 | throw new SerializeException("反序列化时有错误发生"); 45 | } 46 | } 47 | 48 | /** 49 | * @description 使用JSON反序列化Object数组,无法保证反序列化后仍然为原实例类,通常直接被反序列化成String类型,因此要特殊处理 50 | * @param [obj] 51 | * @return [java.lang.Object] 52 | * @date [2021-02-22 15:03] 53 | */ 54 | private Object handleRequest(Object obj) throws IOException{ 55 | RpcRequest rpcRequest = (RpcRequest) obj; 56 | for(int i = 0; i < rpcRequest.getParamTypes().length; i++){ 57 | Class clazz = rpcRequest.getParamTypes()[i]; 58 | if(!clazz.isAssignableFrom(rpcRequest.getParameters()[i].getClass())){ 59 | byte[] bytes = objectMapper.writeValueAsBytes(rpcRequest.getParameters()[i]); 60 | rpcRequest.getParameters()[i] = objectMapper.readValue(bytes, clazz); 61 | } 62 | } 63 | return rpcRequest; 64 | } 65 | 66 | @Override 67 | public int getCode() { 68 | return SerializerCode.valueOf("JSON").getCode(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /rpc-core/src/main/java/com/panda/rpc/serializer/KryoSerializer.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.serializer; 2 | 3 | import com.esotericsoftware.kryo.Kryo; 4 | import com.esotericsoftware.kryo.io.Input; 5 | import com.esotericsoftware.kryo.io.Output; 6 | import com.panda.rpc.entity.RpcRequest; 7 | import com.panda.rpc.entity.RpcResponse; 8 | import com.panda.rpc.enumeration.SerializerCode; 9 | import com.panda.rpc.exception.SerializeException; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | import java.io.ByteArrayInputStream; 14 | import java.io.ByteArrayOutputStream; 15 | 16 | /** 17 | * @author [PANDA] 1843047930@qq.com 18 | * @date [2021-03-09 17:27] 19 | * @description Kryo序列化器 20 | */ 21 | public class KryoSerializer implements CommonSerializer{ 22 | 23 | private static final Logger logger = LoggerFactory.getLogger(KryoSerializer.class); 24 | 25 | //使用ThreadLocal初始化Kryo,因为Kryo中的output和input是线程不安全的 26 | private static final ThreadLocal kryoThreadLocal = ThreadLocal.withInitial(() -> { 27 | Kryo kryo = new Kryo(); 28 | //注册类 29 | kryo.register(RpcResponse.class); 30 | kryo.register(RpcRequest.class); 31 | //循环引用检测,默认为true 32 | kryo.setReferences(true); 33 | //不强制要求注册类,默认为false,若设置为true则要求涉及到的所有类都要注册,包括jdk中的比如Object 34 | kryo.setRegistrationRequired(false); 35 | return kryo; 36 | }); 37 | 38 | @Override 39 | public byte[] serialize(Object obj) { 40 | try(ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 41 | Output output = new Output(byteArrayOutputStream)){ 42 | Kryo kryo = kryoThreadLocal.get(); 43 | kryo.writeObject(output, obj); 44 | kryoThreadLocal.remove(); 45 | return output.toBytes(); 46 | }catch (Exception e){ 47 | logger.error("序列化时有错误发生:" + e); 48 | throw new SerializeException("序列化时有错误发生"); 49 | } 50 | } 51 | 52 | @Override 53 | public Object deserialize(byte[] bytes, Class clazz) { 54 | try(ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); 55 | Input input = new Input(byteArrayInputStream)){ 56 | Kryo kryo = kryoThreadLocal.get(); 57 | Object o = kryo.readObject(input, clazz); 58 | kryoThreadLocal.remove(); 59 | return o; 60 | }catch (Exception e){ 61 | logger.error("反序列化时有错误发生:" + e); 62 | throw new SerializeException("反序列化时有错误发生"); 63 | } 64 | } 65 | 66 | @Override 67 | public int getCode() { 68 | return SerializerCode.valueOf("KRYO").getCode(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /rpc-core/src/main/java/com/panda/rpc/serializer/ProtostuffSerializer.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.serializer; 2 | 3 | import com.panda.rpc.enumeration.SerializerCode; 4 | import io.protostuff.LinkedBuffer; 5 | import io.protostuff.ProtostuffIOUtil; 6 | import io.protostuff.Schema; 7 | import io.protostuff.runtime.RuntimeSchema; 8 | 9 | import java.util.Map; 10 | import java.util.Objects; 11 | import java.util.concurrent.ConcurrentHashMap; 12 | 13 | /** 14 | * @author [PANDA] 1843047930@qq.com 15 | * @date [2021-03-11 18:42] 16 | * @description Protostuff序列化器 17 | */ 18 | public class ProtostuffSerializer implements CommonSerializer { 19 | 20 | /** 21 | * 避免每次序列化都重新申请Buffer空间,用来存放对象序列化之后的数据 22 | * 如果你设置的空间不足,会自动扩展的,但这个大小还是要设置一个合适的值,设置大了浪费空间,设置小了会自动扩展浪费时间 23 | */ 24 | private LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE); 25 | 26 | /** 27 | * 缓存类对应的Schema,由于构造schema需要获得对象的类和字段信息,会用到反射机制 28 | *这是一个很耗时的过程,因此进行缓存很有必要,下次遇到相同的类直接从缓存中get就行了 29 | */ 30 | private Map, Schema> schemaCache = new ConcurrentHashMap<>(); 31 | 32 | @Override 33 | @SuppressWarnings("unchecked") 34 | public byte[] serialize(Object obj) { 35 | Class clazz = obj.getClass(); 36 | Schema schema = getSchema(clazz); 37 | byte[] data; 38 | try { 39 | //序列化操作,将对象转换为字节数组 40 | data = ProtostuffIOUtil.toByteArray(obj, schema, buffer); 41 | } finally { 42 | //使用完清空buffer 43 | buffer.clear(); 44 | } 45 | return data; 46 | } 47 | 48 | @Override 49 | @SuppressWarnings("unchecked") 50 | public Object deserialize(byte[] bytes, Class clazz) { 51 | Schema schema = getSchema(clazz); 52 | Object obj = schema.newMessage(); 53 | //反序列化操作,将字节数组转换为对应的对象 54 | ProtostuffIOUtil.mergeFrom(bytes, obj, schema); 55 | return obj; 56 | } 57 | 58 | @Override 59 | public int getCode() { 60 | return SerializerCode.valueOf("PROTOBUF").getCode(); 61 | } 62 | 63 | /** 64 | * @description 获取Schema 65 | * @param [clazz] 66 | * @return [io.protostuff.Schema] 67 | * @date [2021-03-11 21:38] 68 | */ 69 | @SuppressWarnings("unchecked") 70 | private Schema getSchema(Class clazz) { 71 | //首先尝试从Map缓存中获取类对应的schema 72 | Schema schema = schemaCache.get(clazz); 73 | if(Objects.isNull(schema)) { 74 | //新创建一个schema,RuntimeSchema就是将schema繁琐的创建过程封装了起来 75 | //它的创建过程是线程安全的,采用懒创建的方式,即当需要schema的时候才创建 76 | schema = RuntimeSchema.getSchema(clazz); 77 | if(Objects.nonNull(schema)) { 78 | //缓存schema,方便下次直接使用 79 | schemaCache.put(clazz, schema); 80 | } 81 | } 82 | return schema; 83 | } 84 | } -------------------------------------------------------------------------------- /rpc-core/src/main/java/com/panda/rpc/transport/AbstractRpcServer.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.transport; 2 | 3 | import com.panda.rpc.annotation.Service; 4 | import com.panda.rpc.annotation.ServiceScan; 5 | import com.panda.rpc.enumeration.RpcError; 6 | import com.panda.rpc.exception.RpcException; 7 | import com.panda.rpc.provider.ServiceProvider; 8 | import com.panda.rpc.register.ServiceRegistry; 9 | import com.panda.rpc.util.ReflectUtil; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | import java.net.InetSocketAddress; 14 | import java.util.Set; 15 | 16 | /** 17 | * @author [PANDA] 1843047930@qq.com 18 | * @date [2021-03-16 10:43] 19 | * @description 扫描服务类并进行服务注册 20 | */ 21 | public abstract class AbstractRpcServer implements RpcServer{ 22 | 23 | protected Logger logger = LoggerFactory.getLogger(AbstractRpcServer.class); 24 | 25 | protected String host; 26 | protected int port; 27 | 28 | protected ServiceRegistry serviceRegistry; 29 | protected ServiceProvider serviceProvider; 30 | 31 | public void scanServices(){ 32 | //获取main()入口所在类的类名,即启动类 33 | String mainClassName = ReflectUtil.getStackTrace(); 34 | Class startClass; 35 | try { 36 | //获取启动类对应的实例对象 37 | startClass = Class.forName(mainClassName); 38 | //判断启动类是否存在ServiceScan注解 39 | if(!startClass.isAnnotationPresent(ServiceScan.class)){ 40 | logger.error("启动类缺少@ServiceScan注解"); 41 | throw new RpcException(RpcError.SERVICE_SCAN_PACKAGE_NOT_FOUND); 42 | } 43 | }catch (ClassNotFoundException e){ 44 | logger.info("出现未知错误"); 45 | throw new RpcException(RpcError.UNKNOWN_ERROR); 46 | } 47 | //获取ServiceScan注解接口对应value()的值,默认设置的“” 48 | String basePackage = startClass.getAnnotation(ServiceScan.class).value(); 49 | if("".equals(basePackage)){ 50 | //获取启动类所在的包,因为服务类也放在这个包下面的 51 | basePackage = mainClassName.substring(0, mainClassName.lastIndexOf(".")); 52 | } 53 | //获取包下面的所有类Class对象 54 | Set> classSet = ReflectUtil.getClasses(basePackage); 55 | for(Class clazz : classSet){ 56 | //利用Service注解判断该类是否为服务类 57 | if(clazz.isAnnotationPresent(Service.class)){ 58 | //获取Service注解接口对应name()的值,默认设置的“” 59 | String serviceName = clazz.getAnnotation(Service.class).name(); 60 | Object obj; 61 | try{ 62 | //创建服务Impl类的实例 63 | obj = clazz.newInstance(); 64 | }catch (IllegalAccessException | InstantiationException e){ 65 | logger.error("创建" + clazz + "时有错误发生"); 66 | continue; 67 | } 68 | if("".equals(serviceName)){ 69 | //一个服务Impl类可能实现了多个服务接口 70 | Class[] interfaces = clazz.getInterfaces(); 71 | for(Class oneInterface : interfaces){ 72 | publishService(obj, oneInterface.getCanonicalName()); 73 | } 74 | }else { 75 | publishService(obj, serviceName); 76 | } 77 | } 78 | } 79 | } 80 | 81 | /** 82 | * @description 将服务保存在本地的注册表,同时注册到Nacos 83 | * @param [service, serviceName] 84 | * @return [void] 85 | * @date [2021-03-16 12:32] 86 | */ 87 | @Override 88 | public void publishService(T service, String serviceName){ 89 | serviceProvider.addServiceProvider(service, serviceName); 90 | serviceRegistry.register(serviceName, new InetSocketAddress(host, port)); 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /rpc-core/src/main/java/com/panda/rpc/transport/RpcClient.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.transport; 2 | 3 | import com.panda.rpc.entity.RpcRequest; 4 | import com.panda.rpc.serializer.CommonSerializer; 5 | 6 | /** 7 | * @author [PANDA] 1843047930@qq.com 8 | * @date [2021-02-18 17:50] 9 | * @description 客户端类通用接口 10 | */ 11 | public interface RpcClient { 12 | 13 | int DEFAULT_SERIALIZER = CommonSerializer.KRYO_SERIALIZER; 14 | 15 | Object sendRequest(RpcRequest rpcRequest); 16 | 17 | } 18 | 19 | -------------------------------------------------------------------------------- /rpc-core/src/main/java/com/panda/rpc/transport/RpcClientProxy.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.transport; 2 | 3 | import com.panda.rpc.entity.RpcRequest; 4 | import com.panda.rpc.entity.RpcResponse; 5 | import com.panda.rpc.transport.netty.client.NettyClient; 6 | import com.panda.rpc.transport.socket.client.SocketClient; 7 | import com.panda.rpc.util.RpcMessageChecker; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import java.lang.reflect.InvocationHandler; 12 | import java.lang.reflect.Method; 13 | import java.lang.reflect.Proxy; 14 | import java.util.UUID; 15 | import java.util.concurrent.CompletableFuture; 16 | 17 | /** 18 | * @author [PANDA] 1843047930@qq.com 19 | * @date [2021-02-04 16:46] 20 | * @description Rpc客户端动态代理 21 | */ 22 | public class RpcClientProxy implements InvocationHandler { 23 | 24 | private static final Logger logger = LoggerFactory.getLogger(RpcClientProxy.class); 25 | private final RpcClient client; 26 | 27 | public RpcClientProxy(RpcClient client) { 28 | this.client = client; 29 | } 30 | 31 | //抑制编译器产生警告信息 32 | @SuppressWarnings("unchecked") 33 | public T getProxy(Class clazz){ 34 | //创建代理对象 35 | return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, this); 36 | } 37 | 38 | @SuppressWarnings("unchecked") 39 | @Override 40 | public Object invoke(Object proxy, Method method, Object[] args) { 41 | logger.info("调用方法:{}#{}", method.getDeclaringClass().getName(), method.getName()); 42 | RpcRequest rpcRequest = new RpcRequest(UUID.randomUUID().toString(), method.getDeclaringClass().getName(), 43 | method.getName(), args, method.getParameterTypes(), false); 44 | RpcResponse rpcResponse = null; 45 | if(client instanceof NettyClient){ 46 | try { 47 | //异步获取调用结果 48 | CompletableFuture completableFuture = (CompletableFuture)client.sendRequest(rpcRequest); 49 | rpcResponse = completableFuture.get(); 50 | }catch (Exception e){ 51 | logger.error("方法调用请求发送失败", e); 52 | return null; 53 | } 54 | } 55 | if(client instanceof SocketClient){ 56 | rpcResponse = (RpcResponse) client.sendRequest(rpcRequest); 57 | } 58 | RpcMessageChecker.check(rpcRequest, rpcResponse); 59 | return rpcResponse.getData(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /rpc-core/src/main/java/com/panda/rpc/transport/RpcServer.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.transport; 2 | 3 | import com.panda.rpc.serializer.CommonSerializer; 4 | 5 | /** 6 | * @author [PANDA] 1843047930@qq.com 7 | * @date [2021-02-18 17:53] 8 | * @description 服务端类通过接口 9 | */ 10 | public interface RpcServer { 11 | 12 | int DEFAULT_SERIALIZER = CommonSerializer.KRYO_SERIALIZER; 13 | 14 | void start(); 15 | 16 | /** 17 | * @description 向Nacos注册服务 18 | * @param [service, serviceClass] 19 | * @return [void] 20 | * @date [2021-03-13 15:56] 21 | */ 22 | void publishService(T service, String serviceName); 23 | } 24 | -------------------------------------------------------------------------------- /rpc-core/src/main/java/com/panda/rpc/transport/netty/client/ChannelProvider.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.transport.netty.client; 2 | 3 | import com.panda.rpc.codec.CommonDecoder; 4 | import com.panda.rpc.codec.CommonEncoder; 5 | import com.panda.rpc.serializer.CommonSerializer; 6 | import io.netty.bootstrap.Bootstrap; 7 | import io.netty.channel.*; 8 | import io.netty.channel.nio.NioEventLoopGroup; 9 | import io.netty.channel.socket.SocketChannel; 10 | import io.netty.channel.socket.nio.NioSocketChannel; 11 | import io.netty.handler.timeout.IdleStateHandler; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | import java.net.InetSocketAddress; 16 | import java.util.Map; 17 | import java.util.concurrent.CompletableFuture; 18 | import java.util.concurrent.ConcurrentHashMap; 19 | import java.util.concurrent.ExecutionException; 20 | import java.util.concurrent.TimeUnit; 21 | 22 | /** 23 | * @author [PANDA] 1843047930@qq.com 24 | * @date [2021-03-11 12:20] 25 | * @description 用于获取Channel对象 26 | */ 27 | public class ChannelProvider { 28 | 29 | private static final Logger logger = LoggerFactory.getLogger(ChannelProvider.class); 30 | private static EventLoopGroup eventLoopGroup; 31 | private static Bootstrap bootstrap = initializeBootstrap(); 32 | /** 33 | * 所有客户端Channel都保存在该Map中 34 | */ 35 | private static Map channels = new ConcurrentHashMap<>(); 36 | 37 | private static Bootstrap initializeBootstrap() { 38 | eventLoopGroup = new NioEventLoopGroup(); 39 | Bootstrap bootstrap = new Bootstrap(); 40 | bootstrap.group(eventLoopGroup) 41 | .channel(NioSocketChannel.class) 42 | //连接的超时时间,超过这个时间还是建立不上的话则代表连接失败 43 | .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000) 44 | //启用该功能时,TCP会主动探测空闲连接的有效性。可以将此功能视为TCP的心跳机制,默认的心跳间隔是7200s即2小时。 45 | .option(ChannelOption.SO_KEEPALIVE, true) 46 | //配置Channel参数,nodelay没有延迟,true就代表禁用Nagle算法,减小传输延迟。理解可参考:https://blog.csdn.net/lclwjl/article/details/80154565 47 | .option(ChannelOption.TCP_NODELAY, true); 48 | return bootstrap; 49 | } 50 | 51 | public static Channel get(InetSocketAddress inetSocketAddress, CommonSerializer serializer){ 52 | String key = inetSocketAddress.toString() + serializer.getCode(); 53 | if(channels.containsKey(key)){ 54 | Channel channel = channels.get(key); 55 | if(channel != null && channel.isActive()){ 56 | return channel; 57 | }else { 58 | channels.remove(key); 59 | } 60 | } 61 | bootstrap.handler(new ChannelInitializer() { 62 | @Override 63 | protected void initChannel(SocketChannel ch){ 64 | ch.pipeline().addLast(new CommonEncoder(serializer)) 65 | //设定IdleStateHandler心跳检测每5秒进行一次写检测,如果5秒内write()方法未被调用则触发一次userEventTrigger()方法 66 | //实现客户端每5秒向服务端发送一次消息 67 | .addLast(new IdleStateHandler(0, 5, 0, TimeUnit.SECONDS)) 68 | .addLast(new CommonDecoder()) 69 | .addLast(new NettyClientHandler()); 70 | } 71 | }); 72 | Channel channel = null; 73 | try{ 74 | channel = connect(bootstrap, inetSocketAddress); 75 | }catch (ExecutionException | InterruptedException e){ 76 | logger.error("连接客户端时有错误发生", e); 77 | return null; 78 | } 79 | channels.put(key, channel); 80 | return channel; 81 | } 82 | 83 | /** 84 | * @description Netty客户端创建通道连接 85 | * @param [bootstrap, inetSocketAddress] 86 | * @return [void] 87 | * @date [2021-03-11 14:19] 88 | */ 89 | private static Channel connect(Bootstrap bootstrap, InetSocketAddress inetSocketAddress) throws 90 | ExecutionException, InterruptedException { 91 | CompletableFuture completableFuture = new CompletableFuture<>(); 92 | bootstrap.connect(inetSocketAddress).addListener((ChannelFutureListener) future -> { 93 | if (future.isSuccess()) { 94 | logger.info("客户端连接成功!"); 95 | completableFuture.complete(future.channel()); 96 | }else { 97 | throw new IllegalStateException(); 98 | } 99 | }); 100 | return completableFuture.get(); 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /rpc-core/src/main/java/com/panda/rpc/transport/netty/client/NettyClient.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.transport.netty.client; 2 | 3 | import com.panda.rpc.entity.RpcRequest; 4 | import com.panda.rpc.entity.RpcResponse; 5 | import com.panda.rpc.enumeration.RpcError; 6 | import com.panda.rpc.exception.RpcException; 7 | import com.panda.rpc.factory.SingletonFactory; 8 | import com.panda.rpc.loadbalancer.LoadBalancer; 9 | import com.panda.rpc.loadbalancer.RandomLoadBalancer; 10 | import com.panda.rpc.register.NacosServiceDiscovery; 11 | import com.panda.rpc.register.ServiceDiscovery; 12 | import com.panda.rpc.serializer.CommonSerializer; 13 | import com.panda.rpc.transport.RpcClient; 14 | import io.netty.bootstrap.Bootstrap; 15 | import io.netty.channel.Channel; 16 | import io.netty.channel.ChannelFutureListener; 17 | import io.netty.channel.EventLoopGroup; 18 | import io.netty.channel.nio.NioEventLoopGroup; 19 | import io.netty.channel.socket.nio.NioSocketChannel; 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | 23 | import java.net.InetSocketAddress; 24 | import java.util.concurrent.CompletableFuture; 25 | 26 | /** 27 | * @author [PANDA] 1843047930@qq.com 28 | * @date [2021-02-22 11:08] 29 | * @description Netty方式客户端 30 | */ 31 | public class NettyClient implements RpcClient { 32 | 33 | private static final Logger logger = LoggerFactory.getLogger(NettyClient.class); 34 | private static final EventLoopGroup group; 35 | private static final Bootstrap bootstrap; 36 | 37 | static { 38 | group = new NioEventLoopGroup(); 39 | bootstrap = new Bootstrap(); 40 | bootstrap.group(group) 41 | .channel(NioSocketChannel.class); 42 | } 43 | 44 | private final ServiceDiscovery serviceDiscovery; 45 | private final CommonSerializer serializer; 46 | 47 | private final UnprocessedRequests unprocessedRequests; 48 | 49 | public NettyClient() { 50 | //以默认序列化器调用构造函数 51 | this(DEFAULT_SERIALIZER, new RandomLoadBalancer()); 52 | } 53 | 54 | public NettyClient(LoadBalancer loadBalancer){ 55 | this(DEFAULT_SERIALIZER, loadBalancer); 56 | } 57 | 58 | public NettyClient(Integer serializerCode){ 59 | this(serializerCode, new RandomLoadBalancer()); 60 | } 61 | 62 | public NettyClient(Integer serializerCode, LoadBalancer loadBalancer){ 63 | serviceDiscovery = new NacosServiceDiscovery(loadBalancer); 64 | serializer = CommonSerializer.getByCode(serializerCode); 65 | unprocessedRequests = SingletonFactory.getInstance(UnprocessedRequests.class); 66 | } 67 | 68 | @Override 69 | public CompletableFuture sendRequest(RpcRequest rpcRequest) { 70 | if (serializer == null) { 71 | logger.error("未设置序列化器"); 72 | throw new RpcException(RpcError.SERIALIZER_NOT_FOUND); 73 | } 74 | CompletableFuture resultFuture = new CompletableFuture<>(); 75 | try { 76 | //从Nacos获取提供对应服务的服务端地址 77 | InetSocketAddress inetSocketAddress = serviceDiscovery.lookupService(rpcRequest.getInterfaceName()); 78 | //创建Netty通道连接 79 | Channel channel = ChannelProvider.get(inetSocketAddress, serializer); 80 | if(!channel.isActive()) { 81 | group.shutdownGracefully(); 82 | return null; 83 | } 84 | //将新请求放入未处理完的请求中 85 | unprocessedRequests.put(rpcRequest.getRequestId(), resultFuture); 86 | //向服务端发请求,并设置监听,关于writeAndFlush()的具体实现可以参考:https://blog.csdn.net/qq_34436819/article/details/103937188 87 | channel.writeAndFlush(rpcRequest).addListener((ChannelFutureListener) future1 -> { 88 | if(future1.isSuccess()){ 89 | logger.info(String.format("客户端发送消息:%s", rpcRequest.toString())); 90 | }else { 91 | future1.channel().close(); 92 | resultFuture.completeExceptionally(future1.cause()); 93 | logger.error("发送消息时有错误发生: ", future1.cause()); 94 | } 95 | }); 96 | }catch (Exception e){ 97 | //将请求从请求集合中移除 98 | unprocessedRequests.remove(rpcRequest.getRequestId()); 99 | logger.error(e.getMessage(), e); 100 | //interrupt()这里作用是给受阻塞的当前线程发出一个中断信号,让当前线程退出阻塞状态,好继续执行然后结束 101 | Thread.currentThread().interrupt(); 102 | } 103 | return resultFuture; 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /rpc-core/src/main/java/com/panda/rpc/transport/netty/client/NettyClientHandler.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.transport.netty.client; 2 | 3 | import com.panda.rpc.entity.RpcRequest; 4 | import com.panda.rpc.entity.RpcResponse; 5 | import com.panda.rpc.factory.SingletonFactory; 6 | import com.panda.rpc.serializer.CommonSerializer; 7 | import io.netty.channel.Channel; 8 | import io.netty.channel.ChannelFutureListener; 9 | import io.netty.channel.ChannelHandlerContext; 10 | import io.netty.channel.SimpleChannelInboundHandler; 11 | import io.netty.handler.timeout.IdleState; 12 | import io.netty.handler.timeout.IdleStateEvent; 13 | import io.netty.util.ReferenceCountUtil; 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | 17 | import java.net.InetSocketAddress; 18 | 19 | /** 20 | * @author [PANDA] 1843047930@qq.com 21 | * @date [2021-02-22 16:46] 22 | * @description 客户端Netty处理器 23 | */ 24 | public class NettyClientHandler extends SimpleChannelInboundHandler { 25 | 26 | private static final Logger logger = LoggerFactory.getLogger(NettyClientHandler.class); 27 | 28 | private final UnprocessedRequests unprocessedRequests; 29 | 30 | public NettyClientHandler(){ 31 | unprocessedRequests = SingletonFactory.getInstance(UnprocessedRequests.class); 32 | } 33 | 34 | @Override 35 | public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { 36 | if(evt instanceof IdleStateEvent){ 37 | IdleState state = ((IdleStateEvent) evt).state(); 38 | if(state == IdleState.WRITER_IDLE){ 39 | logger.info("发送心跳包[{}]", ctx.channel().remoteAddress()); 40 | Channel channel = ChannelProvider.get((InetSocketAddress) ctx.channel().remoteAddress(), 41 | CommonSerializer.getByCode(CommonSerializer.DEFAULT_SERIALIZER)); 42 | RpcRequest rpcRequest = new RpcRequest(); 43 | rpcRequest.setHeartBeat(true); 44 | //设置一个Listener监测服务端是否接收到心跳包,如果接收到就代表对方在线,不用关闭Channel 45 | channel.writeAndFlush(rpcRequest).addListener(ChannelFutureListener.CLOSE_ON_FAILURE); 46 | } 47 | }else { 48 | super.userEventTriggered(ctx, evt); 49 | } 50 | } 51 | 52 | @Override 53 | protected void channelRead0(ChannelHandlerContext ctx, RpcResponse msg) throws Exception { 54 | try { 55 | logger.info(String.format("客户端接收到消息:%s", msg)); 56 | //将响应数据取出 57 | unprocessedRequests.complete(msg); 58 | } finally { 59 | ReferenceCountUtil.release(msg); 60 | } 61 | } 62 | 63 | @Override 64 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 65 | logger.error("过程调用中有错误发生:"); 66 | cause.printStackTrace(); 67 | ctx.close(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /rpc-core/src/main/java/com/panda/rpc/transport/netty/client/UnprocessedRequests.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.transport.netty.client; 2 | 3 | import com.panda.rpc.entity.RpcResponse; 4 | 5 | import java.util.concurrent.CompletableFuture; 6 | import java.util.concurrent.ConcurrentHashMap; 7 | 8 | /** 9 | * @author [PANDA] 1843047930@qq.com 10 | * @date [2021-03-15 14:18] 11 | * @description 未处理的请求(对所有Netty客户端请求进行统一管理) 12 | */ 13 | public class UnprocessedRequests { 14 | 15 | private static ConcurrentHashMap> unprocessedRequests = new ConcurrentHashMap<>(); 16 | 17 | public void put(String requestId, CompletableFuture future) { 18 | unprocessedRequests.put(requestId, future); 19 | } 20 | 21 | public void remove(String requestId) { 22 | unprocessedRequests.remove(requestId); 23 | } 24 | 25 | public void complete(RpcResponse rpcResponse){ 26 | //请求完成了,把请求从未完成的请求中移除 27 | CompletableFuture future = unprocessedRequests.remove(rpcResponse.getRequestId()); 28 | if(null != future){ 29 | //把响应对象放入future中 30 | future.complete(rpcResponse); 31 | }else { 32 | throw new IllegalStateException(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /rpc-core/src/main/java/com/panda/rpc/transport/netty/server/NettyServer.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.transport.netty.server; 2 | 3 | import com.panda.rpc.codec.CommonDecoder; 4 | import com.panda.rpc.codec.CommonEncoder; 5 | import com.panda.rpc.hook.ShutdownHook; 6 | import com.panda.rpc.provider.ServiceProviderImpl; 7 | import com.panda.rpc.register.NacosServiceRegistry; 8 | import com.panda.rpc.serializer.CommonSerializer; 9 | import com.panda.rpc.transport.AbstractRpcServer; 10 | import io.netty.bootstrap.ServerBootstrap; 11 | import io.netty.channel.*; 12 | import io.netty.channel.nio.NioEventLoopGroup; 13 | import io.netty.channel.socket.SocketChannel; 14 | import io.netty.channel.socket.nio.NioServerSocketChannel; 15 | import io.netty.handler.logging.LogLevel; 16 | import io.netty.handler.logging.LoggingHandler; 17 | import io.netty.handler.timeout.IdleStateHandler; 18 | 19 | import java.util.concurrent.TimeUnit; 20 | 21 | /** 22 | * @author [PANDA] 1843047930@qq.com 23 | * @date [2021-02-21 14:04] 24 | * @description Netty方式服务端 25 | */ 26 | public class NettyServer extends AbstractRpcServer { 27 | 28 | private final CommonSerializer serializer; 29 | 30 | public NettyServer(String host, int port) { 31 | this(host, port, DEFAULT_SERIALIZER); 32 | } 33 | 34 | public NettyServer(String host, int port, Integer serializerCode) { 35 | this.host = host; 36 | this.port = port; 37 | serviceRegistry = new NacosServiceRegistry(); 38 | serviceProvider = new ServiceProviderImpl(); 39 | serializer = CommonSerializer.getByCode(serializerCode); 40 | //自动注册服务 41 | scanServices(); 42 | } 43 | 44 | @Override 45 | public void start() { 46 | //添加注销服务的钩子,服务端关闭时才会执行 47 | ShutdownHook.getShutdownHook().addClearAllHook(); 48 | //用于处理客户端新连接的主”线程池“ 49 | EventLoopGroup bossGroup = new NioEventLoopGroup(); 50 | //用于连接后处理IO事件的从”线程池“ 51 | EventLoopGroup workerGroup = new NioEventLoopGroup(); 52 | try{ 53 | //初始化Netty服务端启动器,作为服务端入口 54 | ServerBootstrap serverBootstrap = new ServerBootstrap(); 55 | //将主从“线程池”初始化到启动器中 56 | serverBootstrap.group(bossGroup, workerGroup) 57 | //设置服务端通道类型 58 | .channel(NioServerSocketChannel.class) 59 | //日志打印方式 60 | .handler(new LoggingHandler(LogLevel.INFO)) 61 | //配置ServerChannel参数,服务端接受连接的最大队列长度,如果队列已满,客户端连接将被拒绝。理解可参考:https://blog.csdn.net/fd2025/article/details/79740226 62 | .option(ChannelOption.SO_BACKLOG, 256) 63 | //启用该功能时,TCP会主动探测空闲连接的有效性。可以将此功能视为TCP的心跳机制,默认的心跳间隔是7200s即2小时。 64 | .option(ChannelOption.SO_KEEPALIVE, true) 65 | //配置Channel参数,nodelay没有延迟,true就代表禁用Nagle算法,减小传输延迟。理解可参考:https://blog.csdn.net/lclwjl/article/details/80154565 66 | .childOption(ChannelOption.TCP_NODELAY, true) 67 | //初始化Handler,设置Handler操作 68 | .childHandler(new ChannelInitializer() { 69 | @Override 70 | protected void initChannel(SocketChannel ch) throws Exception { 71 | //初始化管道 72 | ChannelPipeline pipeline = ch.pipeline(); 73 | //往管道中添加Handler,注意入站Handler与出站Handler都必须按实际执行顺序添加,比如先解码再Server处理,那Decoder()就要放在前面 74 | //但入站和出站Handler之间则互不影响,这里我就是先添加的出站Handler再添加的入站 75 | //设定IdleStateHandler心跳检测每30秒进行一次读检测,如果30秒内ChannelRead()方法未被调用则触发一次userEventTrigger()方法 76 | pipeline.addLast(new IdleStateHandler(30, 0, 0, TimeUnit.SECONDS)) 77 | .addLast(new CommonEncoder(serializer)) 78 | .addLast(new CommonDecoder()) 79 | .addLast(new NettyServerHandler()); 80 | } 81 | }); 82 | //绑定端口,启动Netty,sync()代表阻塞主Server线程,以执行Netty线程,如果不阻塞Netty就直接被下面shutdown了 83 | ChannelFuture future = serverBootstrap.bind(host, port).sync(); 84 | //等确定通道关闭了,关闭future回到主Server线程 85 | future.channel().closeFuture().sync(); 86 | }catch (InterruptedException e){ 87 | logger.error("启动服务器时有错误发生", e); 88 | }finally { 89 | //优雅关闭Netty服务端且清理掉内存,shutdownGracefully()执行逻辑参考:https://www.icode9.com/content-4-797057.html 90 | bossGroup.shutdownGracefully(); 91 | workerGroup.shutdownGracefully(); 92 | } 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /rpc-core/src/main/java/com/panda/rpc/transport/netty/server/NettyServerHandler.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.transport.netty.server; 2 | 3 | import com.panda.rpc.entity.RpcRequest; 4 | import com.panda.rpc.handler.RequestHandler; 5 | import io.netty.channel.ChannelHandlerContext; 6 | import io.netty.channel.SimpleChannelInboundHandler; 7 | import io.netty.handler.timeout.IdleState; 8 | import io.netty.handler.timeout.IdleStateEvent; 9 | import io.netty.util.ReferenceCountUtil; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | /** 14 | * @author [PANDA] 1843047930@qq.com 15 | * @date [2021-02-22 16:24] 16 | * @description Netty中处理从客户端传来的RpcRequest 17 | */ 18 | public class NettyServerHandler extends SimpleChannelInboundHandler { 19 | 20 | private static final Logger logger = LoggerFactory.getLogger(NettyServerHandler.class); 21 | private final RequestHandler requestHandler; 22 | 23 | public NettyServerHandler(){ 24 | requestHandler = new RequestHandler(); 25 | } 26 | 27 | @Override 28 | public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { 29 | if(evt instanceof IdleStateEvent){ 30 | IdleState state = ((IdleStateEvent) evt).state(); 31 | if(state == IdleState.READER_IDLE){ 32 | logger.info("长时间未收到心跳包,断开连接……"); 33 | ctx.close(); 34 | } 35 | }else { 36 | super.userEventTriggered(ctx, evt); 37 | } 38 | } 39 | 40 | @Override 41 | protected void channelRead0(ChannelHandlerContext ctx, RpcRequest msg) throws Exception { 42 | try{ 43 | if(msg.getHeartBeat()){ 44 | logger.info("接收到客户端心跳包……"); 45 | return; 46 | } 47 | logger.info("服务端接收到请求:{}", msg); 48 | Object response = requestHandler.handle(msg); 49 | if(ctx.channel().isActive() && ctx.channel().isWritable()) { 50 | //注意这里的通道是workGroup中的,而NettyServer中创建的是bossGroup的,不要混淆 51 | ctx.writeAndFlush(response); 52 | }else { 53 | logger.error("通道不可写"); 54 | } 55 | }finally { 56 | ReferenceCountUtil.release(msg); 57 | } 58 | } 59 | 60 | @Override 61 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 62 | logger.error("处理过程调用时有错误发生:"); 63 | cause.printStackTrace(); 64 | ctx.close(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /rpc-core/src/main/java/com/panda/rpc/transport/socket/client/SocketClient.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.transport.socket.client; 2 | 3 | import com.panda.rpc.entity.RpcRequest; 4 | import com.panda.rpc.entity.RpcResponse; 5 | import com.panda.rpc.enumeration.RpcError; 6 | import com.panda.rpc.exception.RpcException; 7 | import com.panda.rpc.loadbalancer.LoadBalancer; 8 | import com.panda.rpc.loadbalancer.RandomLoadBalancer; 9 | import com.panda.rpc.register.NacosServiceDiscovery; 10 | import com.panda.rpc.register.ServiceDiscovery; 11 | import com.panda.rpc.serializer.CommonSerializer; 12 | import com.panda.rpc.transport.RpcClient; 13 | import com.panda.rpc.transport.socket.util.ObjectReader; 14 | import com.panda.rpc.transport.socket.util.ObjectWriter; 15 | import com.panda.rpc.util.RpcMessageChecker; 16 | import org.slf4j.Logger; 17 | import org.slf4j.LoggerFactory; 18 | 19 | import java.io.IOException; 20 | import java.io.InputStream; 21 | import java.io.OutputStream; 22 | import java.net.InetSocketAddress; 23 | import java.net.Socket; 24 | 25 | /** 26 | * @author [PANDA] 1843047930@qq.com 27 | * @date [2021-02-04 17:20] 28 | * @description Socket方式进行远程调用的客户端 29 | */ 30 | public class SocketClient implements RpcClient { 31 | 32 | private static final Logger logger = LoggerFactory.getLogger(SocketClient.class); 33 | 34 | private final ServiceDiscovery serviceDiscovery; 35 | 36 | private final CommonSerializer serializer; 37 | 38 | public SocketClient() { 39 | //以默认序列化器调用构造函数 40 | this(DEFAULT_SERIALIZER, new RandomLoadBalancer()); 41 | } 42 | 43 | public SocketClient(LoadBalancer loadBalancer){ 44 | this(DEFAULT_SERIALIZER, loadBalancer); 45 | } 46 | 47 | public SocketClient(Integer serializerCode){ 48 | this(serializerCode, new RandomLoadBalancer()); 49 | } 50 | 51 | public SocketClient(Integer serializerCode, LoadBalancer loadBalancer) { 52 | serviceDiscovery = new NacosServiceDiscovery(loadBalancer); 53 | serializer = CommonSerializer.getByCode(serializerCode); 54 | } 55 | 56 | @Override 57 | public Object sendRequest(RpcRequest rpcRequest){ 58 | if (serializer == null) { 59 | logger.error("未设置序列化器"); 60 | throw new RpcException(RpcError.SERIALIZER_NOT_FOUND); 61 | } 62 | //从Nacos获取提供对应服务的服务端地址 63 | InetSocketAddress inetSocketAddress = serviceDiscovery.lookupService(rpcRequest.getInterfaceName()); 64 | /** 65 | * socket套接字实现TCP网络传输 66 | * try()中一般放对资源的申请,若{}出现异常,()资源会自动关闭 67 | */ 68 | try (Socket socket = new Socket()) { 69 | socket.connect(inetSocketAddress); 70 | OutputStream outputStream = socket.getOutputStream(); 71 | InputStream inputStream = socket.getInputStream(); 72 | ObjectWriter.writeObject(outputStream, rpcRequest, serializer); 73 | Object obj = ObjectReader.readObject(inputStream); 74 | RpcResponse rpcResponse = (RpcResponse) obj; 75 | RpcMessageChecker.check(rpcRequest, rpcResponse); 76 | return rpcResponse; 77 | } catch (IOException e) { 78 | logger.error("调用时有错误发生:" + e); 79 | throw new RpcException("服务调用失败:", e); 80 | } 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /rpc-core/src/main/java/com/panda/rpc/transport/socket/server/SocketRequestHandlerThread.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.transport.socket.server; 2 | 3 | import com.panda.rpc.entity.RpcRequest; 4 | import com.panda.rpc.handler.RequestHandler; 5 | import com.panda.rpc.serializer.CommonSerializer; 6 | import com.panda.rpc.transport.socket.util.ObjectReader; 7 | import com.panda.rpc.transport.socket.util.ObjectWriter; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import java.io.IOException; 12 | import java.io.InputStream; 13 | import java.io.OutputStream; 14 | import java.net.Socket; 15 | 16 | /** 17 | * @author [PANDA] 1843047930@qq.com 18 | * @date [2021-02-18 11:38] 19 | * @description IO传输模式|处理客户端RpcRequest的工作线程 20 | */ 21 | public class SocketRequestHandlerThread implements Runnable { 22 | 23 | private static final Logger logger = LoggerFactory.getLogger(SocketRequestHandlerThread.class); 24 | 25 | private Socket socket; 26 | private RequestHandler requestHandler; 27 | private CommonSerializer serializer; 28 | 29 | public SocketRequestHandlerThread(Socket socket, RequestHandler requestHandler, CommonSerializer serializer) { 30 | this.socket = socket; 31 | this.requestHandler = requestHandler; 32 | this.serializer = serializer; 33 | } 34 | 35 | @Override 36 | public void run() { 37 | try(InputStream inputStream = socket.getInputStream(); 38 | OutputStream outputStream = socket.getOutputStream()) { 39 | RpcRequest rpcRequest = (RpcRequest) ObjectReader.readObject(inputStream); 40 | Object response = requestHandler.handle(rpcRequest); 41 | ObjectWriter.writeObject(outputStream, response, serializer); 42 | }catch (IOException e){ 43 | logger.info("调用或发送时发生错误:" + e); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /rpc-core/src/main/java/com/panda/rpc/transport/socket/server/SocketServer.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.transport.socket.server; 2 | 3 | import com.panda.rpc.hook.ShutdownHook; 4 | import com.panda.rpc.provider.ServiceProvider; 5 | import com.panda.rpc.provider.ServiceProviderImpl; 6 | import com.panda.rpc.register.NacosServiceRegistry; 7 | import com.panda.rpc.transport.AbstractRpcServer; 8 | import com.panda.rpc.transport.RpcServer; 9 | import com.panda.rpc.enumeration.RpcError; 10 | import com.panda.rpc.exception.RpcException; 11 | import com.panda.rpc.register.ServiceRegistry; 12 | import com.panda.rpc.handler.RequestHandler; 13 | import com.panda.rpc.serializer.CommonSerializer; 14 | import com.panda.rpc.factory.ThreadPoolFactory; 15 | import org.slf4j.Logger; 16 | import org.slf4j.LoggerFactory; 17 | 18 | import java.io.IOException; 19 | import java.net.InetSocketAddress; 20 | import java.net.ServerSocket; 21 | import java.net.Socket; 22 | import java.util.concurrent.*; 23 | 24 | /** 25 | * @author [PANDA] 1843047930@qq.com 26 | * @date [2021-02-05 11:34] 27 | * @description Socket方式进行远程调用连接的服务端 28 | */ 29 | public class SocketServer extends AbstractRpcServer { 30 | 31 | private final ExecutorService threadPool; 32 | private final CommonSerializer serializer; 33 | private final RequestHandler requestHandler = new RequestHandler(); 34 | 35 | public SocketServer(String host, int port) { 36 | this(host, port, DEFAULT_SERIALIZER); 37 | } 38 | 39 | public SocketServer(String host, int port, Integer serializerCode){ 40 | this.host = host; 41 | this.port = port; 42 | serviceRegistry = new NacosServiceRegistry(); 43 | serviceProvider = new ServiceProviderImpl(); 44 | serializer = CommonSerializer.getByCode(serializerCode); 45 | //创建线程池 46 | threadPool = ThreadPoolFactory.createDefaultThreadPool("socket-rpc-server"); 47 | //自动注册服务 48 | scanServices(); 49 | } 50 | 51 | /** 52 | * @description 服务端启动 53 | * @param [] 54 | * @return [void] 55 | * @date [2021-02-05 11:57] 56 | */ 57 | @Override 58 | public void start(){ 59 | try(ServerSocket serverSocket = new ServerSocket()){ 60 | serverSocket.bind(new InetSocketAddress(host, port)); 61 | logger.info("服务器启动……"); 62 | //添加钩子,服务端关闭时会注销服务 63 | ShutdownHook.getShutdownHook().addClearAllHook(); 64 | Socket socket; 65 | //当未接收到连接请求时,accept()会一直阻塞 66 | while ((socket = serverSocket.accept()) != null){ 67 | logger.info("客户端连接!{}:{}", socket.getInetAddress(), socket.getPort()); 68 | threadPool.execute(new SocketRequestHandlerThread(socket, requestHandler, serializer)); 69 | } 70 | threadPool.shutdown(); 71 | }catch (IOException e){ 72 | logger.info("服务器启动时有错误发生:" + e); 73 | } 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /rpc-core/src/main/java/com/panda/rpc/transport/socket/util/ObjectReader.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.transport.socket.util; 2 | 3 | import com.panda.rpc.entity.RpcRequest; 4 | import com.panda.rpc.entity.RpcResponse; 5 | import com.panda.rpc.enumeration.PackageType; 6 | import com.panda.rpc.enumeration.RpcError; 7 | import com.panda.rpc.exception.RpcException; 8 | import com.panda.rpc.serializer.CommonSerializer; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | import java.io.IOException; 13 | import java.io.InputStream; 14 | 15 | /** 16 | * @author [PANDA] 1843047930@qq.com 17 | * @date [2021-03-10 21:32] 18 | * @description Socket方式从输入流中读取字节并反序列化【解码】 19 | */ 20 | public class ObjectReader { 21 | 22 | private static final Logger logger = LoggerFactory.getLogger(ObjectReader.class); 23 | private static final int MAGIC_NUMBER = 0xCAFEBABE; 24 | 25 | public static Object readObject(InputStream in) throws IOException { 26 | byte[] numberBytes = new byte[4]; 27 | in.read(numberBytes); 28 | int magic = bytesToInt(numberBytes); 29 | if (magic != MAGIC_NUMBER) { 30 | logger.error("不识别的协议包:{}", magic); 31 | throw new RpcException(RpcError.UNKNOWN_PROTOCOL); 32 | } 33 | in.read(numberBytes); 34 | int packageCode = bytesToInt(numberBytes); 35 | Class packageClass; 36 | if (packageCode == PackageType.REQUEST_PACK.getCode()) { 37 | packageClass = RpcRequest.class; 38 | } else if (packageCode == PackageType.RESPONSE_PACK.getCode()) { 39 | packageClass = RpcResponse.class; 40 | } else { 41 | logger.error("不识别的数据包:{}", packageCode); 42 | throw new RpcException(RpcError.UNKNOWN_PACKAGE_TYPE); 43 | } 44 | in.read(numberBytes); 45 | int serializerCode = bytesToInt(numberBytes); 46 | CommonSerializer serializer = CommonSerializer.getByCode(serializerCode); 47 | if (serializer == null) { 48 | logger.error("不识别的反序列化器:{}", serializerCode); 49 | throw new RpcException(RpcError.UNKNOWN_SERIALIZER); 50 | } 51 | in.read(numberBytes); 52 | int length = bytesToInt(numberBytes); 53 | byte[] bytes = new byte[length]; 54 | in.read(bytes); 55 | return serializer.deserialize(bytes, packageClass); 56 | } 57 | 58 | /** 59 | * @description 字节数组转换为Int 60 | * @param [src] 61 | * @return [int] 62 | * @date [2021-03-10 21:57] 63 | */ 64 | private static int bytesToInt(byte[] src) { 65 | int value; 66 | value = ((src[0] & 0xFF) << 24) 67 | |((src[1] & 0xFF) << 16) 68 | |((src[2] & 0xFF) << 8) 69 | |(src[3] & 0xFF); 70 | return value; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /rpc-core/src/main/java/com/panda/rpc/transport/socket/util/ObjectWriter.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.transport.socket.util; 2 | 3 | import com.panda.rpc.entity.RpcRequest; 4 | import com.panda.rpc.enumeration.PackageType; 5 | import com.panda.rpc.serializer.CommonSerializer; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import java.io.IOException; 10 | import java.io.OutputStream; 11 | 12 | /** 13 | * @author [PANDA] 1843047930@qq.com 14 | * @date [2021-03-10 22:09] 15 | * @description Socket方式将数据序列化并写入输出流中【编码】 16 | */ 17 | public class ObjectWriter { 18 | 19 | private static final Logger logger = LoggerFactory.getLogger(ObjectWriter.class); 20 | private static final int MAGIC_NUMBER = 0xCAFEBABE; 21 | 22 | public static void writeObject(OutputStream out, Object object, CommonSerializer serializer) throws IOException { 23 | out.write(intToBytes(MAGIC_NUMBER)); 24 | if(object instanceof RpcRequest) { 25 | out.write(intToBytes(PackageType.REQUEST_PACK.getCode())); 26 | } else { 27 | out.write(intToBytes(PackageType.RESPONSE_PACK.getCode())); 28 | } 29 | out.write(intToBytes(serializer.getCode())); 30 | byte[] bytes = serializer.serialize(object); 31 | out.write(intToBytes(bytes.length)); 32 | out.write(bytes); 33 | out.flush(); 34 | } 35 | 36 | /** 37 | * @description 将Int转换为字节数组 38 | * @param [value] 39 | * @return [byte[]] 40 | * @date [2021-03-10 22:15] 41 | */ 42 | private static byte[] intToBytes(int value) { 43 | byte[] src = new byte[4]; 44 | src[0] = (byte) ((value>>24) & 0xFF); 45 | src[1] = (byte) ((value>>16) & 0xFF); 46 | src[2] = (byte) ((value>>8) & 0xFF); 47 | src[3] = (byte) (value & 0xFF); 48 | return src; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /rpc-core/target/classes/META-INF/rpc-core.kotlin_module: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /rpc.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IAMPadA/Panda-Rpc-Framework/cb7b172c9bb48f9cba92247ff2105d7636cfc05b/rpc.jpg -------------------------------------------------------------------------------- /test-client/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | Panda-Rpc-Framework 7 | com.panda 8 | 3.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | test-client 13 | 14 | 15 | 16 | com.panda 17 | rpc-core 18 | ${project.version} 19 | 20 | 21 | com.panda 22 | rpc-api 23 | ${project.version} 24 | 25 | 26 | -------------------------------------------------------------------------------- /test-client/src/main/java/com/panda/test/NettyTestClient.java: -------------------------------------------------------------------------------- 1 | package com.panda.test; 2 | 3 | import com.panda.rpc.api.ByeService; 4 | import com.panda.rpc.api.HelloObject; 5 | import com.panda.rpc.api.HelloService; 6 | import com.panda.rpc.serializer.CommonSerializer; 7 | import com.panda.rpc.transport.RpcClient; 8 | import com.panda.rpc.transport.RpcClientProxy; 9 | import com.panda.rpc.transport.netty.client.NettyClient; 10 | 11 | /** 12 | * @author [PANDA] 1843047930@qq.com 13 | * @date [2021-02-22 17:17] 14 | * @description 测试用Netty客户端 15 | */ 16 | public class NettyTestClient { 17 | public static void main(String[] args) { 18 | RpcClient client = new NettyClient(CommonSerializer.PROTOBUF_SERIALIZER); 19 | RpcClientProxy rpcClientProxy = new RpcClientProxy(client); 20 | HelloService helloService = rpcClientProxy.getProxy(HelloService.class); 21 | HelloObject object = new HelloObject(12, "this is netty style"); 22 | String res = helloService.hello(object); 23 | System.out.println(res); 24 | ByeService byeService = rpcClientProxy.getProxy(ByeService.class); 25 | System.out.println(byeService.bye("Netty")); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test-client/src/main/java/com/panda/test/SocketTestClient.java: -------------------------------------------------------------------------------- 1 | package com.panda.test; 2 | 3 | import com.panda.rpc.api.ByeService; 4 | import com.panda.rpc.api.HelloObject; 5 | import com.panda.rpc.api.HelloService; 6 | import com.panda.rpc.loadbalancer.RoundRobinLoadBalancer; 7 | import com.panda.rpc.serializer.CommonSerializer; 8 | import com.panda.rpc.transport.RpcClientProxy; 9 | import com.panda.rpc.transport.socket.client.SocketClient; 10 | 11 | /** 12 | * @author [PANDA] 1843047930@qq.com 13 | * @date [2021-02-05 14:50] 14 | * @description 测试用客户端 15 | */ 16 | public class SocketTestClient { 17 | 18 | public static void main(String[] args) { 19 | SocketClient client = new SocketClient(CommonSerializer.KRYO_SERIALIZER, new RoundRobinLoadBalancer()); 20 | //接口与代理对象之间的中介对象 21 | RpcClientProxy proxy = new RpcClientProxy(client); 22 | //创建代理对象 23 | HelloService helloService = proxy.getProxy(HelloService.class); 24 | //接口方法的参数对象 25 | HelloObject object = new HelloObject(12, "This is test message"); 26 | //由动态代理可知,代理对象调用hello()实际会执行invoke() 27 | String res = helloService.hello(object); 28 | System.out.println(res); 29 | ByeService byeService = proxy.getProxy(ByeService.class); 30 | System.out.println(byeService.bye("Netty")); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test-client/target/classes/META-INF/test-client.kotlin_module: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /test-server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | Panda-Rpc-Framework 7 | com.panda 8 | 3.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | test-server 13 | 14 | 15 | 16 | com.panda 17 | rpc-core 18 | ${project.version} 19 | 20 | 21 | com.panda 22 | rpc-api 23 | ${project.version} 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /test-server/src/main/java/com/panda/rpc/test/ByeServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.test; 2 | 3 | import com.panda.rpc.annotation.Service; 4 | import com.panda.rpc.api.ByeService; 5 | 6 | /** 7 | * @author [PANDA] 1843047930@qq.com 8 | * @date [2021-03-16 13:12] 9 | * @description 服务实现类 10 | */ 11 | @Service 12 | public class ByeServiceImpl implements ByeService { 13 | 14 | @Override 15 | public String bye(String name) { 16 | return "bye," + name; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /test-server/src/main/java/com/panda/rpc/test/HelloServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.test; 2 | 3 | import com.panda.rpc.annotation.Service; 4 | import com.panda.rpc.api.HelloObject; 5 | import com.panda.rpc.api.HelloService; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | /** 10 | * @author [PANDA] 1843047930@qq.com 11 | * @date [2021-02-03 14:46] 12 | * @description 服务端api接口实现 13 | */ 14 | @Service 15 | public class HelloServiceImpl implements HelloService { 16 | 17 | /** 18 | * 使用HelloServiceImpl初始化日志对象,方便在日志输出的时候,可以打印出日志信息所属的类。 19 | */ 20 | private static final Logger logger = LoggerFactory.getLogger(HelloServiceImpl.class); 21 | 22 | @Override 23 | public String hello(HelloObject object) { 24 | //使用{}可以直接将getMessage()内容输出 25 | logger.info("接收到消息:{}", object.getMessage()); 26 | return "成功调用hello()方法"; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test-server/src/main/java/com/panda/rpc/test/NettyTestServer.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.test; 2 | 3 | import com.panda.rpc.annotation.ServiceScan; 4 | import com.panda.rpc.serializer.CommonSerializer; 5 | import com.panda.rpc.transport.RpcServer; 6 | import com.panda.rpc.transport.netty.server.NettyServer; 7 | 8 | /** 9 | * @author [PANDA] 1843047930@qq.com 10 | * @date [2021-02-22 17:13] 11 | * @description 测试用Netty服务端 12 | */ 13 | @ServiceScan 14 | public class NettyTestServer { 15 | public static void main(String[] args) { 16 | RpcServer server = new NettyServer("127.0.0.1", 9999, CommonSerializer.PROTOBUF_SERIALIZER); 17 | server.start(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test-server/src/main/java/com/panda/rpc/test/SocketTestServer.java: -------------------------------------------------------------------------------- 1 | package com.panda.rpc.test; 2 | 3 | import com.panda.rpc.annotation.ServiceScan; 4 | import com.panda.rpc.serializer.CommonSerializer; 5 | import com.panda.rpc.transport.RpcServer; 6 | import com.panda.rpc.transport.socket.server.SocketServer; 7 | 8 | /** 9 | * @author [PANDA] 1843047930@qq.com 10 | * @date [2021-02-05 14:45] 11 | * @description 测试用服务端 12 | */ 13 | @ServiceScan 14 | public class SocketTestServer { 15 | public static void main(String[] args) { 16 | RpcServer server = new SocketServer("127.0.0.1", 9998, CommonSerializer.HESSIAN_SERIALIZER); 17 | server.start(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test-server/src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=DEBUG,stdout 2 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 3 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 4 | log4j.appender.stdout.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c]-[%p] %m%n -------------------------------------------------------------------------------- /test-server/target/classes/META-INF/test-server.kotlin_module: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /test-server/target/classes/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=DEBUG,stdout 2 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 3 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 4 | log4j.appender.stdout.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c]-[%p] %m%n --------------------------------------------------------------------------------