netty的标准设计流程为:编码,解码,检测,链接,其他handler,业务。按照这个流程将rocketmq的netty的实现流程进行细化。
编码
NettyEncoder
继承MessageToByteEncoder,netty的编码规范要求
将RemotingCommand的请求数据结构数据头:存数据的长度描述;数据体:存数据的内容
RemotingCommand的功能强大:涉及ByteBuffer的频繁操作,实现数据协议的转换
整体的协议格式:<length><header length><header data><body data>
1,4个字节的int型数据存储数据的总长度
2,4个字节的int型数据存储报文头的字节长度
3,存储的头部数据内容
4,存储的报文的数据内容
public class NettyEncoder extends MessageToByteEncoder<RemotingCommand> {
private static final InternalLogger log = InternalLoggerFactory.getLogger(RemotingHelper.ROCKETMQ_REMOTING);
@Override
public void encode(ChannelHandlerContext ctx, RemotingCommand remotingCommand, ByteBuf out)
throws Exception {
try {
//获得请求体消息头,封装为byte的协议要求
ByteBuffer header = remotingCommand.encodeHeader();
//写入头
out.writeBytes(header);
//获得消息体
byte[] body = remotingCommand.getBody();
if (body != null) {
//写入协议
out.writeBytes(body);
}
} catch (Exception e) {
log.error("encode exception, " + RemotingHelper.parseChannelRemoteAddr(ctx.channel()), e);
if (remotingCommand != null) {
log.error(remotingCommand.toString());
}
RemotingUtil.closeChannel(ctx.channel());
}
}
}
解码
NettyDecoder
继承LengthFieldBasedFrameDecoder,netty的编码要求,该实现是基于长度的要求解决拆包粘包问题
将ByteBuf的数据输入按照长度的要求,数据解码到ByteBuf的存储体内
将netty封装的ByteBuf对象转换为java的Nio的ByteBuffer对象:ByteBuf.nioBuffer()
所有的操作都是基于RemotingCommand的decode来实现
解码的操作过程基于编码的规范来
1,获得数据的最大有效位置
2,获得第一个int位的值,该值为数据长度,包括头数据和报文数据
3,byteBuffer操作获得头部的数据
4,将数据的最大有效位置减去 4(标识),再减去头数据的长度,得到报文的长度
5,根据报文长度,byteBuffer操作获得报文长度
public class NettyDecoder extends LengthFieldBasedFrameDecoder {
private static final InternalLogger log = InternalLoggerFactory.getLogger(RemotingHelper.ROCKETMQ_REMOTING);
//基于长度的解码器,定义长度的标准,友好处理粘包及拆包
private static final int FRAME_MAX_LENGTH =
Integer.parseInt(System.getProperty("com.rocketmq.remoting.frameMaxLength", "16777216"));
public NettyDecoder() {
super(FRAME_MAX_LENGTH, 0, 4, 0, 4);
}
@Override
public Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
ByteBuf frame = null;
try {
//基于父类的解码,共性操作
frame = (ByteBuf) super.decode(ctx, in);
if (null == frame) {
return null;
}
//将netty的bytebuf转换为java标准的butebuffer
ByteBuffer byteBuffer = frame.nioBuffer();
//按照编码协议,将结果转换为对象
return RemotingCommand.decode(byteBuffer);
} catch (Exception e) {
log.error("decode exception, " + RemotingHelper.parseChannelRemoteAddr(ctx.channel()), e);
RemotingUtil.closeChannel(ctx.channel());
} finally {
if (null != frame) {
frame.release();
}
}
return null;
}
}
Idle
netty的系统的IdleStateHandler,操作,只是配置了指定的最大idle时间
可以实现心跳的检测
链接管理
NettyConnectManageHandler
继承ChannelDuplexHandler,netty的输入输入及写出的标准设计
将重点对服务的注册,链接激活,链接关闭,链接的检测,链接的操作异常进行管理
链接激活:如果本机的监听存在则设置到永久循环队列中,之间内部业务管理,事件为链接
激活关闭:如果本机的监听存在则设置到永久循环队列中,之间内部业务管理,事件为关闭
链接检测:如果本机的监听存在则设置到永久循环队列中,之间内部业务管理,事件为检测
异常操作:如果本机的监听存在则设置到永久循环队列中,之间内部业务管理,事件为异常
底层是基于无限循环的操作队列内容,对事件进行操作,基于接口设计,不同的服务端有不同的业务需求
class NettyConnectManageHandler extends ChannelDuplexHandler {
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
final String remoteAddress = RemotingHelper.parseChannelRemoteAddr(ctx.channel());
log.info("NETTY SERVER PIPELINE: channelRegistered {}", remoteAddress);
super.channelRegistered(ctx);
}
@Override
public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
final String remoteAddress = RemotingHelper.parseChannelRemoteAddr(ctx.channel());
log.info("NETTY SERVER PIPELINE: channelUnregistered, the channel[{}]", remoteAddress);
super.channelUnregistered(ctx);
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
final String remoteAddress = RemotingHelper.parseChannelRemoteAddr(ctx.channel());
log.info("NETTY SERVER PIPELINE: channelActive, the channel[{}]", remoteAddress);
super.channelActive(ctx);
//处理连接的事件
if (NettyRemotingServer.this.channelEventListener != null) {
NettyRemotingServer.this.putNettyEvent(new NettyEvent(NettyEventType.CONNECT, remoteAddress, ctx.channel()));
}
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
final String remoteAddress = RemotingHelper.parseChannelRemoteAddr(ctx.channel());
log.info("NETTY SERVER PIPELINE: channelInactive, the channel[{}]", remoteAddress);
super.channelInactive(ctx);
//处理连接关闭的事件
if (NettyRemotingServer.this.channelEventListener != null) {
NettyRemotingServer.this.putNettyEvent(new NettyEvent(NettyEventType.CLOSE, remoteAddress, ctx.channel()));
}
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
IdleStateEvent event = (IdleStateEvent) evt;
if (event.state().equals(IdleState.ALL_IDLE)) {
final String remoteAddress = RemotingHelper.parseChannelRemoteAddr(ctx.channel());
log.warn("NETTY SERVER PIPELINE: IDLE exception [{}]", remoteAddress);
RemotingUtil.closeChannel(ctx.channel());
if (NettyRemotingServer.this.channelEventListener != null) {
//处理检测的事件
NettyRemotingServer.this
.putNettyEvent(new NettyEvent(NettyEventType.IDLE, remoteAddress, ctx.channel()));
}
}
}
ctx.fireUserEventTriggered(evt);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
final String remoteAddress = RemotingHelper.parseChannelRemoteAddr(ctx.channel());
log.warn("NETTY SERVER PIPELINE: exceptionCaught {}", remoteAddress);
log.warn("NETTY SERVER PIPELINE: exceptionCaught exception.", cause);
//处理异常的事件
if (NettyRemotingServer.this.channelEventListener != null) {
NettyRemotingServer.this.putNettyEvent(new NettyEvent(NettyEventType.EXCEPTION, remoteAddress, ctx.channel()));
}
RemotingUtil.closeChannel(ctx.channel());
}
}
业务处理
NettyServerHandler:服务端;NettyClientHandler:客户端
都继承SimpleChannelInboundHandler,基于netty的标准规范,主要实现数据的接受
实现channelRead0方法,实现标准的数据接收
封装服务端和客户端共享的方法processMessageReceived来处理数据
处理数据中根据数据类型区分为请求类型和相应类型,如果是服务端,更多的处理请求数据,如果是发送端更多的是出来响应数据
如果是请求数据,内部将不同的事件封装成事件处理模型,根据事件类型获得对应的事件模型来处理
如果是响应数据,主要是针对前面的请求数据,进行结果的设置
class NettyServerHandler extends SimpleChannelInboundHandler<RemotingCommand> {
//读取网络请求
@Override
protected void channelRead0(ChannelHandlerContext ctx, RemotingCommand msg) throws Exception {
processMessageReceived(ctx, msg);
}
}
=========================================================================================
下面将协议的实现细节增加介绍,主要是编码和解码部分
编码消息头的对象
public ByteBuffer encodeHeader() {
return encodeHeader(this.body != null ? this.body.length : 0);
}
public ByteBuffer encodeHeader(final int bodyLength) {
// 1> header length size
//固定长度,头部长度的设置
int length = 4;
// 2> header data length
//编码头部内容为byte数组,并获得长度
byte[] headerData;
headerData = this.headerEncode();
//增加头部长度
length += headerData.length;
// 3> body data length
//增加报文的长度
length += bodyLength;
//数据头的总长度
ByteBuffer result = ByteBuffer.allocate(4 + length - bodyLength);
// length 数据的总长度
result.putInt(length);
// header length
result.put(markProtocolType(headerData.length, serializeTypeCurrentRPC));
// header data
result.put(headerData);
result.flip();
return result;
}
解码是基于编码的规范要求和实现进行逆操作
public static RemotingCommand decode(final ByteBuffer byteBuffer) {
//获得消息走长度
int length = byteBuffer.limit();
//获得第一个int位值
int oriHeaderLen = byteBuffer.getInt();
//获取消息头内容的长度
int headerLength = getHeaderLength(oriHeaderLen);
//获得消息头内容
byte[] headerData = new byte[headerLength];
byteBuffer.get(headerData);
//将消息头解码为对象
RemotingCommand cmd = headerDecode(headerData, getProtocolType(oriHeaderLen));
//获得报文体的长度
int bodyLength = length - 4 - headerLength;
byte[] bodyData = null;
if (bodyLength > 0) {
bodyData = new byte[bodyLength];
byteBuffer.get(bodyData);
}
cmd.body = bodyData;
return cmd;
}