美文网首页
Netty源码-Channel讲解

Netty源码-Channel讲解

作者: HQG | 来源:发表于2021-06-23 18:18 被阅读0次

    Channel的讲解

    • 阅读前需思考的问题
    • Netty中的Channel是什么?
    • Channel是怎么创建的?
    • Channel的创建时机?
    • Netty中的Channel和JDK中的Channel什么关系?
    • 两者什么时候关联起来的,怎么关联的?

    Channel是什么?

    Channel是对socket连接的封装,可以理解成Channel就是socket连接;Netty中的Channel分为NioSocketChannel(客户端)和NioServerSocketChannel(服务端)两种。 我们知道Netty是基于Java Nio的,那么与Java中的SocketChannel什么关系呢?是一对一的关系。

    NioSocketChannel          --------->     SocketChannel           (1:1)
    NioServerSocketChannel    --------->     ServerSocketChannel     (1:1)
    

    Channel如何实例化的?

    image.png

    上图是Netty官网的Echo Server示例,左边是client端代码,右边是server端代码。NioSocketChannel和和NioServerSocketChannel都赋值给channel(...)方法了。

    点进ServerBootstrap的channel方法一看究竟:

    ## AbstractBootstrap类
    
    public B channel(Class<? extends C> channelClass) {
        // 以channelClass为参数,创建了ReflectiveChannelFactory对象
        // 从名字上看,是通过反射技术创建channel实例的工厂
        return channelFactory(new ReflectiveChannelFactory<C>(
                ObjectUtil.checkNotNull(channelClass, "channelClass")
        ));
    }
    

    继续点进去:

    ## ReflectiveChannelFactory类
    
    public class ReflectiveChannelFactory<T extends Channel> implements ChannelFactory<T> {
        // 构造函数成员变量:constructor
        private final Constructor<? extends T> constructor;
    
        public ReflectiveChannelFactory(Class<? extends T> clazz) {
            ObjectUtil.checkNotNull(clazz, "clazz");
            try {
                // 获取一个无参构造函数
                this.constructor = clazz.getConstructor();
            } catch (NoSuchMethodException e) {
                throw new IllegalArgumentException("Class " + StringUtil.simpleClassName(clazz) +
                        " does not have a public non-arg constructor", e);
            }
        }
    
        @Override
        public T newChannel() {
            try {
                // 通过无参构造函数,创建一个实例
                return constructor.newInstance();
            } catch (Throwable t) {
                throw new ChannelException("Unable to create Channel from class " + constructor.getDeclaringClass(), t);
            }
        }
        ......
    }
    

    到此我们知道,在 newChannel() 方法中利用 反射技术 创建Channel实例的,此类使用了我们非常熟悉的工厂设计模式

    思考:我们知道ReflectiveChannelFactory是利用反射技术创建对象的,是不是可以创建任意对象?不是的,泛型T是有约束条件的,T 必须是实现Channel接口的类才行的。

    public class ReflectiveChannelFactory<T extends Channel> implements ChannelFactory<T> 
    ## 泛型T是有约束条件的,T 必须是实现Channel接口的类
    

    Channel无参构造函数做了什么?

    来看下NioServerSocketChannel类:

    ## NioServerSocketChannel类
    
    public NioServerSocketChannel() {
        // 创建一个Socket连接,传递给了一个参数的构造函数
        this(newSocket(DEFAULT_SELECTOR_PROVIDER));
    }
    
    // 创建了一个服务端Socket连接
    private static ServerSocketChannel newSocket(SelectorProvider provider) {
        try {
            // 使用JDK的SelectorProvider,开启了一个SocketChannel
            return provider.openServerSocketChannel();
        } catch (IOException e) {
            throw new ChannelException(
                    "Failed to open a server socket.", e);
        }
    }
    
    public NioServerSocketChannel(ServerSocketChannel channel) {
            // 创建好的SocketChannel传递父类构造函数,同时Accpet事件也传递给了父类,后面会用到
            super(null, channel, SelectionKey.OP_ACCEPT);
            config = new NioServerSocketChannelConfig(this, javaChannel().socket());
        }
    

    到此可知,Netty的Channel中维护着一个JDK的SocketChannel,所以Netty的Channel和JDK的Channel关系是1:1。

    我们知道Nio是非阻塞的,来看看什么时候配置?
    一直点super(...)下去,直到:

    ## AbstractNioChannel类
    
    protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
        super(parent);
        this.ch = ch;
        // 客户端监听OP_READ事件,服务端监听OP_ACCEPT事件,后面会用到
        this.readInterestOp = readInterestOp;
        try {
            // 客户端和服务端的SocketChannel都需要配置非阻塞
            ch.configureBlocking(false);
        } catch (IOException e) {
            try {
                ch.close();
            } catch (IOException e2) {
                logger.warn(
                            "Failed to close a partially initialized socket.", e2);
            }
    
            throw new ChannelException("Failed to enter non-blocking mode.", e);
        }
    }
    

    Channel何时被创建的?

    • 对于NioSocketChannel来说,它是在Bootstrap的connect(...)时被创建的。
    • 对于NioServerSocketChannel来说,它是在ServerBootstrap的bind(...)时被创建的。

    以服务端的NioServerSocketChannel来研究何时被创建的,客户端NioSocketChannel创建过程差不多。

    来看ServerBootstrap的bind(...)方法源码:

    ## AbstractBootstrap类
    
    public ChannelFuture bind(int inetPort) {
        // 服务端SocketChannel绑定监听端口,格式:0.0.0.0:Port
        return bind(new InetSocketAddress(inetPort));
    }
    

    一直点下去,会跟到doBind(...)方法:

    ## AbstractBootstrap类
    
    private ChannelFuture doBind(final SocketAddress localAddress) {
        // 对channel进行初始化和注册操作
        final ChannelFuture regFuture = initAndRegister();
        final Channel channel = regFuture.channel();
        if (regFuture.cause() != null) {
            return regFuture;
        }
    
        ......
    }
    

    来看下initAndRegister(...)方法:

    ## AbstractBootstrap类
    
    final ChannelFuture initAndRegister() {
        Channel channel = null;
        try {
            // 创建一个NioServerSocketChannel实例
            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注册到bossGroup线程池,由bossGroup线程池来处理accept事件
        ChannelFuture regFuture = config().group().register(channel);
        if (regFuture.cause() != null) {
            if (channel.isRegistered()) {
                channel.close();
            } else {
                channel.unsafe().closeForcibly();
            }
        }
        return regFuture;
    }
    

    到此,我们知道了服务端的Channel是在AbstractBootstrap类的initAndRegister()方法中创建的

    总结

    image

    相关文章

      网友评论

          本文标题:Netty源码-Channel讲解

          本文链接:https://www.haomeiwen.com/subject/noxerltx.html