Netty学习笔记——Server端初始化
2019-11-03 21:08 CeddieCoding 阅读(499) 评论(0) 收藏 举报这段时间一直在学习Netty,在通过《Netty实战》一书学习了一些Netty的基础知识之后,便开始写一些简单的Server-Client交互Demo。一个Server端的代码大致如下:
1 EventLoopGroup bossGroup = new NioEventLoopGroup(); 2 EventLoopGroup workerGroup = new NioEventLoopGroup(); 3 try { 4 ServerBootstrap b = new ServerBootstrap(); 5 b.group(bossGroup, workerGroup) 6 .channel(NioServerSocketChannel.class) 7 .childHandler(new SimpleChatServerInitializer()) 8 .option(ChannelOption.SO_BACKLOG, 128) 9 .childOption(ChannelOption.SO_KEEPALIVE, true); 10 11 ChannelFuture f = b.bind(port).sync(); 12 f.channel().closeFuture().sync(); 13 } finally { 14 workerGroup.shutdownGracefully(); 15 bossGroup.shutdownGracefully(); 16 }
其中ServerBootStrap相当于Server端的引导,由其group方法指定了事件处理的线程组,每个group中都存在多个EventLoop,每个EventLoop都和某个Thread绑定,处理多个Channel(可以看做是Socket)。一旦一个Channel被分配给一个EventLoop,它将在它的整个生命周期中都是用这个EventLoop,这是Netty的基本线程模型。
理解了以上概念后,我在网上找了一些例子进行学习,发现大多数示例代码都通过重写io.netty.channel.ChannelHandlerAdapter.handlerAdded(ChannelHandlerContext)方法,来对连接Server的Client进行注册。从方法名字上看,似乎是当handler添加时被调用的回调,与Client的Channel毫不相干,所以它是如何生效的呢?带着这个疑问,我开始了源码的学习:
首先,从ChannelInitializer开始看,这个抽象类继承了ChannelInboundHandlerAdapter这个适配器类,所以它也算是一个Handler,它有一个需要被实现的抽象方法,initChannel,从调用栈上可以看到,当handlerAdded被调用时,此方法也同时被调用:
1 public void handlerAdded(ChannelHandlerContext ctx) throws Exception { 2 if (ctx.channel().isRegistered()) { 3 initChannel(ctx); 4 } 5 }
handlerAdded方法是在ChannelPipeline调用其addFirst/addLast方法时被调用(也有许多其他调用,但这两个方法最常用)。回过头来看,在引导Server端时调用了bind方法,进而调用doBind方法:
1 private ChannelFuture doBind(final SocketAddress localAddress) { 2 final ChannelFuture regFuture = initAndRegister(); 3 ... 4 }
doBind方法中的第一行调用了initAndRegister方法,在这个方法中又调用了init方法:
1 void init(Channel channel) throws Exception { 2 ... 3 ChannelPipeline p = channel.pipeline(); 4 5 final ChannelHandler currentChildHandler = childHandler; 6 ... 7 p.addLast(new ChannelInitializer<Channel>() { 8 @Override 9 public void initChannel(Channel ch) throws Exception { 10 final ChannelPipeline pipeline = ch.pipeline(); 11 ChannelHandler handler = config.handler(); 12 if (handler != null) { 13 pipeline.addLast(handler); 14 } 15 ch.eventLoop().execute(new Runnable() { 16 @Override 17 public void run() { 18 pipeline.addLast(new ServerBootstrapAcceptor( 19 currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs)); 20 } 21 }); 22 } 23 }); 24 }
其中,childHandler就是ServerBootStrap传入childHandler方法中的参数,p代表了与NioServerSocketChannel绑定的ChannelPipeLine。init方法向p中添加了一个ChannelInitializer,我在上面提到,当ChannelPipeline调用其addFirst/addLast方法时,handlerAdded会被调用,从而initChannel方法会被调用(此处不会被直接调用,而是延迟调用,但最终总会被调用)。在initChannel方法中,用NioServerSocketChannel的eventLoop(线程)执行了任务:向该pipeline中添加ServerBootstrapAcceptor,而此对象也是一个handler,重写了channelRead方法:
1 public void channelRead(ChannelHandlerContext ctx, Object msg) { 2 final Channel child = (Channel) msg; 3 child.pipeline().addLast(childHandler); 4 ... 5 }
由此可以看到,当有Client连接到Server时,Client的channel会调用其绑定pipeline的addLast方法,添加childHandler,而childHandler在前面提到过,就是ServerBootStrap传入childHandler方法中的参数,而此handler(或者说initializer)通过ChannelHandlerContext获取到pipeline,调用其addLast方法添加了自定义的handler,此pipeline就是Client的channel绑定的pipeline,当addLast方法被调用时,自定义handler的handlerAdded被调用,所以我们可以通过重写该方法来对连接Server的Client进行注册
浙公网安备 33010602011771号