nioeventloopgroup 多少比较好

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了nioeventloopgroup 多少比较好相关的知识,希望对你有一定的参考价值。

参考技术A 相比Netty3,Netty4有很多显著的变化:NioEventLoopGroup是一个处理I/O操作的多线程事件环。即为Netty4里的线程池,在3.x里,一个Channel是由ChannelFactory创建的,同时新创建的Channel会自动注册到一个隐藏的I/O线程。4.0使用新的名为EventLoopGroup的接口来替换ChannelFactory,它由一个或多个EventLoop来构成。一个新的Channel不会自动注册到EventLoopGroup,但用户可以显式调用EventLoopGroup.register()来注册。在Server端的Bootstrap参数中,有两个EventLoopGroup,第一个通常称为'boss',用于接收发来的连接请求。第二个称为'worker',,用于处理boss接受并且注册给worker的连接中的信息。ChannelInitializer是一个特殊的handler,用于方便的配置用户自定义的handler实现,如代码中所示。在channelRegistered的生命周期中会触发用户复写的initChannel(Cch)方法,并且在调用后会讲自身从channelPipeline中移除。代码示例?importio.netty.bootstrap.ServerBootstrap;importio.netty.channel.ChannelFuture;importio.netty.channel.ChannelHandlerContext;importio.netty.channel.ChannelInboundHandlerAdapter;importio.netty.channel.ChannelInitializer;importio.netty.channel.ChannelOption;importio.netty.channel.ChannelPipeline;importio.netty.channel.EventLoopGroup;importio.netty.channel.nio.NioEventLoopGroup;importio.netty.channel.socket.SocketChannel;importio.netty.channel.socket.nio.NioserverSocketChannel;importio.netty.handler.codec.serialization.ClassResolvers;importio.netty.handler.codec.serialization.ObjectDecoder;importio.netty.handler.codec.serialization.ObjectEncoder;importio.netty.handler.logging.LogLevel;importio.netty.handler.logging.LoggingHandler;importio.netty.handler.ssl.SslContext;importio.netty.handler.ssl.util.SelfSignedCertificate;/***DateTime:2015年1月5日上午9:56:10**/publicclassHelloWorldServerstaticfinalbooleanSSL=System.getProperty("ssl")!=null;staticfinalintPORT=Integer.parseInt(System.getProperty("port","8007"));publicstaticvoidmain(String[]args)throwsException//ConfigureSSL.finalSslContextsslCtx;if(SSL)SelfSignedCertificatessc=newSelfSignedCertificate();sslCtx=SslContext.newServerContext(ssc.certificate(),ssc.privateKey());elsesslCtx=null;//Configuretheserver.EventLoopGroupbossGroup=newNioEventLoopGroup(1);EventLoopGroupworkerGroup=newNioEventLoopGroup();tryServerBootstrapb=newServerBootstrap();b.group(bossGroup,workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG,100).handler(newLoggingHandler(LogLevel.INFO)).childHandler(newChannelInitializer()@OverridepublicvoidinitChannel(SocketChannelch)throwsExceptionChannelPipelinep=ch.pipeline();if(sslCtx!=null)p.addLast(sslCtx.newHandler(ch.alloc()));p.addLast(newLoggingHandler(LogLevel.INFO));p.addLast(newObjectEncoder(),newObjectDecoder(ClassResolvers.cacheDisabled(null)),newHelloWorldServerHandler()););//Starttheserver.ChannelFuturef=b.bind(PORT).sync();//Waituntiltheserversocketisclosed.f.channel().closeFuture().sync();finally//Shutdownalleventloopstoterminateallthreads.bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();classHelloWorldServerHandlerextendsChannelInboundHandlerAdapter@OverridepublicvoidchannelRead(ChannelHandlerContextctx,Objectmsg)ctx.write("serverwritemsg:"+msg);@OverridepublicvoidchannelReadComplete(ChannelHandlerContextctx)ctx.flush();@OverridepublicvoidexceptionCaught(ChannelHandlerContextctx,Throwablecause)//Closetheconnectionwhenanexceptionisraised.cause.printStackTrace();ctx.close();importio.netty.bootstrap.Bootstrap;importio.netty.channel.ChannelFuture;importio.netty.channel.ChannelHandlerContext;importio.netty.channel.ChannelInboundHandlerAdapter;importio.netty.channel.ChannelInitializer;importio.netty.channel.ChannelOption;importio.netty.channel.ChannelPipeline;importio.netty.channel.EventLoopGroup;importio.netty.channel.nio.NioEventLoopGroup;importio.netty.channel.socket.SocketChannel;importio.netty.channel.socket.nio.NioSocketChannel;importio.netty.handler.codec.serialization.ClassResolvers;importio.netty.handler.codec.serialization.ObjectDecoder;importio.netty.handler.codec.serialization.ObjectEncoder;importio.netty.handler.ssl.SslContext;importio.netty.handler.ssl.util.InsecureTrustManagerFactory;/***DateTime:2015年1月5日上午9:56:22**/publicclassHelloWorldClientstaticfinalbooleanSSL=System.getProperty("ssl")!=null;staticfinalStringHOST=System.getProperty("host","127.0.0.1");staticfinalintPORT=Integer.parseInt(System.getProperty("port","8007"));publicstaticvoidmain(String[]args)throwsException//ConfigureSSL.gitfinalSslContextsslCtx;if(SSL)sslCtx=SslContext.newClientContext(InsecureTrustManagerFactory.INSTANCE);elsesslCtx=null;//Configuretheclient.EventLoopGroupgroup=newNioEventLoopGroup();tryBootstrapb=newBootstrap();b.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY,true).handler(newChannelInitializer()@OverridepublicvoidinitChannel(SocketChannelch)throwsExceptionChannelPipelinep=ch.pipeline();if(sslCtx!=null)p.addLast(sslCtx.newHandler(ch.alloc(),HOST,PORT));p.addLast(newObjectEncoder(),newObjectDecoder(ClassResolvers.cacheDisabled(null)),newHelloWorldClientHandler()););//Starttheclient.ChannelFuturef=b.connect(HOST,PORT).sync();//Waituntiltheconnectionisclosed.f.channel().closeFuture().sync();finally//Shutdowntheeventlooptoterminateallthreads.group.shutdownGracefully();classHelloWorldClientHandlerextendsChannelInboundHandlerAdapterprivatefinalStringmsg="hellojavaworld";/***Createsaclient-sidehandler.*/publicHelloWorldClientHandler()//TODO@OverridepublicvoidchannelActive(ChannelHandlerContextctx)ctx.writeAndFlush(msg);@OverridepublicvoidchannelRead(ChannelHandlerContextctx,Objectmsg)System.out.println(msg);//ctx.write(msg);@OverridepublicvoidchannelReadComplete(ChannelHandlerContextctx)ctx.flush();@OverridepublicvoidexceptionCaught(ChannelHandlerContextctx,Throwablecause)//Closetheconnectionwhenanexceptionisraised.cause.printStackTrace();ctx.close();

Netty4.XNetty源码分析之NioEventLoopGroup

NioEventLoopGroup实际是NioEventLoop的线程组,主要负责管理EventLoop的生命周期,EventLoop的默认大小是2倍的CPU核数,但这并不是一个恒定的最佳数量,为了避免线程上下文切换,只要能满足要求,这个值其实越小越好。

继承关系如下:

首先看NioEventLoopGroup构造方法:

public NioEventLoopGroup() 
    this(0);


public NioEventLoopGroup(int nThreads) 
    this(nThreads, null);


public NioEventLoopGroup(int nThreads, ThreadFactory threadFactory) 
    this(nThreads, threadFactory, SelectorProvider.provider());


public NioEventLoopGroup(
        int nThreads, ThreadFactory threadFactory, final SelectorProvider selectorProvider) 
    super(nThreads, threadFactory, selectorProvider);

从以上代码可以发现NioEventLoopGroup虽然有4个构造方法,但最终调用的是MultithreadEventLoopGroup的构造方法,代码如下:

protected MultithreadEventLoopGroup(int nThreads, ThreadFactory threadFactory, Object... args)   
    super(nThreads == 0? DEFAULT_EVENT_LOOP_THREADS : nThreads, threadFactory, args);  


private static final int DEFAULT_EVENT_LOOP_THREADS;

    static 
        DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
                "io.netty.eventLoopThreads", Runtime.getRuntime().availableProcessors() * 2));

        if (logger.isDebugEnabled()) 
            logger.debug("-Dio.netty.eventLoopThreads: ", DEFAULT_EVENT_LOOP_THREADS);
        
    

在NioEventLoopGroup初始化之前,会先执行父类MultithreadEventLoopGroup的静态模块,其中DEFAULT_EVENT_LOOP_THREADS的大小默认是CPU核数的两倍,就是文章开头说过的。

MultithreadEventExecutorGroup的构造方法:

//EventExecutor数组,保存eventLoop
private final EventExecutor[] children;
//从children中选取一个eventLoop的策略
private final EventExecutorChooser chooser;

protected MultithreadEventExecutorGroup(int nThreads, ThreadFactory threadFactory, Object... args) 
    if (nThreads <= 0) 
        throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
    

    if (threadFactory == null) 
        //是一个通用的ThreadFactory实现,方便配置线程池
        threadFactory = newDefaultThreadFactory();
    

    //根据线程数来创建SingleThreadEventExecutor数组
    //从命名上可以看出SingleThreadEventExecutor是一个只有一个线程的线程池
    children = new SingleThreadEventExecutor[nThreads];
    //根据children数组的大小,采用不同策略初始化chooser。
    if (isPowerOfTwo(children.length)) 
        chooser = new PowerOfTwoEventExecutorChooser();
     else 
        chooser = new GenericEventExecutorChooser();
    

    for (int i = 0; i < nThreads; i ++) 
        boolean success = false;
        try 
            //
            children[i] = newChild(threadFactory, args);
            success = true;
         catch (Exception e) 
            // TODO: Think about if this is a good exception type
            throw new IllegalStateException("failed to create a child event loop", e);
         finally 
            //如果没有创建成功,循环关闭所有SingleThreadEventExecutor
            if (!success) 
                for (int j = 0; j < i; j ++) 
                    children[j].shutdownGracefully();
                

                //等待关闭成功
                for (int j = 0; j < i; j ++) 
                    EventExecutor e = children[j];
                    try 
                        while (!e.isTerminated()) 
                            e.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);
                        
                     catch (InterruptedException interrupted) 
                        Thread.currentThread().interrupt();
                        break;
                    
                
            
        
    

    final FutureListener<Object> terminationListener = new FutureListener<Object>() 
        @Override
        public void operationComplete(Future<Object> future) throws Exception 
            if (terminatedChildren.incrementAndGet() == children.length) 
                terminationFuture.setSuccess(null);
            
        
    ;

    for (EventExecutor e: children) 
        e.terminationFuture().addListener(terminationListener);
    

回到NioEventLoopGroup的newChild方法重载

@Override
protected EventExecutor newChild(
        ThreadFactory threadFactory, Object... args) throws Exception 
    return new NioEventLoop(this, threadFactory, (SelectorProvider) args[0]);

MultithreadEventExecutorGroup构造方法中执行的是NioEventLoopGroup中的newChild方法,所以children元素的实际类型是NioEventLoop。

解释下EventExecutorChooser的选择

    //判断一个数是否是2的幂次方
    private static boolean isPowerOfTwo(int val) 
        return (val & -val) == val;
    

    private final class PowerOfTwoEventExecutorChooser implements EventExecutorChooser 
        @Override
        public EventExecutor next() 
            return children[childIndex.getAndIncrement() & children.length - 1];
        
    

    private final class GenericEventExecutorChooser implements EventExecutorChooser 
        @Override
        public EventExecutor next() 
            return children[Math.abs(childIndex.getAndIncrement() % children.length)];
        
    

它是根据线程数组大小是否是2的幂次方来选择初始化chooser。如果大小为2的幂次方,则采用PowerOfTwoEventExecutorChooser,否则使用GenericEventExecutorChooser。也就是如果线程数是2的倍数时,Netty选择线程时会使用PowerOfTwoEventExecutorChooser,因为&比%更快(Netty为了性能也是拼了)。

作者:小毛驴,一个游戏人 
梦想:世界和平   
github主页:https://liulongling.github.io/
csdn主页:http://blog.csdn.net/liulongling
简书主页:http://www.jianshu.com/u/1342f21e1748
若有错误之处,请多多谅解并欢迎批评指正。    
本博客中未标明转载的文章归作者小毛驴所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

以上是关于nioeventloopgroup 多少比较好的主要内容,如果未能解决你的问题,请参考以下文章

PVEV和AC

面试官:NIOEventLoopGroup源码?

Netty中NioEventLoopGroup的创建源码分析

线程组 NioEventLoopGroup

Netty的NioEventLoop和NioEventLoopGroup是什么关系?

服务器一般达到多少qps比较好