> future = FUTURE_MAP.remove(rpcResponse.getRequestId());
30 | if (future != null) {
31 | future.complete(rpcResponse);
32 | } else {
33 | throw new IllegalStateException("future is null. rpcResponse=" + JSONUtil.toJsonStr(rpcResponse));
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/ccx-rpc-core/src/main/java/com/ccx/rpc/core/remoting/codec/RpcMessageDecoder.java:
--------------------------------------------------------------------------------
1 | package com.ccx.rpc.core.remoting.codec;
2 |
3 | import com.ccx.rpc.common.extension.ExtensionLoader;
4 | import com.ccx.rpc.core.compress.Compressor;
5 | import com.ccx.rpc.core.consts.SerializeType;
6 | import com.ccx.rpc.core.consts.CompressType;
7 | import com.ccx.rpc.core.consts.MessageType;
8 | import com.ccx.rpc.core.dto.RpcMessage;
9 | import com.ccx.rpc.core.dto.RpcRequest;
10 | import com.ccx.rpc.core.dto.RpcResponse;
11 | import com.ccx.rpc.core.serialize.Serializer;
12 | import io.netty.buffer.ByteBuf;
13 | import io.netty.channel.ChannelHandlerContext;
14 | import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
15 | import lombok.extern.slf4j.Slf4j;
16 |
17 | import java.util.Arrays;
18 |
19 | import static com.ccx.rpc.core.consts.MessageFormatConst.*;
20 |
21 | /**
22 | *
23 | * 自定义协议解码器
24 | *
25 | *
26 | * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
27 | * +---+---+-------+---+---+---+---+-----------+---------+--------+---+---+---+---+---+---+---+---+
28 | * | magic |version| full length |messageType|serialize|compress| RequestId |
29 | * +---+---+-------+---+---+---+---+-----------+---------+--------+---+---+---+---+---+---+---+---+
30 | * | |
31 | * | body |
32 | * | |
33 | * | ... ... |
34 | * +----------------------------------------------------------------------------------------------+
35 | * 2B magic(魔法数)
36 | * 1B version(版本)
37 | * 4B full length(消息长度)
38 | * 1B messageType(消息类型)
39 | * 1B serialize(序列化类型)
40 | * 1B compress(压缩类型)
41 | * 8B requestId(请求的Id)
42 | * body(object类型数据)
43 | *
44 | *
45 | * @author chenchuxin
46 | * @date 2021/7/25
47 | */
48 | @Slf4j
49 | public class RpcMessageDecoder extends LengthFieldBasedFrameDecoder {
50 | public RpcMessageDecoder() {
51 | super(
52 | // 最大的长度,如果超过,会直接丢弃
53 | MAX_FRAME_LENGTH,
54 | // 描述长度的字段[4B full length(消息长度)]在哪个位置:在 [2B magic(魔数)]、[1B version(版本)] 后面
55 | MAGIC_LENGTH + VERSION_LENGTH,
56 | // 描述长度的字段[4B full length(消息长度)]本身的长度,也就是 4B 啦
57 | FULL_LENGTH_LENGTH,
58 | // LengthFieldBasedFrameDecoder 拿到消息长度之后,还会加上 [4B full length(消息长度)] 字段前面的长度
59 | // 因为我们的消息长度包含了这部分了,所以需要减回去
60 | -(MAGIC_LENGTH + VERSION_LENGTH + FULL_LENGTH_LENGTH),
61 | // initialBytesToStrip: 去除哪个位置前面的数据。因为我们还需要检测 魔数 和 版本号,所以不能去除
62 | 0);
63 | }
64 |
65 | @Override
66 | protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
67 | Object decoded = super.decode(ctx, in);
68 | if (decoded instanceof ByteBuf) {
69 | ByteBuf frame = (ByteBuf) decoded;
70 | if (frame.readableBytes() >= HEADER_LENGTH) {
71 | try {
72 | return decodeFrame(frame);
73 | } catch (Exception ex) {
74 | log.error("Decode frame error.", ex);
75 | } finally {
76 | frame.release();
77 | }
78 | }
79 | }
80 | return decoded;
81 | }
82 |
83 | /**
84 | * 业务解码
85 | */
86 | private RpcMessage decodeFrame(ByteBuf in) {
87 | readAndCheckMagic(in);
88 | readAndCheckVersion(in);
89 | int fullLength = in.readInt();
90 | byte messageType = in.readByte();
91 | byte codec = in.readByte();
92 | byte compress = in.readByte();
93 | long requestId = in.readLong();
94 |
95 | RpcMessage rpcMessage = RpcMessage.builder()
96 | .serializeType(codec)
97 | .compressTye(compress)
98 | .requestId(requestId)
99 | .messageType(messageType)
100 | .build();
101 |
102 | if (messageType == MessageType.HEARTBEAT.getValue()) {
103 | return rpcMessage;
104 | }
105 |
106 | int bodyLength = fullLength - HEADER_LENGTH;
107 | if (bodyLength == 0) {
108 | return rpcMessage;
109 | }
110 |
111 | byte[] bodyBytes = new byte[bodyLength];
112 | in.readBytes(bodyBytes);
113 | // 解压
114 | CompressType compressType = CompressType.fromValue(compress);
115 | Compressor compressor = ExtensionLoader.getLoader(Compressor.class).getExtension(compressType.getName());
116 | byte[] decompressedBytes = compressor.decompress(bodyBytes);
117 |
118 | // 反序列化
119 | SerializeType serializeType = SerializeType.fromValue(codec);
120 | if (serializeType == null) {
121 | throw new IllegalArgumentException("unknown codec type:" + codec);
122 | }
123 | Serializer serializer = ExtensionLoader.getLoader(Serializer.class).getExtension(serializeType.getName());
124 | Class> clazz = messageType == MessageType.REQUEST.getValue() ? RpcRequest.class : RpcResponse.class;
125 | Object object = serializer.deserialize(decompressedBytes, clazz);
126 | rpcMessage.setData(object);
127 | return rpcMessage;
128 | }
129 |
130 | /**
131 | * 读取并检查版本
132 | */
133 | private void readAndCheckVersion(ByteBuf in) {
134 | byte version = in.readByte();
135 | if (version != VERSION) {
136 | throw new IllegalArgumentException("Unknown version: " + version);
137 | }
138 | }
139 |
140 | /**
141 | * 读取并检查魔数
142 | */
143 | private void readAndCheckMagic(ByteBuf in) {
144 | byte[] bytes = new byte[MAGIC_LENGTH];
145 | in.readBytes(bytes);
146 | for (int i = 0; i < bytes.length; i++) {
147 | if (bytes[i] != MAGIC[i]) {
148 | throw new IllegalArgumentException("Unknown magic: " + Arrays.toString(bytes));
149 | }
150 | }
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/ccx-rpc-core/src/main/java/com/ccx/rpc/core/remoting/codec/RpcMessageEncoder.java:
--------------------------------------------------------------------------------
1 | package com.ccx.rpc.core.remoting.codec;
2 |
3 | import com.ccx.rpc.common.extension.ExtensionLoader;
4 | import com.ccx.rpc.core.compress.Compressor;
5 | import com.ccx.rpc.core.consts.SerializeType;
6 | import com.ccx.rpc.core.consts.CompressType;
7 | import com.ccx.rpc.core.consts.MessageFormatConst;
8 | import com.ccx.rpc.core.consts.MessageType;
9 | import com.ccx.rpc.core.dto.RpcMessage;
10 | import com.ccx.rpc.core.serialize.Serializer;
11 | import io.netty.buffer.ByteBuf;
12 | import io.netty.channel.ChannelHandlerContext;
13 | import io.netty.handler.codec.MessageToByteEncoder;
14 |
15 | /**
16 | *
17 | * 自定义协议编码器
18 | *
19 | *
20 | * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
21 | * +-----+-----+-------+----+----+----+----+-----------+---------+--------+----+----+----+----+----+----+----+---+
22 | * | magic |version| full length |messageType|serialize|compress| RequestId |
23 | * +-----+-----+-------+----+----+----+----+-----------+----- ---+--------+----+----+----+----+----+----+----+---+
24 | * | |
25 | * | body |
26 | * | |
27 | * | ... ... |
28 | * +-------------------------------------------------------------------------------------------------------------+
29 | * 2B magic(魔数)
30 | * 1B version(版本)
31 | * 4B full length(消息长度)
32 | * 1B messageType(消息类型)
33 | * 1B serialize(序列化类型)
34 | * 1B compress(压缩类型)
35 | * 8B requestId(请求的Id)
36 | * body(object类型数据)
37 | *
38 | *
39 | * @author chenchuxin
40 | * @date 2021/7/25
41 | */
42 | public class RpcMessageEncoder extends MessageToByteEncoder {
43 |
44 | @Override
45 | protected void encode(ChannelHandlerContext ctx, RpcMessage rpcMessage, ByteBuf out) {
46 | // 2B magic code(魔数)
47 | out.writeBytes(MessageFormatConst.MAGIC);
48 | // 1B version(版本)
49 | out.writeByte(MessageFormatConst.VERSION);
50 | // 4B full length(消息长度). 总长度先空着,后面填。
51 | out.writerIndex(out.writerIndex() + MessageFormatConst.FULL_LENGTH_LENGTH);
52 | // 1B messageType(消息类型)
53 | out.writeByte(rpcMessage.getMessageType());
54 | // 1B codec(序列化类型)
55 | out.writeByte(rpcMessage.getSerializeType());
56 | // 1B compress(压缩类型)
57 | out.writeByte(rpcMessage.getCompressTye());
58 | // 8B requestId(请求的Id)
59 | out.writeLong(rpcMessage.getRequestId());
60 | // 写 body,返回 body 长度
61 | int bodyLength = writeBody(rpcMessage, out);
62 |
63 | // 当前写指针
64 | int writerIndex = out.writerIndex();
65 | out.writerIndex(MessageFormatConst.MAGIC_LENGTH + MessageFormatConst.VERSION_LENGTH);
66 | // 4B full length(消息长度)
67 | out.writeInt(MessageFormatConst.HEADER_LENGTH + bodyLength);
68 | // 写指针复原
69 | out.writerIndex(writerIndex);
70 | }
71 |
72 | /**
73 | * 写 body
74 | *
75 | * @return body 长度
76 | */
77 | private int writeBody(RpcMessage rpcMessage, ByteBuf out) {
78 | byte messageType = rpcMessage.getMessageType();
79 | // 如果是 ping、pong 心跳类型的,没有 body,直接返回头部长度
80 | if (messageType == MessageType.HEARTBEAT.getValue()) {
81 | return 0;
82 | }
83 |
84 | // 序列化器
85 | SerializeType serializeType = SerializeType.fromValue(rpcMessage.getSerializeType());
86 | if (serializeType == null) {
87 | throw new IllegalArgumentException("codec type not found");
88 | }
89 | Serializer serializer = ExtensionLoader.getLoader(Serializer.class).getExtension(serializeType.getName());
90 |
91 | // 压缩器
92 | CompressType compressType = CompressType.fromValue(rpcMessage.getCompressTye());
93 | Compressor compressor = ExtensionLoader.getLoader(Compressor.class).getExtension(compressType.getName());
94 |
95 | // 序列化
96 | byte[] notCompressBytes = serializer.serialize(rpcMessage.getData());
97 | // 压缩
98 | byte[] compressedBytes = compressor.compress(notCompressBytes);
99 |
100 | // 写 body
101 | out.writeBytes(compressedBytes);
102 | return compressedBytes.length;
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/ccx-rpc-core/src/main/java/com/ccx/rpc/core/remoting/server/ShutdownHook.java:
--------------------------------------------------------------------------------
1 | package com.ccx.rpc.core.remoting.server;
2 |
3 | import com.ccx.rpc.common.extension.ExtensionLoader;
4 | import com.ccx.rpc.core.config.ConfigManager;
5 | import com.ccx.rpc.core.config.RegistryConfig;
6 | import com.ccx.rpc.core.registry.Registry;
7 | import com.ccx.rpc.core.registry.RegistryFactory;
8 | import lombok.extern.slf4j.Slf4j;
9 |
10 | /**
11 | * 关闭的钩子
12 | *
13 | * @author chenchuxin
14 | * @date 2021/7/24
15 | */
16 | @Slf4j
17 | public class ShutdownHook {
18 |
19 | public static void addShutdownHook() {
20 | log.info("addShutdownHook for clearAll");
21 | Runtime.getRuntime().addShutdownHook(new Thread(() -> {
22 | RegistryFactory registryFactory = ExtensionLoader.getLoader(RegistryFactory.class).getAdaptiveExtension();
23 | RegistryConfig registryConfig = ConfigManager.getInstant().getRegistryConfig();
24 | Registry registry = registryFactory.getRegistry(registryConfig.toURL());
25 | registry.unregisterAllMyService();
26 | }));
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/ccx-rpc-core/src/main/java/com/ccx/rpc/core/remoting/server/netty/NettyServerBootstrap.java:
--------------------------------------------------------------------------------
1 | package com.ccx.rpc.core.remoting.server.netty;
2 |
3 | import cn.hutool.core.net.NetUtil;
4 | import cn.hutool.core.thread.ThreadUtil;
5 | import cn.hutool.core.util.RuntimeUtil;
6 | import com.ccx.rpc.core.config.ConfigManager;
7 | import com.ccx.rpc.core.config.ServiceConfig;
8 | import com.ccx.rpc.core.remoting.codec.RpcMessageDecoder;
9 | import com.ccx.rpc.core.remoting.codec.RpcMessageEncoder;
10 | import com.ccx.rpc.core.remoting.server.ShutdownHook;
11 | import io.netty.bootstrap.ServerBootstrap;
12 | import io.netty.channel.*;
13 | import io.netty.channel.epoll.Epoll;
14 | import io.netty.channel.epoll.EpollServerSocketChannel;
15 | import io.netty.channel.nio.NioEventLoopGroup;
16 | import io.netty.channel.socket.SocketChannel;
17 | import io.netty.channel.socket.nio.NioServerSocketChannel;
18 | import io.netty.handler.logging.LogLevel;
19 | import io.netty.handler.logging.LoggingHandler;
20 | import io.netty.handler.timeout.IdleStateHandler;
21 | import io.netty.util.concurrent.DefaultEventExecutorGroup;
22 | import lombok.extern.slf4j.Slf4j;
23 | import org.springframework.stereotype.Component;
24 |
25 | import java.util.concurrent.TimeUnit;
26 |
27 | /**
28 | * netty 服务端
29 | *
30 | * @author chenchuxin
31 | * @date 2021/7/24
32 | */
33 | @Slf4j
34 | @Component
35 | public class NettyServerBootstrap {
36 |
37 | public void start() {
38 | ShutdownHook.addShutdownHook();
39 | EventLoopGroup bossGroup = new NioEventLoopGroup(1);
40 | EventLoopGroup workerGroup = new NioEventLoopGroup();
41 | DefaultEventExecutorGroup serviceHandlerGroup = new DefaultEventExecutorGroup(
42 | RuntimeUtil.getProcessorCount() * 2,
43 | ThreadUtil.newNamedThreadFactory("service-handler-group", false)
44 | );
45 | try {
46 | ServerBootstrap bootstrap = new ServerBootstrap()
47 | .group(bossGroup, workerGroup)
48 | .channel(Epoll.isAvailable() ? EpollServerSocketChannel.class : NioServerSocketChannel.class)
49 | // 系统用于临时存放已完成三次握手的请求的队列的最大长度。如果连接建立频繁,服务器处理创建新连接较慢,可以适当调大这个参数
50 | .option(ChannelOption.SO_BACKLOG, 128)
51 | // 程序进程非正常退出,内核需要一定的时间才能够释放此端口,不设置 SO_REUSEADDR 就无法正常使用该端口。
52 | .option(ChannelOption.SO_REUSEADDR, Boolean.TRUE)
53 | // TCP/IP协议中针对TCP默认开启了Nagle 算法。
54 | // Nagle 算法通过减少需要传输的数据包,来优化网络。在内核实现中,数据包的发送和接受会先做缓存,分别对应于写缓存和读缓存。
55 | // 启动 TCP_NODELAY,就意味着禁用了 Nagle 算法,允许小包的发送。
56 | // 对于延时敏感型,同时数据传输量比较小的应用,开启TCP_NODELAY选项无疑是一个正确的选择
57 | .childOption(ChannelOption.TCP_NODELAY, Boolean.TRUE)
58 | // 是否开启 TCP 底层心跳机制
59 | .childOption(ChannelOption.SO_KEEPALIVE, Boolean.TRUE)
60 | .handler(new LoggingHandler(LogLevel.INFO))
61 | .childHandler(new ChannelInitializer() {
62 | @Override
63 | protected void initChannel(SocketChannel ch) {
64 | ChannelPipeline p = ch.pipeline();
65 | // 30 秒之内没有收到客户端请求的话就关闭连接
66 | p.addLast(new IdleStateHandler(30, 0, 0, TimeUnit.SECONDS));
67 | // 编解码器
68 | p.addLast(new RpcMessageEncoder());
69 | p.addLast(new RpcMessageDecoder());
70 | // RPC 消息处理器
71 | p.addLast(serviceHandlerGroup, new NettyServerHandler());
72 | }
73 | });
74 | // 绑定端口,同步等待绑定成功
75 | ServiceConfig serviceConfig = ConfigManager.getInstant().getServiceConfig();
76 | ChannelFuture channelFuture = bootstrap.bind(NetUtil.getLocalHostName(), serviceConfig.getPort()).sync();
77 | log.info("server start success. port=" + serviceConfig.getPort());
78 | // 等待服务端监听端口关闭
79 | channelFuture.channel().closeFuture().sync();
80 | } catch (Exception ex) {
81 | log.error("shutdown bossGroup and workerGroup", ex);
82 | bossGroup.shutdownGracefully();
83 | workerGroup.shutdownGracefully();
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/ccx-rpc-core/src/main/java/com/ccx/rpc/core/remoting/server/netty/NettyServerHandler.java:
--------------------------------------------------------------------------------
1 | package com.ccx.rpc.core.remoting.server.netty;
2 |
3 | import com.ccx.rpc.common.consts.RpcException;
4 | import com.ccx.rpc.core.consts.MessageFormatConst;
5 | import com.ccx.rpc.core.consts.MessageType;
6 | import com.ccx.rpc.core.proxy.RpcServiceCache;
7 | import com.ccx.rpc.core.dto.RpcMessage;
8 | import com.ccx.rpc.core.dto.RpcRequest;
9 | import com.ccx.rpc.core.dto.RpcResponse;
10 | import io.netty.channel.ChannelFutureListener;
11 | import io.netty.channel.ChannelHandlerContext;
12 | import io.netty.channel.SimpleChannelInboundHandler;
13 | import io.netty.handler.timeout.IdleState;
14 | import io.netty.handler.timeout.IdleStateEvent;
15 | import io.netty.util.ReferenceCountUtil;
16 | import lombok.extern.slf4j.Slf4j;
17 |
18 | import java.lang.reflect.Method;
19 |
20 | /**
21 | * @author chenchuxin
22 | * @date 2021/7/25
23 | */
24 | @Slf4j
25 | public class NettyServerHandler extends SimpleChannelInboundHandler {
26 |
27 | @Override
28 | protected void channelRead0(ChannelHandlerContext ctx, RpcMessage requestMsg) {
29 | try {
30 | // 不理心跳消息
31 | if (requestMsg.getMessageType() != MessageType.REQUEST.getValue()) {
32 | return;
33 | }
34 | RpcMessage.RpcMessageBuilder responseMsgBuilder = RpcMessage.builder()
35 | .serializeType(requestMsg.getSerializeType())
36 | .compressTye(requestMsg.getCompressTye())
37 | .requestId(requestMsg.getRequestId());
38 | RpcRequest rpcRequest = (RpcRequest) requestMsg.getData();
39 | Object result;
40 | try {
41 | // 根据请求的接口名和版本,获取服务。这个服务是在bean初始化的时候加上的
42 | Object service = RpcServiceCache.getService(rpcRequest.getRpcServiceForCache());
43 | Method method = service.getClass().getMethod(rpcRequest.getMethodName(), rpcRequest.getParamTypes());
44 | // 开始执行
45 | result = method.invoke(service, rpcRequest.getParams());
46 | log.info("service:[{}] successful invoke method:[{}]", rpcRequest.getInterfaceName(), rpcRequest.getMethodName());
47 | } catch (Exception e) {
48 | throw new RpcException(e.getMessage(), e);
49 | }
50 | responseMsgBuilder.messageType(MessageType.RESPONSE.getValue());
51 | if (ctx.channel().isActive() && ctx.channel().isWritable()) {
52 | RpcResponse