Java教程:Netty知识点详解(十一)

开课吧开课吧锤锤2021-02-24 10:44

近年来,随着云计算、大数据等热门技术的发展和普及,Java在企业中越来越受到重视,公司对于Java人才需求也是每年都在增长,对于这样热门的行业,很多人都是心动的,那么对于如何学习,怎么提高Java的技术,那就来看看本篇文章吧。

Tag 1 : initAndRegister() 初始化并注册 Channel

先找到代码:

final ChannelFuture initAndRegister() {

    Channel channel = null;

    try {

        // 创建channel

        channel = channelFactory.newChannel();

        // 初始化channel

        init(channel);

    } catch (Throwable t) {

        if (channel != null) {

            channel.unsafe().closeForcibly();

            return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);

        }

        return new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);

    }

    // 将channel注册到selector

    ChannelFuture regFuture = config().group().register(channel);

    if (regFuture.cause() != null) {

        if (channel.isRegistered()) {

            channel.close();

        } else {

            channel.unsafe().closeForcibly();

        }

    }

    return regFuture;

}

嗯?!代码意一看,咋就这么点,也就做了三件事,可是这三件事做的每一个都不是一句代码的可以完成的。这里我们一个一个分析,除了这三件事情,其他的也就是异常后的处理逻辑,所以主流程就是下面的三句代码,也为了跟进继续打上标记吧:

Tag 1.1 创建channel

channel = channelFactory.newChannel();

Tag 1.2 初始化channel

init(channel);

Tag 1.3 将channel注册到selector

ChannelFuture regFuture = config().group().register(channel);

Java

针对这三处,还是要一处一处分析。

Tag 1.1 channelFactory.newChannel() 创建 Channel

找到对应的代码:io.netty.channel.ReflectiveChannelFactory#newChannel

@Override

public T newChannel() {

    try {

        // 调用无参构造器创建channel

        return constructor.newInstance();

    } catch (Throwable t) {

        throw new ChannelException("Unable to create Channel from class " + constructor.getDeclaringClass(), t);

    }

}

这里为什么直接找到 ReflectiveChannelFactory ,需要提一下,在分析 ServerBootstrap与 Bootstrap 启动配置类的时候,设置 channel 的方法,跟进去可以找到针对属性 channelFactory 的赋值代码:

public B channel(Class<? extends C> channelClass) {

    if (channelClass == null) {

        throw new NullPointerException("channelClass");

    }

    return channelFactory(new ReflectiveChannelFactory<C>(channelClass));

}

可以看到这里 new 的就是 ReflectiveChannelFactory 工厂类,然后再看 ReflectiveChannelFactory 的构造:

public ReflectiveChannelFactory(Class<? extends T> clazz) {

    ObjectUtil.checkNotNull(clazz, "clazz");

    try {

        // 将NioServerSocketChannel的无参构造器初始化到constructor

        this.constructor = clazz.getConstructor();

    } catch (NoSuchMethodException e) {

        throw new IllegalArgumentException("Class " + StringUtil.simpleClassName(clazz) +

                                           " does not have a public non-arg constructor", e);

    }

}

看到的是 ReflectiveChannelFactory 在创建时初始化了 constructor 属性,将传入的 channel 类 clazz 中获取构造赋值给了 ReflectiveChannelFactory 反射工厂的 constructor 属性。

而我们再 Server 端传入的 channel 类为NioServerSocketChannel.class ,所以上面看的 constructor.newInstance(); 对应的也就是 NioServerSocketChannel 的无参构造。这样我们就继续跟进 NioServerSocketChannel :

// NIO中的provider,其用于创建selector与channel。并且是单例的

private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();

public NioServerSocketChannel() {

    // DEFAULT_SELECTOR_PROVIDER 静态变量

    this(newSocket(DEFAULT_SELECTOR_PROVIDER));

}

继续跟进 newSocket()

private static ServerSocketChannel newSocket(SelectorProvider provider) {

    try { 

        // 创建NIO原生的channel => ServerSocketChannel

        return provider.openServerSocketChannel();

    } catch (IOException e) {

        throw new ChannelException(

            "Failed to open a server socket.", e);

    }

}

就是返回了一个 Java NIO 原生的 Channel,最后将 NIO 原生的Channel 包装成 NioServerSocketChannel,继续跟进 this(newSocket(DEFAULT_SELECTOR_PROVIDER)) 找到有参构造具体代码:

public NioServerSocketChannel(ServerSocketChannel channel) {

    // 参数1:父channel

    // 参数2:NIO原生channel

    // 参数3:指定当前channel所关注的事件为  接受连接

    super(null, channel, SelectionKey.OP_ACCEPT);

    // 用于对channel进行配置的属性集合

    config = new NioServerSocketChannelConfig(this, javaChannel().socket());

}

这里主要做了两件事情,1. 调用父类构造,2. 对 channel 进行配置属性集合。

这里先说下 new NioServerSocketChannelConfig(),这部操作就是给当前 Channel 的 config 进行赋值,用来保存当前 Channel 的属性配置的集合。好了,这个说了我们继续跟主线:super(null, channel, SelectionKey.OP_ACCEPT)

// io.netty.channel.nio.AbstractNioMessageChannel#AbstractNioMessageChannel

protected AbstractNioMessageChannel(Channel parent, SelectableChannel ch, int readInterestOp) {

    super(parent, ch, readInterestOp);

}

// io.netty.channel.nio.AbstractNioChannel#AbstractNioChannel

protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {

    super(parent);

    // 这里的this.ch为NIO原生channel

    this.ch = ch;

    this.readInterestOp = readInterestOp;

    try {

        // NIO,非阻塞

        ch.configureBlocking(false);

    } catch (IOException e) {

        try {

            ch.close();

        } catch (IOException e2) {

            if (logger.isWarnEnabled()) {

                logger.warn(

                    "Failed to close a partially initialized socket.", e2);

            }

        }

        throw new ChannelException("Failed to enter non-blocking mode.", e);

    }

}

直接找到 AbstractNioChannel 父类构造,这也第一步也是调用父类构造 super(parent); 先记着,先看除了调用父类构造还做了什么事情:

1、调用父类构造 super(parent);

2、将前面创建的原生 Channel 复制给属性保存 this.ch = ch;

3、当前 channel 的关注事件属性赋值 this.readInterestOp = readInterestOp; // SelectionKey.OP_ACCEPT 接受事件

4、将 NIO 原生 Channel 设置为非阻塞 ch.configureBlocking(false);

在 AbstractNioChannel 构造中就做了这么四件事情,主要需要说的还是其调用父类构造又做了什么事情,找到代码:

// io.netty.channel.AbstractChannel#AbstractChannel(io.netty.channel.Channel)

protected AbstractChannel(Channel parent) {

    this.parent = parent;

    // 为channel生成id,由五部分构成

    id = newId();

    // 生成一个底层操作对象unsafe

    unsafe = newUnsafe();

    // 创建与这个channel相绑定的channelPipeline

    pipeline = newChannelPipeline();

}

在 AbstractChannel 构造中主要做了三件事:

1、为当前 Channel 生成 id newId(),感兴趣可以跟进去看看。

2、生成一个底层操作对象 unsafe,用于 I/O 线程调用传输时使用,用户代码无法调用。newUnsafe()

3、创建与这个channel相绑定的channelPipeline,这也是一个重点操作,不过在这里先不展开细说,后面会单独细跟 channelPipeline 的代码。

所以到此 **Tag 1 : initAndRegister() ** 中的 **Tag 1.1 newChannel() ** 创建 Channel 才算跟完。针对 Tag 1.1 newChannel()们也画图简图整理下思路:

Java

根据图,在结合上面代码的分析,最好自己再可以跟一遍代码,我想这一块的理解还是没什么问题的。到这也只是创建了 Channel。Tag 1.1 的 Channel 创建结束,接着跟进 Tag 1.2 init(channel).

以上内容由开课吧老师敖丙提供,更多Java教程尽在开课吧广场Java教程频道。更多免费课程可以关注公众号“码农集散地”

有用
分享