天天看点

通过走读netty源码来学习netty(一)(基于netty-4.0.19.Final)

本文基于netty-4.0.19.Final包

本文使用netty事例包中的EchoServer.java类来进行分析

先看EchoServer.java中run方法代码

public void run() throws Exception {
        // Configure the server.
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        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) throws Exception {
                     ch.pipeline().addLast(
                             //new LoggingHandler(LogLevel.INFO),
                             new EchoServerHandler());
                 }
             });

            // Start the server.
            ChannelFuture f = b.bind(port).sync();

            // Wait until the server socket is closed.
            f.channel().closeFuture().sync();
        } finally {
            // Shut down all event loops to terminate all threads.
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
           

 为什么两个group?它们有什么作用?为什么一个handler?为什么又来一个childHandler?

先不管它,直接看重点的bind方法

进入bind方法后,最终会调用到AbstractBootstrap.java中doBind方法,然后调用initAndRegister方法

final ChannelFuture regFuture = initAndRegister();
           

 进入initAndRegister方法后,发现先生成了一个新的channel

final Channel channel = channelFactory().newChannel();
           

 channelFactory如何创建的?

在EchoServer.java中run方法中发现调用了一个channel方法,channel方法中就会调用channelFactory创建

 BootstrapChannelFactory对象的实例,由于是调用的channelFactory().newChannel()方法,我们进入BootstrapChannelFactory类查看newChannel方法,会发现其实是通过反射创建了一个NioServerSocketChannel类的实例

public T newChannel() {
            try {
                return clazz.newInstance();
            } catch (Throwable t) {
                throw new ChannelException("Unable to create Channel from class " + clazz, t);
            }
        }
           

接着看initAndRegister方法

initAndRegister方法中调用了init方法,查看发现init方法是抽象方法,在ServerBootstrap类中实现

try {
            init(channel);
        } catch (Throwable t) {
            channel.unsafe().closeForcibly();
            return channel.newFailedFuture(t);
        }
           

查看ServerBootstrap类的init方法,发现做了两件事情

1、设置了options和attrs,这两个选项在EchoServer类的run方法中没有调用,先忽略

2、在channel的pipeline中增加了两个ChannelHandler,第一个是EchoServer类的run方法中指定的LoggingHandler,第二个是ChannelInitializer

继续看initAndRegister方法

调用group的register方法,而group其实是EchoServer类中run方法中调用group方法设置的NioEventLoopGroup,另外一个为group为childGroup,是定义在ServerBootstrap类中变量,目前还没有用到。

在NioEventLoopGroup方法中查找register方法,发现在父类MultithreadEventLoopGroup中有定义,于是我们跳到了MultithreadEventLoopGroup类的register方法中

MultithreadEventLoopGroup中register方法很简单,委派给EventLoop的register方法,而获取EventLoop是通过调用父类的next方法来做到的

@Override
    public EventLoop next() {
        return (EventLoop) super.next();
    }

    @Override
    public ChannelFuture register(Channel channel) {
        return next().register(channel);
    }
           

 现在我们来到了父类MultithreadEventExecutorGroup的next方法中

@Override
    public EventExecutor next() {
        return chooser.next();
    }
           

 chooser是在MultithreadEventExecutorGroup构造方法中实例化的,查看构造方法,发现chooser的构造又和children数组有关系,而children数组长度是由传入的nThreads来决定的

protected MultithreadEventExecutorGroup(int nThreads, ThreadFactory threadFactory, Object... args) {

        if (threadFactory == null) {
            threadFactory = newDefaultThreadFactory();
        }

        children = new SingleThreadEventExecutor[nThreads];
        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);
            }
        }
           

 那nThreads值是什么呢?threadFactory是否为null呢?args值是什么呢? 由于前面说过,MultithreadEventExecutorGroup类是NioEventLoopGroup类的父类,我们还需要倒回到实例化NioEventLoopGroup类的EchoServer的run方法中找答案 通过在NioEventLoopGroup类中不停地点啊点,发现了所有的答案: 1、如果调用的是NioEventLoopGroup的int参数构造方法(参数值非0),那么nThreads值为传入的值;如果调用的是默认构造方法,那么nThreads值为DEFAULT_EVENT_LOOP_THREADS的值,而DEFAULT_EVENT_LOOP_THREADS值为io.netty.eventLoopThreads属性指定值,如果没有指定io.netty.eventLoopThreads属性,那么DEFAULT_EVENT_LOOP_THREADS值为两倍处理器个数(在MultithreadEventLoopGroup构造方法中给出)

static {
        DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
                "io.netty.eventLoopThreads", Runtime.getRuntime().availableProcessors() * 2));
    }
   protected MultithreadEventLoopGroup(int nThreads, ThreadFactory threadFactory, Object... args) {
        super(nThreads == 0? DEFAULT_EVENT_LOOP_THREADS : nThreads, threadFactory, args);
    }
           

  2、threadFactory值为null(在NioEventLoopGroup构造方法中给出)

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

  3、args值为SelectorProvider实例(在NioEventLoopGroup构造方法中给出)

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

 这些问题弄清楚后,终于可以继续往下走了 接下来是判断children长度是否为2的幂,这个方法自认为很简洁,长知识了

private static boolean isPowerOfTwo(int val) {
        return (val & -val) == val;
    }
           

  然后是实例化children数组的每个实例,调用的是newChild方法,找来找去,发现newChild方法是在NioEventLoopGroup中实现的,返回了NioEventLoop的实例 现在知道MultithreadEventLoopGroup中next方法返回的是children数组中的下一个实例,即NioEventLoop实例,而register方法的调用当然也就是委派给NioEventLoop的实例来调用register方法了 这里有一个疑问:children长度是否为2的幂导致调用next方法返回children数组中值的方式有差别,但是有必要这样吗?也许是性能上的考虑吧   写了不少了,下次有时间继续走读NioEventLoop的register方法吧