Java Netty 学习(八) - Netty的Channel

版权声明:本博客所有的原创文章,转载请注明出处,作者皆保留版权。 https://blog.csdn.net/anLA_/article/details/83050582

在普通IO中,通过把机器传输抽象成java.net.Socket流,也就是SocketServerSocket
NIO时,则变为了java.nio.channels.Channel,也是作为传输的管道,具体可看Java Netty 学习(四) - NIO基础知识Channel和Pipe
那今天就一起学习Netty中的Channel,看它又是一种怎么样的设计形式。

介绍

在netty的Channel中,提供了很多共用的API,相比与大大降低了直接使用Socket的复杂性。另外,它是Netty网络通信的主题,由他负责直接对端进行网络通信,注册,以及相关的数据操作。
Channel提供了许多预定义的专门实现的类:

  • EmbeddedChannel:用于测试ChannelHandler的类
  • LocalServerChannel:同一个JVM内部实现client和server之间通信的Channel
  • NioDatagramChannel:使用UDP进行网络传输,和java.net.DatagramSocket对应
  • NioSctpChannel:基于Sctp协议的操作
  • NioSocketChannel:使用TCP进行网络传输

结构

Channel接口里面主要有以下方法:

  • ChannelId id():返回唯一的channel id
  • EventLoop eventLoop():返回专属被注册进入的EventLoop
  • Channel parent():返回父channel
  • ChannelConfig config():返回channel的config
  • SocketAddress localAddress():返回自己的socketAddress
  • SocketAddress remoteAddress():返回远端的Address
  • Unsafe unsafe():返回unsafe,内部实现的unsafe,一些方法只能用于I/O线程调用,其他的如localAddress则可以被用户线程调用。

状态

按照Channel定义的模型,Channel有四个状态

  • ChannelUnregistered:Channel已经被创建,但还未注册到EventLoop
  • ChannelRegistered:Channel已经被注册到EventLoop
  • ChannelActive:Channel处于活动状态,他现在可以接受和发送数据了
  • ChannelInactive:Channel没有连接到远程节点

NioSocketChannel

下面看看NioSocketChannel相关特性:
在这里插入图片描述

赋值

记得上一篇文章给出的实例,Bootstrap调用的建造者模式的过程中用到了NioSocketChannel

 b.group(group)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch) throws Exception {
                ch.pipeline().addLast(new HelloClientHandler());
            }
        });

下面跟着源码看看里面做了些什么事:

    public B channel(Class<? extends C> channelClass) {
        if (channelClass == null) {
            throw new NullPointerException("channelClass");
        }
        return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
    }

再看channelFactory:

    public B channelFactory(io.netty.channel.ChannelFactory<? extends C> channelFactory) {
        return channelFactory((ChannelFactory<C>) channelFactory);
    }

最后设置以下channelFactory:

    public B channelFactory(ChannelFactory<? extends C> channelFactory) {
        if (channelFactory == null) {
            throw new NullPointerException("channelFactory");
        }
        if (this.channelFactory != null) {
            throw new IllegalStateException("channelFactory set already");
        }

        this.channelFactory = channelFactory;
        return (B) this;
    }

在channel方法里面,首先还是返回一个AbstractBootstrap的子类以供继续建造对象,接着把一个
ReflectiveChannelFactory对象传递给了channelFactory,并且以传入的channelClass为参数,名字上可知是返回一个channel类,包装了很多层工厂方法为了实现代码的统一性。
进入源码可知,ReflectiveChannelFactory是一个继承了ChannelFactory的对象里面有一个newChannel方法如下:

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

执行了传入参数的Class的newInstance方法。

现在已经清楚知道Bootstrap中的channel方法做了什么事了,就是把一个相应类型的ChannelFactory传进去,这样就可以产生Channel。
那么,NioSocketChannel什么时候被初始化的呢?

初始化

首先在NioSocketChannel中打断点,跟踪到调用服务:
在这里插入图片描述

可以很清楚的看到
由connect->doResolveAndConnect->initAndRegister->newChannel一级一级调用:
返回一个异步的ChannelFuture

ChannelFuture future = b.connect(this.address, this.port);

再到:

 public ChannelFuture connect(String inetHost, int inetPort) {
        return connect(InetSocketAddress.createUnresolved(inetHost, inetPort));
    }

接着在doResolveAndConnect执行初始化一个Channel,但此时并没有连接上,就相当先拿到一个默认配置的Channel,但是现在还没有用。
接着执行initAndRegister,并在这里面调用:

    final ChannelFuture initAndRegister() {
        Channel channel = null;
        try {
            channel = channelFactory.newChannel();
            init(channel);
        } catch (Throwable t) {
            if (channel != null) {
                channel.unsafe().closeForcibly();
            }
            return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
        }

        ChannelFuture regFuture = config().group().register(channel);
        if (regFuture.cause() != null) {
            if (channel.isRegistered()) {
                channel.close();
            } else {
                channel.unsafe().closeForcibly();
            }
        }
        return regFuture;
    }

从而再经过JNI等一步步反射调用NioSocketChannel的构造方法。

    public NioSocketChannel() {
        this(DEFAULT_SELECTOR_PROVIDER);
    }

用途

那么,Channel创建了,也初始化了,那么它在Netty里面,地位是啥呢?
先看看一张图:
Netty工作原理

上图有以下几个概念:

  1. 客户端连接成功,就新建一个Channel来接受(代码中是先初始化一个Channel,连接成功后赋值属性)
  2. EventLoopGroup分配一个EventLoop给Channel,并注册到该EventLoop,channel生命周期内都和该EventLoop在一起(不变)同时,注册时获得selectionKey
  3. Channel进行网络连接、关闭和读写,生成相对应的event(改变selectinKey信息),触发eventloop调度线程进行执行
  4. EventLoop执行调度ChannelPipline来处理用户业务逻辑

并且,多个Channel可以同时注册到同一个EventLoop,并按照顺序循环处理Channel中的逻辑。

总结

Channel就类似于一个容器,或者理解为Socket,通过它与另一端进行通信,由EventLoop来处理Channel中产生的事件,在事件中实现相关逻辑,接下来文章将逐步学习其他组件。

帮助:

  1. Netty In Action
  2. https://www.cnblogs.com/TomSnail/p/6192885.html
  3. https://blog.csdn.net/u011801189/article/details/54729412

猜你喜欢

转载自blog.csdn.net/anLA_/article/details/83050582