前言:所谓RPC是一种通过网络从远程计算机请求服务,而不必了解底层技术的协议,客户端不在乎传输层使用TCP或者UDP,不在意IO模型选择select还是epoll。现在典型的RPC框架有:Thrift,Dubbo等。接下来将参考一些dubbo的东西,展示如何基于Netty和zookeeper开发实现一个高性能RPC框架,同时结合问题分析解决方法
一,定义RPC请求和响应消息结构。
1,首先是请求类。必须包含的是:(1)类名(2)方法名(3)参数(4)参数结构。然后为了其他的一些考虑,我们可以加入其他的特性。例如:本次请求ID,服务的版本号等。实现如下:
public class RpcRequest {
private String requestId;
private String interfaceName;
private String serviceVersion;
private String methodName;
private Class<?>[] parameterTypes;
private Object[] parameters;
//省略get和set
}
2,响应类。包括(1)请求ID(2)异常信息(3)执行结果
public class RpcResponse {
private String requestId;
private Exception exception;
private Object result;
public boolean hasException() {
return exception != null;
}
//省略set和get
}
二,定义消息协议和消息encode和decode
1,消息协议:
一个消息报文分为两部分:(4bytes)消息长度+消息主体。
2,TCP半包和粘包问题
发生的原因:
(1)应用程序写入的字节大小大于套接字发送的缓冲区大小
(2)进行MSS大小的TCP分段
(3)以太网的payload大于MTU进行IP分片
(4)接收端读取速度小于接受速度
Netty内置了LengthFieldBasedFrameDecoder处理读半包问题,但是为了了解如何处理,选择了使用ByteToMessageDecoder。
3,Decoder
因为选择ByteToMessageDecoder,所以会出现读半包问题,那么我们可以这样来解决:由于我们定义了4个byte来存储消息长度,所以如果可读byte小于4和消息主体长度跟消息长度不吻合,那么就先不读,以为这是个半包,可以等到下次再来读取。
public class RpcDecoder extends ByteToMessageDecoder {
private Class<?> genericClass;
public RpcDecoder(Class<?> genericClass) {
this.genericClass = genericClass;
}
@Override
public void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
if (in.readableBytes() < 4) {
return;
}
in.markReaderIndex();
int dataLength = in.readInt();
if (in.readableBytes() < dataLength) {
in.resetReaderIndex();
return;
}
byte[]