1.Netty
Netty是由JBOSS提供的一个java开源框架。在吸收了FTP,SMTP,HTTP,各种二进制,文本协议等多种协议的实现经验,并经过设计相当精心的项目后,Netty最终成功地找到了一种方式,在保证易于开发的同时还保证了其应用的性能,稳定性和伸缩性。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。Netty是一个基于NIO的客户,服务器端编程框架,使用Netty可以确保你快速和简单的开发出一个网络应用。
建议参考《Netty权威指南》或《Netty In Action》,后者已有中文翻译版本在此
2.依赖配置
本例使用netty 4.0.23.Final实现网络通信
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.0.23.Final</version>
<scope>compile</scope>
</dependency>
使用maven直接在pom.xml中添加上面的依赖即可
3.ChannelHandler设计
本例情景相对简单,在客户端和服务端均只有3个ChannelHandler。两者均有一个NettyKryoEncoder和一个NettyKryoDecoder。其中NettyKryoEncoder用于将发送的对象序列化为字节序列,是一个ChannelInboundHandler。而NettyKryoDecoder用于从接收到的字节序列还原出对象,是一个ChannelOutboundHandler。
客户端还有一个RpcClientDispatchHandler,用于接收到服务端返回的对象时(实际应用场景中只可能为RpcResponse对象)进行事件的分发。与之类似,服务端设有一个RpcServerDispatchHandler,用于接收到客户端发送的对象时(实际应用场景中只可能为RpcRequest对象)进行事件分发。
(1).NettyKryoEncoder
package com.maigo.rpc.netty;
import com.maigo.rpc.serializer.KryoSerializer;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
public class NettyKryoEncoder extends MessageToByteEncoder<Object>
{
@Override
protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out)
throws Exception
{
KryoSerializer.serialize(msg, out);
}
}
直接调用上一节实现的KryoSerializer进行序列化
(2).NettyKryoDecoder
package com.maigo.rpc.netty;
import com.maigo.rpc.serializer.KryoSerializer;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
public class NettyKryoDecoder extends LengthFieldBasedFrameDecoder
{
public NettyKryoDecoder()
{
super(1048576, 0, 4, 0, 4);
}
@Override
protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception
{
ByteBuf frame = (ByteBuf) super.decode(ctx, in);
if (frame == null)
return null;
return KryoSerializer.deserialize(frame);
}
@Override
protected ByteBuf extractFrame(ChannelHandlerContext ctx, ByteBuf buffer, int index, int length)
{
return buffer.slice(index, length);
}
}
使用LengthFieldBasedFrameDecoder进行解码,解码依赖于字节序列的头部的4个字节中存放的长度信息(KryoSerializer.serialize()方法会在头部写入长度信息,详见上一节)。获取到长度信息后使用父类的decode()方法截取出实际的字节序列,直接调用KryoSerializer的deserialize()方法反序列化还原出对象。
(3).RpcClientDispatchHandler
package com.maigo.rpc.client;
import com.maigo.rpc.context.RpcResponse;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class RpcClientDispatchHandler extends ChannelInboundHandlerAdapter
{
private RpcClientResponseHandler rpcClientResponseHandler;
private RpcClientChannelInactiveListener rpcClientChannelInactiveListener = null;
public RpcClientDispatchHandler(
RpcClientResponseHandler rpcClientResponseHandler,
RpcClientChannelInactiveListener rpcClientChannelInactiveListener)
{
this.rpcClientResponseHandler = rpcClientResponseHandler;
this.rpcClientChannelInactiveListener = rpcClientChannelInactiveListener;
}
@Override
public void channelRead(