造个轮子之基于 Netty 实现自己的 RPC 框架
作 者:
原文链接:https://www.hchstudio.cn/article/2018/b674/
版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。
服务端开发都会或多或少的涉及到 RPC 的使用,当然如果止步于会用,对自己的成长很是不利,所以楼主今天本着知其然,且知其所以然的精神来探讨一下 RPC 这个东西。
child-rpc模型
child-rpc 采用 socket 直连的方式来实现服务的远程调用,然后使用 jdk 动态代理的方式让调用者感知不到远程调用。
child-rpc 开箱使用
发布服务
RPC 服务类要监听指定IP端口,设置要发布的服务的实现及其接口的引用,并指定序列化的方式,目前 child-rpc 支持 Hessian,JACKSON 两种序列化方式。
1 | /** |
引用服务
RPC 客户端要链接远程 IP 端口,并注册要引用的服务,然后调用 sayHi 方法,输出结果1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19/**
* @author wuhf
* @Date 2018/9/1 18:31
**/
public class ClientTest {
public static void main(String[] args) {
ClientConfig clientConfig = new ClientConfig();
clientConfig.setHost("127.0.0.1")
.setPort(5201)
.setTimeoutMillis(100000)
.setSerializer(Serializer.SerializeEnum.HESSIAN.serializer);
ClientProxy clientProxy = new ClientProxy(clientConfig,new NettyClient(),HelloService.class);
for (int i = 0; i < 10; i++) {
HelloService helloService = (HelloService) clientProxy.refer();
System.out.println(helloService.sayHi());
}
}
}
运行
server 端输出
client 端输出
child-rpc 具体实现
RPC 请求,响应消息实体定义
定义消息请求响应格式,消息类型、消息唯一 ID 和消息的 json 序列化字符串内容。消息唯一 ID 是用来客户端验证服务器请求和响应是否匹配。
1 | // rpc 请求 |
网络传输过程中的编码解码
消息编码解码使用自定义的编解码器,根据服务初始化是使用的序列化器来将数据序列化成字节流,拆包的策略是设定指定长度的数据包,对 socket 粘包,拆包感兴趣的小伙伴请移步 Socket 中粘包问题浅析及其解决方案
下面是解码器代码实现 :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
39public class NettyDecoder extends ByteToMessageDecoder {
private Class<?> genericClass;
private Serializer serializer;
public NettyDecoder(Class<?> genericClass, Serializer serializer) {
this.genericClass = genericClass;
this.serializer = serializer;
}
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
if (byteBuf.readableBytes() < 4) {
return;
}
byteBuf.markReaderIndex();
// 读取消息长度
int dataLength = byteBuf.readInt();
if (dataLength < 0) {
channelHandlerContext.close();
}
if (byteBuf.readableBytes() < dataLength) {
byteBuf.resetReaderIndex();
return;
}
try {
byte[] data = new byte[dataLength];
byteBuf.readBytes(data);
Object object = serializer.deserialize(data,genericClass);
list.add(object);
} catch (Exception e) {
e.printStackTrace();
}
}
}
下面是编码器的实现:
1 | public class NettyEncoder extends MessageToByteEncoder<Object> { |
RPC 业务逻辑处理 handler
server 端业务处理 handler 实现 : 主要业务逻辑是 通过 java 的反射实现方法的调用。
1 | public class NettyServerHandler extends SimpleChannelInboundHandler<RpcRequest> { |
client 端主要业务实现是等待 server 响应返回。代码比较简单就不贴代码了,详情请看下面给出的 github 链接。
RPC 服务端与客户端启动
因为服务端与客户端启动都是 Netty 的模板代码,因为篇幅原因就不贴出来了,感兴趣的伙伴请移步 造个轮子—RPC动手实现。
小结
因为只是为了理解 RPC 的本质,所以在实现细节上还有好多没有仔细去雕琢的地方。不过 RPC 的目的就是允许像调用本地服务一样调用远程服务,对调用者透明,于是我们使用了动态代理。并使用 Netty 的 handler 发送数据和响应数据,总的来说该框架实现了简单的 RPC 调用。代码比较简单,主要是思路,以及了解 RPC 底层的实现。
参考文章
作 者:
原文链接:https://www.hchstudio.cn/article/2018/b674/
版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。