通过前面对duubo的介绍,我们支持,dubbo底层默认的使用netty作为nio框架来进行网络通信,等于说netty是基础,dubbo是建立在netty之上的能力扩展,所以后面我们会通过系列文章,对netty做一个系列化的介绍。只有通过对netty的完全的理解,我们才能完全的吃透dubbo,使用dubbo。
Selector是 SelectableChannel对象的多路复用器
可以使用Selector的 open 方法来创建一个Selector对象,open方法会使用系统默认的java.nio.channels.spi.SelectorProvider 对象来创建一个新的Selector对象。
也可以使用java.nio.channels.spi.SelectorProvider的openSelector方法来创建一个Selector对象。
一个Selector一直保持 open 状态,除非显示的调用其close 方法。
一个 selectable的channel注册到一个Selector之后会 返回一个 SelectionKey对象来表征这个关系。一个Selector维护这三组 selection keys。
其中 key set 存储的 keys 代表了所有注册到此Selector的当前的Channel。可以通过 keys() 方法返回此set
其中selected-key 集合中存储的keys代表在一个 selection操作期间,注册到此Selector上的某些Channel,这些Channel至少被Selector感知到了发生了其自注册的感兴趣事件。selected-key永远是key set的一个子集。
还有一组canneled-key ,这个集合不能被我们直接的获取,其存储了那些关联的Channel没有被deregistered 但是被cancelled的key。
当Selector被初创出来的时候,这三个集合都是空集合。
当通过调用SelectableChannel的register(Selector,int)方法时,一个key 会被加入到Selector的 key set 集合中。在发生 selection操作期间,被cancelled的key会被移除出key set。 Key set集合不能直接的被修改。
不管是关闭channel还是通过 SelectionKey的cancel方法都会将此channel关联的key加入到canneled-key 集合。取消一个key会导致在下个selection操作期间会将此关联的channel注销,并将此key在selector关联的所有的 key set集合里面移除(等于说取消是有延迟的)
在selection操作期间,才会往selected-key集合加入keys。可以通过Set的remove或是Iterator的remove方法可以将selected-key集合里面的key移除。Keys不能直接的加入selected-key集合,除非是在selection发生期间。
在每个selection操作期间,selector关联的selected-key集合都可能加入和移除key。当调用selector的select(),select(long)或是selectNow()方法的时候,会触发selection操作。在此期间会触发三个步骤。
第一步
将在canceled-key 集合里面的key在selecor关联的三个key set集合里面移除(如果是其中的成员的话),经过这一步,cancelled-key集合会被清空。
第二步 操作系统会查询更新那些已经触发了感兴趣事件的channel到 ready 态。
1 如果上面查询出来的key(可以同认为是关联的channel)不在selected-key集合里面,此key将会添加到selected-key集合里面,并将已经发生的兴趣事件被更新到ready-operation set集合,ready-operation set集合先前的值会被清空。
2如果查询出来的key已经在selected-key里面,那么新的感兴趣事件会与原先的感兴趣事件的 并集存储到ready-operation set。
如果key set集合里面的channel没有在selector上面注册感兴趣事件,那么selected-key集合的ready-operation set不会做任何的更新。
Selector本身是线程安全的,但是其key set集合不是的。
由于key可以被随时的取消,channel也可以被随时的关闭,即使我们selector的key集合里面看到了某个key,也不能认为此key是安全的,或其关联的channel是open状态。由于其他的线程随时可以取消一个key或是关闭一个channel,应用代码应该格外的小心的对其状态进行校验。
当一个线程在调用 select()或是select(long)方法堵塞的时候,可以被其他线程已以下三种情况中断。
被其他的线程调用了 wakeup方法
被其他线程调用了close方法
调用当前堵塞线程的interrupt方法,这时被堵塞线程的中断状态会被重置,继而wakeup方法会被触发。
在并发场景下,总的来说,key set集合和 selected-key集合都不是线程安全的,必须进行同步控制。
//=========================================
SelectionKey表征了SelectableChannel与Selector的注册关系
当一个channel注册到selector上的时候会创建一个selectionkey出来,除非调用selectionkey的cancel方法,或者关闭channel或者关闭selector,selectionkey始终有效。
取消一个selectionkey的时候不会立即的从其关联的selector上面移除掉,其会立即的添加到selector关联的cancelled-key集合里面去,下次selection操作期间,会从selector关联的三个集合里面都移除掉。
通过调用isValid方法可以测试key是否有效。
Selection key用两个整数来代表两个操作集合,每个整数的每个bit位代表其key关联的channel支持的 selectable 操作。
Interest set在key被创建的时候被初始化,可以通过interestOps(int)方法进行修改。其表征了在selector进行selection操作期间channel注册的感兴趣的操作。
Ready set属于interest set的子集,表征了在selector在进行selection操作期间在interest set上检测到的已经准备好的操作。
Selection key 的 ready set按时着此channel已经准备好进行进行某些网络操作,但是也不一定,因为其他线程也可以对此channel进行取消的操作,所以在检测到ready set有值之后,应该尽快的进行相关操作。
selelctionkey类定义了一些通用的位操作,但是selectionKey的不同的子类可以定义自己独特的位操作,如果定义了某个子类不支持的位操作,会报运行时错误。
SelelctKey的attach方法可以绑定一些业务数据来表征某些别的信息。