NIO 源碼分析(04) 從 SelectorProvider 看 JDK SPI 機制

NIO 源碼分析(04) 從 SelectorProvider 看 JDK SPI 機制html

[toc]java

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

SelectorProvider 定義了建立 Selector、ServerSocketChannel、SocketChannel 等方法,採用 JDK 的 Service Provider Interface (SPI) 方式實現。ide

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

1、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;

<b>總結:</b> SelectorProvider 至關於一個工廠類,提供了對 DatagramChannel、ServerSocketChannel、SocketChannel、Selector 了建立方法。源碼分析

java.nio.channels.spi 中提供了一系列的抽象類,由具體的廠商實現,固然咱們通常使用的都是 JDK 本身的實現。相關的 SPI 接口以下:this

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

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

<b>總結:</b> SelectorProvider 提供了三種方式來自定義 SelectorProvider 的實現類。url

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

<b>總結:</b> SelectorProviderImpl 提供了 ServerSocketChannel、SocketChanne 的建立,至於 Selector 在不一樣的平臺下有不一樣的實現。spa

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

<b>總結:</b> 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();
    }
}

<b>總結:</b> 不管是 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 方法,在不一樣的平臺有不一樣的實現。


天天用心記錄一點點。內容也許不重要,但習慣很重要!

相關文章
相關標籤/搜索