Skip to content

Latest commit

 

History

History
280 lines (226 loc) · 14.1 KB

8.Selector.md

File metadata and controls

280 lines (226 loc) · 14.1 KB
title date categories tags description
Selector
2020-01-20
Netty
Netty
网络

Selector 介绍

Selecto能够检测多个注册在它上面的通道上,是否有事件发生。因此,可以先将多个通道(channel)注册到一个Selector上面,注册的时候需要指出当前这个通道(channel)感兴趣的事件是什么。然后 Selector 会一直检测,哪个通道上有事件发生。

当有事件发生的时候,就可以获取事件,然后进行相应的处理。

采用这样的架构模型,有什么好处?

只有当连接(通道、channel)上面真正有事件(比如读写)发生的时候,才会进行操作(比如读写),这样就不需要阻塞线程,就等通道上的各种事件就绪的时候,再去处理就好了。

比如Read事件,之前使用BIO的时候,read()方法会阻塞在那里,一直等着能读到数据的时候才会返回。

现在用NIO,只要让Selector在那检测就好了, 当它检测到读事件就绪,也就是在通道上已经有数据准备好让我们读了,才会通知我们去获取数据,这样就不需要阻塞了。

Selector 文档

/**
 * A multiplexor of {@link SelectableChannel} objects.
 * SelectableChannel 对象的多路复用器
 *
 * A selector may be created by invoking the {@link #open open} method of
 * this class, which will use the system's default {@link
 * java.nio.channels.spi.SelectorProvider selector provider} to
 * create a new selector.  
 * 可以通过 Selector 类的静态方法open()来创建一个 Selector对象, open()方法将会使用系统默认的 SelectorProvider来创建一个新的 selector。
 *
 * A selector may also be created by invoking the
 * {@link java.nio.channels.spi.SelectorProvider#openSelector openSelector}
 * method of a custom selector provider.  
 * 也可以通过 调用自定义的SelectorProvider的openSelector() 方法创建一个selector
 * 
 * A selector remains open until it is
 * closed via its {@link #close close} method.
 * 一个selector 创建后,将会一直保持打开的状态,直到调用close()方法将它关闭
 *
 *
 * A selectable channel's registration with a selector is represented by a
 * {@link SelectionKey} object.  A selector maintains three sets of selection
 * keys:
 * 当一个 channel(连接、通道)注册到一个 selector 上之后, 就用一个 SelectionKey 来表示。就是用selectionKey 代表这个注册关系。
 * 一个selector 维护了三个 SelectionKey的集合。
 *
 *
 *   The <i>key set</i> contains the keys representing the current
 *   channel registrations of this selector.  This set is returned by the
 *   {@link #keys() keys} method.
 *   第一个集合 key set , 可以理解为全集。里面维护了当前注册到这个selector上的所有的channel的  SelectionKey 对象。用方法  keys() 来返回。
 *
 *   The <i>selected-key set</i> is the set of keys such that each
 *   key's channel was detected to be ready for at least one of the operations
 *   identified in the key's interest set during a prior selection operation.
 *   This set is returned by the {@link #selectedKeys() selectedKeys} method.
 *   The selected-key set is always a subset of the key set.
 *   第二个集合 selected-key set。它代表什么呢?
 *   (直白的翻译,很难懂)一个 key 对应的 channel 在一段时间内被检测到  存在于 感兴趣的事件列表中的至少一个操作 就绪了, 那么就把这个channel 对应的selectionKey 放到这个集合中。
 *   通俗的解释:当一个 channel 注册到selector 的时候,要指定一下自己关注哪些事件,比如 READ 事件。那么,当selector 在一段时间监测的过程中,检测到 channel 上有事件就绪了了,而且这个事件必须是在之前注册时指定的那个感兴趣的列表中的,这时,selector就会把这个 channel 对应的selectionKey 放到这个集合里面。 
 *   一句话解释:这个集合代表了当前有哪些channel上有事件发生。
 *   这个集合是由 selectedKeys()方法返回。 它总是 key set 的子集。
 *
 *   The <i>cancelled-key</i> set is the set of keys that have been
 *   cancelled but whose channels have not yet been deregistered.  This set is
 *   not directly accessible.  The cancelled-key set is always a subset of the
 *   key set.
 *   第三个集合  cancelled-key set , 里面存放的都是那些 被取消了,但是 channel 还没有被注销的 key。
 *   这个集合不能直接访问。 这个  cancelled-key set 总是 key set 的 子集。
 *
 *
 * <p> All three sets are empty in a newly-created selector.
 * 当selector新创建的时候, 三个集合都是空的。
 * 那一个key 是何时被添加到各个集合当中的呢?
 *
 * <p> A key is added to a selector's key set as a side effect of registering a
 * channel via the channel's {@link SelectableChannel#register(Selector,int)
 * register} method.  Cancelled keys are removed from the key set during
 * selection operations.  The key set itself is not directly modifiable.
 * 当channel 调用 register 方法注册到selector的时候,这个channel 对应的key 就会被添加到key set (说的是上面第一个集合)当中了。
 * 我们看下 register 方法的源码就可以看到:
 * 先用 findKey() 方法从 key set 中查找,如果找到, 就设置一下感兴趣的事件,然后就返回这个 SelectionKey 对象
 * 如果没找到,就表示是新注册的channel,调用底层的 register方法, 并返回一个 SelectionKey, 然后把这个SelectionKey 对象添加到key set 中。
 * 
 *     public final SelectionKey register(Selector sel, int ops,
                                       Object att)
        throws ClosedChannelException
    {
        synchronized (regLock) {
            if (!isOpen())
                throw new ClosedChannelException();
            if ((ops & ~validOps()) != 0)
                throw new IllegalArgumentException();
            if (blocking)
                throw new IllegalBlockingModeException();
            SelectionKey k = findKey(sel);
            if (k != null) {
                k.interestOps(ops);
                k.attach(att);
            }
            if (k == null) {
                // New registration
                synchronized (keyLock) {
                    if (!isOpen())
                        throw new ClosedChannelException();
                    k = ((AbstractSelector)sel).register(this, ops, att);
                    addKey(k);
                }
            }
            return k;
        }
    }
 *
 * <p> A key is added to its selector's cancelled-key set when it is cancelled,
 * whether by closing its channel or by invoking its {@link SelectionKey#cancel
 * cancel} method.  Cancelling a key will cause its channel to be deregistered
 * during the next selection operation, at which time the key will removed from
 * all of the selector's key sets.
 * 无论是关闭SelectionKey 对应的那个channel, 还是调用 SelectionKey 的 cancel方法,都会将 SelectionKey 添加到 cancelled key set 中
 * 取消一个 key 将导致它对应的channel在下一次选择操作期间被注销,这时该key将从selector的所有集合中删除。
 *
 * Keys are added to the selected-key set by selection
 * operations.  A key may be removed directly from the selected-key set by
 * invoking the set's {@link java.util.Set#remove(java.lang.Object) remove}
 * method or by invoking the {@link java.util.Iterator#remove() remove} method
 * of an {@link java.util.Iterator iterator} obtained from the
 * set.  
 * Key 在 selection操作的时候, 会被添加到selected-key set 中。说的是啥呢?
 * 就是当selector 监听的一系列 channel 上有 事件发生的时候,发生了事件的那个channel 对应的 selectionKey 就会被添加到 selected-key set 中了。
 * 准确的说,发生的事件一定要在注册的列表内啊。(发生的事件,必须是这个channel关注的事件才行)
 * 可以通过调用 集合 的  remove 方法直接将 key 从  selected-key set 中删除;
 * 也可以通过 set 获取到 Iterator对象, 然后调用 Iterator 对象的 remove 方法,将key从  selected-key set 中删除;
 *
 * Keys are never removed from the selected-key set in any other way;
 * they are not, in particular, removed as a side effect of selection
 * operations.  Keys may not be added directly to the selected-key set. 
 *
 * 决不能以任何其他方式将键从选定键集中删除。 作为选择操作的副作用,尤其不要将其删除。键可能不会直接添加到所选键集中。
 *
 * <a name="selop"></a>
 * <h2>Selection</h2>
 *
 * During each selection operation, keys may be added to and removed from a
 * selector's selected-key set and may be removed from its key and
 * cancelled-key sets.  Selection is performed by the {@link #select()}, {@link
 * #select(long)}, and {@link #selectNow()} methods, and involves three steps:
 * 在selection 操作期间,key  可能 从 key set、 cancelled-key set  selected-key set 中移除,也可能添加到这几个集合中。
 * 执行 selection 操作, 是通过调用 Selector.select()方法, selection操作包括三个步骤:
 *
 * <ol>
 *
 *   <li><p> Each key in the cancelled-key set is removed from each key set of
 *   which it is a member, and its channel is deregistered.  This step leaves
 *   the cancelled-key set empty. </p></li>
 *   第一步: 遍历 cancelled-key 中的每个 key , 只要 这个key  是 cancelled-key set 、key set、 selected-key set 的成员,都会被移除。
 *   并且,这个key对应的channel 将被注销。 这一步会导致 cancelled-key set 为空。
 *
 *   <li><p> The underlying operating system is queried for an update as to the
 *   readiness of each remaining channel to perform any of the operations
 *   identified by its key's interest set as of the moment that the selection
 *   operation began.  For a channel that is ready for at least one such
 *   operation, one of the following two actions is performed: </p>
 *   第二步:询问底层操作系统,去查询一下,所有的channel上是否有操作就绪。这个操作必须是在selection操作开始时(注册的时候)指定的那个感兴趣的事件(操作)。
 *   对于一个channel而言,如果有至少一个操作就绪,就执行下面两个操作中的一个:
 *   <ol>
 *
 *     <li><p> If the channel's key is not already in the selected-key set then
 *     it is added to that set and its ready-operation set is modified to
 *     identify exactly those operations for which the channel is now reported
 *     to be ready.  Any readiness information previously recorded in the ready
 *     set is discarded.  </p></li>
 *     如果 这个 channel 的key 不在 selected-key set,那么就把这个key 加入到 selected-key set,并且 修改 ready-operation set ,来标识该* channel的这些操作已经准备就绪。
 *     先前记录在ready-operation set集中的所有准备信息都将被丢弃
 *
 *     <li><p> Otherwise the channel's key is already in the selected-key set,
 *     so its ready-operation set is modified to identify any new operations
 *     for which the channel is reported to be ready.  Any readiness
 *     information previously recorded in the ready set is preserved; in other
 *     words, the ready set returned by the underlying system is
 *     bitwise-disjoined into the key's current ready set. </p></li>
 *     如果 这个 channel 的key 已经存在于  selected-key set, 就修改 ready-operation set 以标识通道已准备就绪的任何新操作。
 *     先前记录在ready-operation set中的任何准备就绪信息都将保留。
 *
 *   </ol>
 *
 *   If all of the keys in the key set at the start of this step have empty
 *   interest sets then neither the selected-key set nor any of the keys'
 *   ready-operation sets will be updated.
 *
 *   <li><p> If any keys were added to the cancelled-key set while step (2) was
 *   in progress then they are processed as in step (1). </p></li>
 *
 * </ol>
 *
 * <p> Whether or not a selection operation blocks to wait for one or more
 * channels to become ready, and if so for how long, is the only essential
 * difference between the three selection methods. </p>
 * Selector.select()  Selector.select(long) Selector.selectNow() 这三个方法的唯一区别是:selection 操作是否阻塞等待一个或者多个channel上有操作准备就绪,以及阻塞多长时间。
 *
 *
 * <h2>Concurrency</h2> 并发,不看了,太难了
 */

相关方法:

Selector 是个抽象类, 其类层级结构:

  • open() 创建选择器对象。其返回类型,在MacOS 上是: KQueueSelectorImpl

  • select() 监听所有注册的通道,当某个通道上有事件发生的时候,则将对应的SelectionKey加入到内部集合并且返回。

  • selectedKeys() 表示在 Selector 监听的时候, 有哪些通道上有感兴趣的事件发生了

  • Selector.keys() 表示所有注册到Selector上的通道对应的key

  • select()select(long timeout)方法是阻塞的。直到监听的通道上有事件发生。selectNow() 是非阻塞,立即返回

流程

  1. 服务端:一个线程上有一个Selector。调用 open() 创建Selector对象。

  2. 把多个Channel 都注册到Selector 上(注册的时候, 需要告诉Selector自己关注什么类型的事件)。

  3. 注册后, 返回SelectionKey。 SelectionKey 会被 Selector 管理起来, 放到集合里 protected HashSet<SelectionKey> keys 中。就是我们上面说的全集:key set

  4. Selector调用select()方法, 进行监听。

  5. select()方法返回, 表示有事件发生了, 返回int, 就是代表有几个通道发生事件了。

  6. 然后就可以通过 Selector 的 selectedKeys 集合(就是源码中所说的第二个集合),拿到有哪些通道上面有事件发生了。

  7. 再通过selectionKey可以获取到Channel

  8. 通过channel完成业务读写业务