Netty中Reactor模型的实现
在Netty中,能够同时支持单线程,多线程和主从Reactor三种模式:
下图为Netty的线程模型:
以常用的Netty代码举例分析:
// 配置服务端的NIO线程组 EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 100) .handler(new LoggingHandler(LogLevel.INFO)) .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) { // ch.pipeline().addLast( // new ProtobufVarint32FrameDecoder()); ch.pipeline().addLast( new ProtobufDecoder( SubscribeReqProto.SubscribeReq .getDefaultInstance())); ch.pipeline().addLast( new ProtobufVarint32LengthFieldPrepender()); ch.pipeline().addLast(new ProtobufEncoder()); ch.pipeline().addLast(new SubReqServerHandler()); } }); // 绑定端口,同步等待成功 ChannelFuture f = b.bind(port).sync(); // 等待服务端监听端口关闭 f.channel().closeFuture().sync(); } finally { // 优雅退出,释放线程池资源 bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); }
以上服务端启动,创建了两个EventLoopGroup,实际上就是两个Selector线程组。其中boss线程组负责接收客户端连接,work线程组负责处理IO操作和系统Task和定时任务的调度。每个EventLoopGroup里面都维护了一个selector。
为了减少锁竞争,Netty的work线程组处理IO操作时,都是单线程的,如图下图所示:
如图,work线程组接收到读事件时,将读事件往pipeline从头到尾传递,处理完数据后,将写事件往pipeline从尾到头传递,整个过程都是同一条线程,避免了多线程操作,也避免了锁竞争带来的性能消耗。
EventLoopGroup的简析:
EventLoopGroup是一个不仅具备IO操作线程,还具体系统Task和定时任务的调度的功能的线程组。
EventLoopGroup内部维护了一个selector,因此会出现空轮询的bug,netty通过周期性的对select()为0的情况进行计数,当出现次数达到一定值,
则认为出现空轮询的bug,此时会重新打开一个新的selector,将旧的替换掉。