Selector源码学习笔记

Selector学习笔记.

Selector open流程

sequenceDiagram Selector.open()-->>SelectorProvider: SelectorProvider.provider() SelectorProvider-->>Selector.open(): 返回具体SelectorProvider Selector.open()-->>具体SelectorProvider: 具体SelectorProvider.openSelector() 具体SelectorProvider-->>Selector.open(): 返回具体Selector

Selector.open()

可以看到Selector是由Selector.provider()提供的provider提供的.所以接下来看看SelectorProvider::provider方法

    public static Selector open() throws IOException {
        return SelectorProvider.provider().openSelector();
    }

SelectorProvider.provider()

graph LR A[SelectorProvider::provider] A --> B{环境变量是否有值} B-->|有| E[返回] B -->|无| C{SPI是否有Provider实现} C -->|有| E C -->|无| D[sun.nio.ch.DefaultSelectorProvider::create] D --> E

provider返回有多种情况,具体优先级见下

  • 从系统环境变量"java.nio.channels.spi.SelectorProvider"中加载指定的Provider
  • 通过java SPI加载Provider实现
  • 默认Provider,通过sun.nio.ch.DefaultSelectorProvider.create()创建,且根据jdk不同rt包里的实现也不同.
    public static SelectorProvider provider() {
        synchronized (lock) {
            if (provider != null)
                return provider;
            return AccessController.doPrivileged(
                new PrivilegedAction<SelectorProvider>() {
                    public SelectorProvider run() {
                            //从系统环境变量中"java.nio.channels.spi.SelectorProvider"加载Provider实现.
                            if (loadProviderFromProperty())
                                return provider;
                            //通过JAVA SPI加载Provider的实现
                            if (loadProviderAsService())
                                return provider;
                            //默认Provider
                            provider = sun.nio.ch.DefaultSelectorProvider.create();
                            return provider;
                        }
                    });
        }
    }

sun.nio.ch.DefaultSelectorProvider.create()

DefaultSelectorProvider在不同的rt包实现不一样.所以通过这种方式会因为不同的操作系统得到不一样的结果.
截止jdk8为止
可获取的具体Selector实现如下:
SelectorImpl

windows版DefaultSelectorProvider

windows版本就直接返回windowsSelectorProvider

    public static SelectorProvider create() {
        return new WindowsSelectorProvider();
    }

linux版DefaultSelectorProvider

graph LR A[sun.nio.ch.DefaultSelectorProvider] A --> B{环境变量os.name} B-->|os.name=='SunOs'| E[sun.nio.ch.DevPollSelectorProvider] B --> C{os.name=='linux'} C -->|是| D[sun.nio.ch.EPollSelectorProvider] C -->|无| F[PollSelectorProvider]

linux版本则根据环境变量os.name返回对应的provider.
2.6以后的linux返回的是EPollSelectorProvider

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

SelectorProvider.openSelector()

不同的provider来自于SelectorProvider.provider()创建,而具体provider的openSelector方法则负责返回具体的Selector.

WindowsSelectorProvider.openSelector()

返回WindowsSelectorImpl.java

    public AbstractSelector openSelector() throws IOException {
        return new WindowsSelectorImpl(this);
    }

EPollSelectorProvider.openSelector()

返回EPollSelectorImpl

    public AbstractSelector openSelector() throws IOException {
        return new EPollSelectorImpl(this);
    }

register流程

sequenceDiagram AbstractSelectableChannel-->>AbstractSelector: register() AbstractSelector-->>SelectorImpl: register() SelectorImpl-->>具体Selector实现: implRegister() 具体Selector实现-->>SelectorImpl: SelectorImpl-->>AbstractSelector: AbstractSelector-->>AbstractSelectableChannel:

AbstractSelectableChannel::register

AbstractSelectableChannel
如上图所示,首先只有SelectableChannel可以注册至selector上,而我们常用到的大部分channel基本都继承自SelectableChannel.
接下来看看AbstractSelectableChannel::register的API介绍

使用给定的Selector注册此Channel,并返回SelectionKey。
如果此Channel已在给定的Selector中注册,则将其兴趣(ops参数)设置为给定值后,返回表示注册的SelectionKey.
若此Channel尚未在给定的Selector中注册,则在保持适当的锁的同时调用AbstractSelector::register方法进行注册.返回key之前先添加进channel的key集合中.(每个channel也维护了自己注册过的selector集合)

    public final SelectionKey register(Selector sel, int ops,
                                       Object att) throws ClosedChannelException
    {
        synchronized (regLock) {
            //验证通道是否打开
            if (!isOpen())
                throw new ClosedChannelException();
            //validOps返回子类支持的ops,即对应的位数值为1. 此处校验ops是否有支持权限外的位数值为1
            if ((ops & ~validOps()) != 0)
                throw new IllegalArgumentException();
            //selector不支持阻塞channel注册.此处检查了channel是否为阻塞模式.
            if (blocking)
                throw new IllegalBlockingModeException();
            //检查此channel是否已经注册过Selector了.
            SelectionKey k = findKey(sel);
            //如果注册过,就更新ops和attachment
            if (k != null) {
                k.interestOps(ops);
                k.attach(att);
            }
            //如果没有注册过
            if (k == null) {
                // New registration
                synchronized (keyLock) {
                    //检查连接是否是打开的
                    if (!isOpen())
                        throw new ClosedChannelException();
                    //走selector的register方法注册此channel.
                    k = ((AbstractSelector)sel).register(this, ops, att);
                    //并添加至此channel记录的注册的Selector集合.
                    addKey(k);
                }
            }
            return k;
        }
    }

看下来,发现channel最后还是通过调用Selector::register方法进行注册,所以接下来看Selector的register方法.

SelectorImpl::register

Selector
SelectorImpl实现了Selector的register接口.SelectionKey的创建是在这一步进行的.
这一步又会调用具体Selector实现(如WindowsSelectorImpl、EPollSelectorImpl等)的implRegister方法.

    protected final SelectionKey register(AbstractSelectableChannel var1, int var2, Object var3) {
        //只允许SelChImpl实现类可以注册.
        if (!(var1 instanceof SelChImpl)) {
            throw new IllegalSelectorException();
        } else {
            //创建SelectionKey并返回.
            SelectionKeyImpl var4 = new SelectionKeyImpl((SelChImpl)var1, this);
            var4.attach(var3);
            synchronized(this.publicKeys) {
                //注册到Selector实现里.
                this.implRegister(var4);
            }
            var4.interestOps(var2);
            return var4;
        }
    }

WindowsSelectorImpl::implRegister

windows下的JDK8默认实现是这个. 总的来说selector维护了一批SelectionKey.

    //从SelectorImpl下继承来的.
    protected HashSet<SelectionKey> keys = new HashSet();
    //一个存放 selectionKey的Map(继承于HashMap)
    private final WindowsSelectorImpl.FdMap fdMap = new 
WindowsSelectorImpl.FdMap();
    //一个用于存放SelectionKey的数组. 此处自己用数组实现增删可能是为了快?
    private SelectionKeyImpl[] channelArray = new 
SelectionKeyImpl[8];
    private int totalChannels = 1;

    protected void implRegister(SelectionKeyImpl var1) {
        synchronized(this.closeLock) {
            if (this.pollWrapper == null) {
                throw new ClosedSelectorException();
            } else {
                //判断数组是否需要扩容
                this.growIfNeeded();
                //追加至channelArray数组的末尾.
                this.channelArray[this.totalChannels] = var1;
                //并且selectionKey自己也记录着自己在selector中的哪个位置.
                var1.setIndex(this.totalChannels);
                //往map集合里存一个
                this.fdMap.put(var1);
                //往set集合里存一个
                this.keys.add(var1);
                //存放文件句柄信息( fd ( file description ))
                this.pollWrapper.addEntry(this.totalChannels, var1);
                ++this.totalChannels;
            }
        }
    }

EpollSelectorImpl::implRegister

    protected void implRegister(SelectionKeyImpl key) {
        if (this.closed) {
            throw new ClosedSelectorException();
        } else {
            SelChImpl ch = key.channel;
            //文件描述符
            int fd = Integer.valueOf(ch.getFDVal());
            //记录fd与selectoinKey的映射.
            this.fdToKey.put(fd, key);
            //将新的fd存入EPollArrayWrapper(负责对Epoll调用)
            this.pollWrapper.add(fd);
             //往set集合里存一个
            this.keys.add(key);
        }
    }

select流程

SelectorImpl.doSelect(long timeout)

WindowsSelectorImpl::doSelect

windows JDK8 默认自带的Selector的doSelect()方法.

    
    //存放着文件句柄,基本用unsafe直接操作内存.
    private PollArrayWrapper pollWrapper = new PollArrayWrapper(8);

    protected int doSelect(long var1) throws IOException {
        if (this.channelArray == null) {
            throw new ClosedSelectorException();
        } else {
            this.timeout = var1;
            //清除注销队列(有个集合记录了cancel的key.通过这个方法将其正式清除)
            this.processDeregisterQueue();
            if (this.interruptTriggered) {
                this.resetWakeupSocket();
                return 0;
            } else {
                //启动/销毁WindowsSelectorImpl.SelectThread线程,保证有threadsCount(每增加1024个连接,这个值+1)个线程在工作.每个SelectThread都有一个SubSelector去poll这个Selector里的PollArrayWrapper里的句柄.  *推测*  并发分段轮询机制.
                this.adjustThreadsCount();
                this.finishLock.reset();
                this.startLock.startThreads();

                try {
                    this.begin();

                    try {
                        //负责一开始1024个channel的poll工作.后面的每1024个连接都将给SelectThread的SubSelector负责poll. 据说系统调用的poll会因为句柄太多影响性能,所以进行拆分.(待证)
                        this.subSelector.poll();
                    } catch (IOException var7) {
                        this.finishLock.setException(var7);
                    }

                    if (this.threads.size() > 0) {
                        this.finishLock.waitForHelperThreads();
                    }
                } finally {
                    this.end();
                }

                this.finishLock.checkForException();
                //清除Cancel了的连接.
                this.processDeregisterQueue();
                //更新keys事件. 这个方法的核心在于SubSelector的processSelectedKeys()方法
                int var3 = this.updateSelectedKeys();
                this.resetWakeupSocket();
                return var3;
            }
        }
    }

EPollSelectorImpl::doSelect

    protected int doSelect(long timeout) throws IOException {
        if (this.closed) {
            throw new ClosedSelectorException();
        } else {
            this.processDeregisterQueue();
            try {
                this.begin();
                this.pollWrapper.poll(timeout);
            } finally {
                this.end();
            }
            this.processDeregisterQueue();
            int var3 = this.updateSelectedKeys();
            if (this.pollWrapper.interrupted()) {
               this.pollWrapper.putEventOps(this.pollWrapper.interruptedIndex(), 0);
                synchronized(this.interruptLock) {
                    this.pollWrapper.clearInterrupted();
                    IOUtil.drain(this.fd0);
                    this.interruptTriggered = false;
                }
            }
            return var3;
        }
    }

poll具体实现

WindowsSelectorImpl.SelectThread.SubSelector::poll

此处windows每次poll系统调用传递一堆fd,推测此处是比linux慢的原因?

        private SubSelector(int var2) {
            this.readFds = new int[1025];
            this.writeFds = new int[1025];
            this.exceptFds = new int[1025];
            this.pollArrayIndex = (var2 + 1) * 1024;
        }
        //每次poll都传递一堆的fd
        private int poll() throws IOException {
            return this.poll0(WindowsSelectorImpl.this.pollWrapper.pollArrayAddress, Math.min(WindowsSelectorImpl.this.totalChannels, 1024), this.readFds, this.writeFds, this.exceptFds, WindowsSelectorImpl.this.timeout);
        }

EPollArrayWrapper::poll

    int poll(long var1) throws IOException {
        this.updateRegistrations();
        // native方法,没进去看.但估计基本上就是调用epoll的wait了.
        this.updated = this.epollWait(this.pollArrayAddress, NUM_EPOLLEVENTS, var1, this.epfd);

        for(int var3 = 0; var3 < this.updated; ++var3) {
            if (this.getDescriptor(var3) == this.incomingInterruptFD) {
                this.interruptedIndex = var3;
                this.interrupted = true;
                break;
            }
        }

        return this.updated;
    }
posted @ 2020-07-10 19:15  江借时www  阅读(440)  评论(0编辑  收藏  举报