Java-IO系列:Netty中Channel创建过程以及与操作系统系统调用的关系

本文详细解读了Java中的ChannelFactory和SelectorProvider在Netty服务器启动过程中的作用,涉及ReflectiveChannelFactory实例化Channel、SelectorProvider的选择与配置,以及NioServerSocketChannel的构造细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

应用代码

public static void start() throws Exception {
        ServerBootstrap bootstrap = new ServerBootstrap();
        EventLoopGroup boss = new NioEventLoopGroup();
        EventLoopGroup work = new NioEventLoopGroup();
        bootstrap.group(boss, work)
                .handler(new LoggingHandler(LogLevel.DEBUG))
                //重点看这句Channel
                .channel(NioServerSocketChannel.class)
                .childHandler(new HttpServerInitializer());
        ChannelFuture f = bootstrap.bind(new InetSocketAddress(port)).sync();
        ...

源码

一、初始化ServerBootstrap的channelFactory属性

    public B channel(Class<? extends C> channelClass) {
        return channelFactory(new ReflectiveChannelFactory<C>(
                ObjectUtil.checkNotNull(channelClass, "channelClass")
        ));
    }

这里学习下一个工厂

ReflectiveChannelFactory

在这里插入图片描述

public class ReflectiveChannelFactory<T extends Channel> implements ChannelFactory<T> {

    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);
        }
    }

    @Override
    public String toString() {
        return StringUtil.simpleClassName(ReflectiveChannelFactory.class) +
                '(' + StringUtil.simpleClassName(constructor.getDeclaringClass()) + ".class)";
    }
}

解读一下:

ChannelFactory是一个泛型接口,定义了newChannel()方法,也就是ChannelFactory的实现类要有能力构造一个new Channel。
ReflectiveChannelFactory的泛型T是Channel的子类。也就是说ReflectiveChannelFactory支持实例化Channel的子类。比如NioServerScoketChannel。

而ReflectiveChannelFactory的构造方法中,将Channel类的无参构造方法,赋值给constructor属性。
在newChannel()方法中,通过反射调用constructor无参构造方法,实例化一个Channel对象。

此处只是初始化一个ServerBootStrap对象的channelFactory属性。设置channelFactory为ReflectiveChannelFactory(NioServerSocketChannel.class)。

二、bootstrap.bind时initAndRegister过程中new Channel

final ChannelFuture initAndRegister() {
        Channel channel = null;
        try {
            channel = channelFactory.newChannel();
            init(channel);
        .......

channelFactory.newChannel();
这句会调用ReflectiveChannelFactory的newChannel方法,调用NioServerSocketChannel的构造方法构造一个Channel。

NioServerSocketChannel

NioServerSocketChannel类后面单独讲

DEFAULT_SELECTOR_PROVIDER

NioServerSocketChannel.java

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

SelectorProvider.java

public static SelectorProvider provider() {
        synchronized (lock) {
            if (provider != null)
                return provider;
            return AccessController.doPrivileged(
                new PrivilegedAction<SelectorProvider>() {
                    public SelectorProvider run() {
                            if (loadProviderFromProperty())
                                return provider;
                            if (loadProviderAsService())
                                return provider;
                            provider = sun.nio.ch.DefaultSelectorProvider.create();
                            return provider;
                        }
                    });
        }
    }

provider中,如果SelectorProvider的provider有了就返回。
如果没有,则从系统配置中找配置的provider,如果有就实例化并返回。

System.getProperty("java.nio.channels.spi.SelectorProvider");

如果系统配置没有,则通过SPI加载配置的provider。使用了JDK的SPI能力,关键源码如下:

ServiceLoader<SelectorProvider> sl =
            ServiceLoader.load(SelectorProvider.class,
                               ClassLoader.getSystemClassLoader());

如果SPI也没有配置provider,这通过jdk中DefaultSelectorProvider静态方法create()来创建provider。核心代码入下,根据运行操作系统,如果是linux则new一个EPollSelectorProvider。

public static SelectorProvider create() {
        String osname = (String)AccessController.doPrivileged(new GetPropertyAction("os.name"));
        if (osname.equals("SunOS")) {
            return createProvider("sun.nio.ch.DevPollSelectorProvider");
        } else {
            return (SelectorProvider)(osname.equals("Linux") ? createProvider("sun.nio.ch.EPollSelectorProvider") : new PollSelectorProvider());
        }
    }
EPollSelectorProvider

类关系如下
在这里插入图片描述EPollSelectorProvider和PollSelectorProvider继承SelectorProviderImpl这个抽象类,实现openSelector和inheritedChannel两个方法。
区别是EPollSelectorProvider的openSelector,返回EPollSelectorImpl对象
而PollSelectorProvider返回PollSelectorImpl对象。

这两个的区别后面单独说。

NioServerSocketChannel构造方法

    public NioServerSocketChannel() {
        this(newSocket(DEFAULT_SELECTOR_PROVIDER));
    }
    
    private static ServerSocketChannel newSocket(SelectorProvider provider) {
        try {
            /**
             *  Use the {@link SelectorProvider} to open {@link SocketChannel} and so remove condition in
             *  {@link SelectorProvider#provider()} which is called by each ServerSocketChannel.open() otherwise.
             *
             *  See <a href="https://github.com/netty/netty/issues/2308">#2308</a>.
             */
            //参考下文provider.openServerSocketChannel()讲解;
            return provider.openServerSocketChannel();
        } catch (IOException e) {
            throw new ChannelException(
                    "Failed to open a server socket.", e);
        }
    }
    public NioServerSocketChannel(ServerSocketChannel channel) {
    	 //参考:AbstractNioChannel的构造方法章节
        super(null, channel, SelectionKey.OP_ACCEPT);
        config = new NioServerSocketChannelConfig(this, javaChannel().socket());
    }    
provider.openServerSocketChannel()

EPollSelectorImpl的openServerSocketChannel,看名字就能猜到,返回一个ServerSocketChannel。当然EPollSelectorImpl作为一个SelectorProvider也能获取UDP等。

public ServerSocketChannel openServerSocketChannel() throws IOException {
        return new ServerSocketChannelImpl(this);
    }
ServerSocketChannelImpl

    ServerSocketChannelImpl(SelectorProvider sp) throws IOException {
        super(sp);
        this.fd =  Net.serverSocket(true);
        this.fdVal = IOUtil.fdVal(fd);
        this.state = ST_INUSE;
    }

Net.java

    static FileDescriptor serverSocket(boolean stream) {
        return IOUtil.newFD(socket0(isIPv6Available(), stream, true));
    }

    // Due to oddities SO_REUSEADDR on windows reuse is ignored
    private static native int socket0(boolean preferIPv6, boolean stream, boolean reuse);

可见ServerSocketChannelImpl构造方法中,Net.serverSocket(true);
会调用native的方法,最终会进行socket函数的系统调用。并返回操作系统上的该socket的fd号,并将fd号封装在Java中的FileDescriptor对象中。

AbstractNioChannel的构造方法
    protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
        super(parent);
        this.ch = ch;
        this.readInterestOp = readInterestOp;
        try {
            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感兴趣的事件,并切设置channel的模式为非阻塞模式。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值