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

开课吧开课吧锤锤2021-02-24 14:39

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

Tag 1:initAndRegister() 分析

final ChannelFuture initAndRegister() {

    Channel channel = null;

    try {

        // 创建channel

        channel = channelFactory.newChannel();

        // 初始化channel

        init(channel);

    } catch (Throwable t) {

        if (channel != null) {

            // channel can be null if newChannel crashed (eg SocketException("too many open files"))

            channel.unsafe().closeForcibly();

            // as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor

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

        }

        // as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor

        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;

    }

这里调用的 io.netty.bootstrap.AbstractBootstrap#initAndRegister 的方法与 Server 端调用的完全一样。我们h还是把当前方法分成三部分然后一个一个细说:

Tag 1.1: channelFactory.newChannel();

Tag 1.2:init(channel);

Tag 1.3:config().group().register(channel);

Tag 1.1 newChannel() 分析

首先在这里不一样的是 Bootstrap 的启动配置类传入的 channel 是 .channel(NioSocketChannel.class)。而在 Server 端传入的 NioServerSoucketChannel

所以在 Client 端 channelFactory 调用的则是 NioSocketChannel 的无参构造来初始化创建 channel。所以这里直接找到 NioSocketChannel 的构造方法:

public NioSocketChannel() {

    // DEFAULT_SELECTOR_PROVIDER:全局唯一的provider,通过它可以创建出selector与channel

    this(DEFAULT_SELECTOR_PROVIDER);

}

// 继续跟进 this

public NioSocketChannel(SelectorProvider provider) {

    this(newSocket(provider));

}

到这里调用了 newSocket(provider) 方法。主要是创建了原生的 SocketChannel ,provider.openSocketChannel() 。初始化 Nio 原生的 channel 之后,就是创建 Netty 包装的 channel 的过程了。

继续往后跟:

public NioSocketChannel(Channel parent, SocketChannel socket) {

    super(parent, socket);

    config = new NioSocketChannelConfig(this, socket.socket());

}

这里的 new NioSocketChannelConfig(this, socket.socket()) 与 Server 端一样,此处就不再跟。我们直接跟 super(parent, socket) :点击去找到 io.netty.channel.nio.AbstractNioByteChannel#AbstractNioByteChannel:

protected AbstractNioByteChannel(Channel parent, SelectableChannel ch) {

    // 第三个参数: 指定关注事件为读事件 OP_READ

    super(parent, ch, SelectionKey.OP_READ);

}

这里就出现了与 Server 端不一样的地方了。不知道大家还记得在 Server 端创建 channel 的时候,指定的关注事件是什么,直接贴上代码:

Java

当时跟 Server 端注册 channel 时关注就的是 OP_ACCEPT,而在 Client 端创建的 channel 变成了关注的事件为 OP_READ 事件,因为当 Client 连接 Server 完成时,会由 Server 端通知 Client 端连接成功,所以此时 Client 直接注册 OP_READ 使事件来监听来自 Server 的返回。

然后下面的 super 的代码与 server 端的是同一个父类 io.netty.channel.nio.AbstractNioChannel#AbstractNioChannel 。所以后面的代码是与 Server 端一样。如果忘了可以往上翻翻,找到 Server 端的 newChannel 方法就可以看到。

Java

Tag 1.2 init(channel) 分析

上面跟完了 client 端的 channel 创建,接着就是看 channel 的初始化。同样是先找到代码:

Java

因为这是 Client,启动类是 Bootstrap。所以找到对应方法:这也是与 Server 端的区别。

void init(Channel channel) throws Exception {

    ChannelPipeline p = channel.pipeline();

    // 将bootstrap中创建的ChannelInitializer处理器添加到pipeline

    p.addLast(config.handler());

    // 将bootstrap中的options初始化到channel

    final Map<ChannelOption<?>, Object> options = options0();

    synchronized (options) {

        setChannelOptions(channel, options, logger);

    }

    // 将bootstrap中的attrs初始化到channel

    final Map<AttributeKey<?>, Object> attrs = attrs0();

    synchronized (attrs) {

        for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {

            channel.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());

        }

    }

}

这里可以明显看到与 Server 端的 init 方法相比,做的事情要少很多。首先将 Bootstrap 配置传入的 handler 添加到 channel 的 pipeline 中 p.addLast(config.handler());。然后将 bootstrap 中配置的 attr 和 options 的属性值初始化到 channel 中。

这里稍微比较下 Client 端和 Server 端的 init 方法区别:

Java

可以看到当时再 Server 端我们不仅处理了 ParentGroup 的属性初始化,还初始化了以 Child 开头的 ChildGroup 的属性初始化,而再 Server 端是获取 pipeline 将传入的 childHandler 再次注册成一个新的 hanndler 然后添加到当前的 pipeline 中。再 Clinet 端则是直接将配置类传入的 handler 添加到 pipeline。这就是两边主要的区别,也就是因为 Server 传入的是两个 NioEventLoopGrou 才有的处理逻辑上的区别。也就是Server 端使用的是 Reactor 线程池模型,而Client 使用的 Reactor 模型。

Netty-Client 端的 Reactor 模型:

Java

所以因为选择模型的区别,再处理逻辑上也有区别。

Tag 1.3 register(channel) 分析

这部分就完全与 Server 端一致,所以这里也不再啰嗦。不明白的可以直接翻到前面就可以看到。不在赘述。

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

有用
分享