**netty的线程模型**。
netty绑定端口后,开始监听连接。
当一个连接进来后,先是由boss线程来处理accept的操作(也就是nioeventloop调度boss
线程处理,后面会细讲),当accept成功后则可以互相通讯了。如果有数据进来则将连接交给work
线程来执行读的操作,netty将数据处理为Bytebuf形式交给(其实是交给channelpipeline并且由它
来调用channelhandler)socketchannnel中的channelhandler(在连接成功后会触发channelhandler
的channelActive方法,在这里可以做一些初始的工作),这就是为什么说boss线程池处理连接,
work线程池处理io读写,也被说为io读写线程池。
当把数据交给 work线程,通常会对进来的数据解码,这就会用到解码器,在笔者的一篇
文章中详细讲解了解码器的基础,有读者不怎么了解的可以去看看。数据编码后,netty会将数据传
给下一个handler.一般我们自己写的handler会放在最后一个,里面有我们业务操作的代码,一般项
目中在这里也是将数据投入业务处理池的位置。最后业务处理好后会想要把数据发送给客户端,所
以又会经过outhandler对数据进行解码给底层socket发送,这个操作又会交给work线程完成,work
线程会调用channel进行写操作即发送数据给
客户端。(也就是channel底层socket发送数据)
当客户端断开连接后又会触发channelhandler的channelInActive方法。学习过Java nio的读
者应该知道reactor模式,netty也是这个模式。Java nio 中有一个selector,即多路复用器。在netty
中nioeventloopGroup充当selector的角色,nioeventloop是调度类,他负责着netty大大小小事情,
netty是事件驱动的异步框架,所以在事件驱动中也有它的影子。nioeventloopGroup包含一个或多
个nioeventloop。(本篇博客是对netty大体框架进行一个分析,细节之后博客会有详解,比如这个
调度的细节)。
一个连接对应这一个channel,一个channel对应一个nioeventloop,但是一个nioeventloop对应一
个或者多个channel。Java nio是单线程调度,而netty是多线程维护有着更好的并发处理性能。
public class Server {
public void stratServer(int port) throws InterruptedException {
EventLoopGroup boss = new NioEventLoopGroup();//里面维护这boss线程池;
EventLoopGroup work = new NioEventLoopGroup();//里面维护这work线程池
try {
ServerBootstrap sp = new ServerBootstrap();//netty启动辅助类
sp.group(boss, work);
sp.channel(NioServerSocketChannel.class);//涉及到netty启动时用那个来初始化channel,在不同模式下用的类不同,一般都是NioServerSocketChannel
sp.option(ChannelOption.SO_BACKLOG, 1024); // 连接数
sp.option(ChannelOption.TCP_NODELAY, true); // 不延迟,消息立即发送//
sp.childOption(ChannelOption.SO_KEEPALIVE, true); // 长连接
sp.childHandler(new ChannelInitializer<SocketChannel>() {// 在初始化channel的时候会用到这个,调用initchannel方法装载handler,并且剔除自身即ChannelInitializer(本身也是个handler)
@Override
protected void initChannel(SocketChannel ch)
throws Exception {
ByteBuf tr=Unpooled.copiedBuffer("@".getBytes());
ch.pipeline().addLast(new DelimiterBasedFrameDecoder(2048,tr));//netty自带的解码器
ch.pipeline().addLast(new Handler());//用户自定义的解码器
}
});
sp.bind(port).sync().channel().closeFuture().sync();//绑定端口,这个看起来吓人其实就是绑定端口的操作sync就是堵塞的意思,当操作完成后才不堵塞。,比如第一个sync方法指一直阻塞到bind操
完成。第二个指一直阻塞到channel关闭后才不阻塞。
} catch (Exception e) {
} finally {
//完美的释放内存
boss.shutdownGracefully();
work.shutdownGracefully();
}
}
public static void main(String[] args) throws InterruptedException {
new Server().stratServer(11232);
}
}
下面这个就是笔者自定义的handler,业务入口就在channelRead0方法中。
public class Handler extends SimpleChannelInboundHandler<Object>{
/**
* JFree_Wolf
*/
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {//接受数据,处理数据
ByteBuf by=(ByteBuf)msg;
System.out.println(ctx.channel().remoteAddress().toString());
System.out.println("by大小为 "+by.readableBytes());
//ReferenceCountUtil.release("12");
//ctx.executor();
// ctx.channel().config().setWriteBufferLowWaterMark(12);
ctx.channel().writeAndFlush("123\n").addListener(new ChannelFutureListener(){
public void operationComplete(ChannelFuture future) throws Exception {
System.out.println("IO操作完成");
}
});
// ctx.fireChannelRead(msg);
}
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("================success connect");
super.channelActive(ctx);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("server niosokcetchannel close ");
System.out.println(t);
super.channelInactive(ctx);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx,Throwable cause){
cause.printStackTrace();
//handler发生错误则会调用次方法。
ctx.close();
}
}
这是笔者学习netty时写的demo,用来研究 DelimiterBasedFrameDecoder解码器的
用法的。 在这里推荐新手不要直接复制,这样对你的理解没有帮助,哪怕你照着代码看注释敲
一遍都对你理解比较好。学习Java,敲代码是必须的,哪怕照着敲一遍!另外这篇博客是笔者
对于netty大体框架的一个总结。但是很有很多没有解释,比如channelhadler,socketchannnel
的细节处理,他的父类,接口的用处,eventloop调用的细节,tcp/ip连接的细节 等等,这些在
之后有时间笔者会好好写一下,和大家做个交流。