Netty 是基于 Java NIO 的异步事件驱动的网络应用框架,使用 Netty 可以快速开发网络应用,下面以一段代码来说明一下Netty的启动流程。
public void start() {
ServerBootstrap bootstrap;
EventLoopGroup group;
MessageCollector collector;
Channel serverChannel;
//启动NIO服务的辅助启动类
bootstrap = new ServerBootstrap();
/**
1)用来接收进来的连接,数量如果不设置,默认为机器CPU核数的2倍,
bossGroup里面注册的是ServerChannel,用来接收接收客户端的连接请求
workerGroup里面注册的是Channel,即用来和客户端通信的channel
2)NioEventLoopGroup里面会创建ioThreads个NioEventLoop,
每个NioEventLoop内部是channel通信的逻辑,多个channel注册到一个selector,
NioEventLoop启动一个单实例线程池,这个单实例线程中执行一个方法,
这个方法在while循环中通过事件驱动的方式监听来自客户端的消息,同时执行本地队列中的任务。
*/
int ioThreads = 1;
EventLoopGroup bossGroup = new NioEventLoopGroup(ioThreads);
EventLoopGroup workerGroup = new NioEventLoopGroup();
bootstrap.group(bossGroup, workerGroup)
EchoServerHandler serverHandler = new EchoServerHandler();
MessageEncoder encoder = new MessageEncoder();
/**
配置Channel
1)传入NioServerSocketChannel这个类,Netty内部采用工厂模式来创建
NioServerSocketChannel实例。
2)通过childHandler这个方法将一个handlerContext加入到NioServerSocketChannel实例的
channelPipeline中,当服务器接收到客户端的连接请求时,会创建一个NioSocketChannel,
并把这个NioSocketChannel分配给一个NioEventLoop,在NioEventLoop注册到一个selector上,由这个NioEventLoop来管理Channel的通信。
3)ChannelInitializer是创建NioSocketChannel后向NioSocketChannel添加的创建ChannelPipeline的方法,主要包括4个handler:超时处理、解码器、编码器、业务处理。
*/
bootstrap.channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
//注册hander
ChannelPipeline pipe = ch.pipeline();
//如果客户端60秒没有任何请求,就关闭客户端连接
pipe.addLast(new ReadTimeoutHandler(60));
//加解码器
pipe.addLast(new MessageDecoder());
//编码器
pipe.addLast(encoder);
//将业务处理器放到最后
pipe.addLast(serverHandler);
}
});
bootstrap.option(ChannelOption.SO_BACKLOG, 100) //客户端套接字默认接受队列的大小
.option(ChannelOption.SO_REUSEADDR, true) //reuse addr 避免端口冲突
.option(ChannelOption.TCP_NODELAY, true) //关闭小流合并,保证消息的及时性
.childOption(ChannelOption.SO_KEEPALIVE, true); //长时间没动静的连接自动关闭
//绑定端口,开始接收进来的连接
serverChannel = bootstrap.bind(this.ip, this.port).channel();
LOG.warn("server started @ {}:{}\n", ip, port);
}
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ctx.write(msg);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
// Close the connection when an exception is raised.
cause.printStackTrace();
ctx.close();
}
}
//消息编码器
public class MessageEncoder extends MessageToMessageEncoder<MessageOutput> {
@Override
protected void encode(ChannelHandlerContext ctx, MessageOutput msg, List<Object> out) throws Exception {
ByteBuf buf = PooledByteBufAllocator.DEFAULT.directBuffer();
writeStr(buf, msg.getRequestId());
writeStr(buf, msg.getType());
writeStr(buf, JSON.toJSONString(msg.getPayload()));
out.add(buf);
}
private void writeStr(ByteBuf buf, String s) {
buf.writeInt(s.length());
buf.writeBytes(s.getBytes(Charsets.UTF8));
}
}
//消息解码器
//使用Netty的ReplayingDecoder实现。
public class MessageDecoder extends ReplayingDecoder<MessageInput> {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
String requestId = readStr(in);
String type = readStr(in);
String content = readStr(in);
out.add(new MessageInput(type, requestId, content));
}
private String readStr(ByteBuf in) {
int len = in.readInt();
if (len < 0 || len > (1 << 20)) {
throw new DecoderException("string too long len=" + len);
}
byte[] bytes = new byte[len];
in.readBytes(bytes);
return new String(bytes, Charsets.UTF8);
}
}
上面是一个Netty的Server端的启动流程,通过这个可以看出使用Netty做网络通信的基本原理。下面通过一张图来详细说明: