zoukankan      html  css  js  c++  java
  • NIO 源码分析(04) 从 SelectorProvider 看 JDK SPI 机制

    NIO 源码分析(04) 从 SelectorProvider 看 JDK SPI 机制

    Netty 系列目录(https://www.cnblogs.com/binarylei/p/10117436.html)

    SelectorProvider 定义了创建 Selector、ServerSocketChannel、SocketChannel 等方法,采用 JDK 的 Service Provider Interface (SPI) 方式实现。

    public static ServerSocketChannel open() throws IOException {
        return SelectorProvider.provider().openServerSocketChannel();
    }
    

    一、SelectorProvider SPI

    SelectorProvider 是一个抽象类,需要子类实现。主要方法如下:

    public abstract DatagramChannel openDatagramChannel() throws IOException;
    public abstract DatagramChannel openDatagramChannel(ProtocolFamily family) throws IOException;
    public abstract ServerSocketChannel openServerSocketChannel() throws IOException;
    public abstract SocketChannel openSocketChannel() throws IOException;
    
    public abstract AbstractSelector openSelector() throws IOException;
    public abstract Pipe openPipe() throws IOException;
    

    总结: SelectorProvider 相当于一个工厂类,提供了对 DatagramChannel、ServerSocketChannel、SocketChannel、Selector 了创建方法。

    java.nio.channels.spi 中提供了一系列的抽象类,由具体的厂商实现,当然我们一般使用的都是 JDK 自己的实现。相关的 SPI 接口如下:

    AbstractInterruptibleChannel    -> SocketChannelImpl/ServerSocketChannelImpl     
    AbstractSelectableChannel   
    AbstractSelectionKey            -> SelectionKeyImpl
    AbstractSelector                -> WindowsSelectorImpl/PollSelectorImpl/EpollSelectorImpl
    SelectorProvider                -> DefaultSelectorProvider
    

    二、SelectorProvider 加载过程

    2.1 SelectorProvider 加载

    private static SelectorProvider provider = null;
    public static SelectorProvider provider() {
        synchronized (lock) {
            if (provider != null)
                return provider;
            return AccessController.doPrivileged(
                new PrivilegedAction<SelectorProvider>() {
                    public SelectorProvider run() {
                            // 1. java.nio.channels.spi.SelectorProvider 属性指定实现类
                            if (loadProviderFromProperty())
                                return provider;
                            // 2. SPI 指定实现类
                            if (loadProviderAsService())
                                return provider;
                            // 3. 默认实现,Windows 和 Linux 下不同
                            provider = sun.nio.ch.DefaultSelectorProvider.create();
                            return provider;
                        }
                    });
        }
    }
    

    总结: SelectorProvider 提供了三种方式来自定义 SelectorProvider 的实现类。

    1. java.nio.channels.spi.SelectorProvider 属性指定实现类
    2. 采用 SPI 方法创建 SelectorProvider
    3. 默认实现 DefaultSelectorProvider,Windows 和 Linux 下具体的实现不同。

    SelectorProvider 类图

    public abstract class SelectorProviderImpl extends SelectorProvider {
        public DatagramChannel openDatagramChannel() throws IOException {
            return new DatagramChannelImpl(this);
        }
        public DatagramChannel openDatagramChannel(ProtocolFamily family) throws IOException {
            return new DatagramChannelImpl(this, family);
        }
    
        public Pipe openPipe() throws IOException {
            return new PipeImpl(this);
        }
    
        public abstract AbstractSelector openSelector() throws IOException;
    
        public ServerSocketChannel openServerSocketChannel() throws IOException {
            return new ServerSocketChannelImpl(this);
        }
        public SocketChannel openSocketChannel() throws IOException {
            return new SocketChannelImpl(this);
        }
    }
    

    总结: SelectorProviderImpl 提供了 ServerSocketChannel、SocketChanne 的创建,至于 Selector 在不同的平台下有不同的实现。

    2.2 Windows 下 DefaultSelectorProvider

    public class DefaultSelectorProvider {
        public static SelectorProvider create() {
            return new sun.nio.ch.WindowsSelectorProvider();
        }
    }
    
    public class WindowsSelectorProvider extends SelectorProviderImpl {
        public AbstractSelector openSelector() throws IOException {
            return new WindowsSelectorImpl(this);
        }
    }
    

    2.3 Unix 下 DefaultSelectorProvider

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

    总结: Unix 平台下需要根据不同的操作系统选择不同的 Selector,例如 Linux 下是 EPollSelectorProvider。

    public class EPollSelectorProvider extends SelectorProviderImpl {
        public AbstractSelector openSelector() throws IOException {
            return new EPollSelectorImpl(this);
        }
    
        public Channel inheritedChannel() throws IOException {
            return InheritedChannel.getChannel();
        }
    }
    

    总结: 无论是 WindowsSelectorProvider 还是 EPollSelectorImpl,它们都继承 SelectorProviderImpl,关于 ServerSocketChannel、SocketChanne 的创建都是一样的,区别是 Selector 有兼容性问题。难道 Socket 在 Windows 和 Linux 下就没有区别吗,肯定也是有兼容性问题的。

    ServerSocketChannelImpl(SelectorProvider sp) throws IOException {
        super(sp);
        this.fd =  Net.serverSocket(true);  // 创建 socket,这个 Net 工具本身是跨平台的
        this.fdVal = IOUtil.fdVal(fd);
        this.state = ST_INUSE;
    }
    

    Socket 的创建是在 sun.nio.ch.Net 工具类的 socket0 完成的,这个类很多方法都是 native 方法,在不同的平台有不同的实现。


    每天用心记录一点点。内容也许不重要,但习惯很重要!

  • 相关阅读:
    简单通讯聊天 群聊功能 Windows下的客户端 Linux下的epoll服务器
    Windows客户端 Linux服务器通讯 字符编码问题
    C++时间标准库时间time和系统时间的使用
    Window7系统安装Ubuntu16双系统
    Window7 系统下重新建立一个新分区
    UltraISO(软碟通) 制作U盘启动盘
    Python 列表反转显示方法
    HTML,CSS,JS个别知识点总结
    Git 创建版本库并实现本地上传数据到GitHub库
    Python爬虫数据保存到MongoDB中
  • 原文地址:https://www.cnblogs.com/binarylei/p/11147083.html
Copyright © 2011-2022 走看看