├── .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 |
4 |
5 |
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 |
--------------------------------------------------------------------------------
/.idea/jarRepositories.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
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 | 
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