Selector在netty和tomcat中的使用

普通nio的使用方式

普通方式的nio使用,客户端使用socketChannel进行connect,而服务端通过ServerSocketChannel的accept去接受客户端的socketChannel,
然后客户端和服务端通过socketChannel进行write和read。

缺点:我们可以想象下如果我们需要写还需要判断channel是否可写否则导致写失败,读取还需要判断channel是否可读否则读取到空数据,accept还需要判断channel是否有链接事件否则会阻塞,connect事件则需要判断之前的connect方法是否成功,否则还需要调用finishconnect如果不调用而直接read或者write会抛出异常。而这些都需要我们使用很多线程去轮询判断(connect可以不用 ,我们在调用connect为false可以直接调用finishconnect,但是其是阻塞的),如果我们只使用单线程去操作很有可能被第一个channel就阻塞住了 这就导致后续的其他channel 可读写链接或者accept的被延迟,而如果我们使用多线程,这就可能导致连接数太多资源损耗。因此selector就闪亮上场。

tips:我们可以通过channel获取到socket,然后给该socket设置各种属性,tcpNoDelay,timeout,缓存区大小等。

tips:nio的注册多个感兴趣事件就是用或符号,取消注册就是interestOps&~需要取消注册的事件

selector的select方法是返回有几个注册的selectionKey有就绪的io事件,然后我们可以通过selectedKeys方法获取到是那几个channel有io事件,这边还有一个需要注意的就是selectionKey的readyOps每次只返回一个准备好的io事件的,比如我们同事注册了write和read 如果都有会默认只返回一个事件等某个事件处理完再调用select就返回下一个事件。

netty使用selector

netty使用selector:服务端先启动一个ServerSocketChannel注册到一个selector(selector绑定一个loop)上面,而ServerSocketChannel一般只有accept(不算bind),所以当这个channel注册accept事件时候,selector发现有事件 了则我们在调用accept方法就不会长期阻塞了。当我们接收到socketchannel之后会注册read事件,这样有数据需要读取则再去获取,对于write则是我们在我们channel的通道已经写满的情况下再注册,这样通道可写的时候会告诉我们可以进行发送数据了从而不需要轮询判断。

而对于connect一般都是异步,如果立即建立成功则返回true,否则返回false然后需要调用finishConnect才可以,当我们每次connect返回false我们会注册一个connect事件 但是注意了 该事件会被selector的select方法给捕捉到(即当socketChannel准备好了就会触发connect事件,一般是注册了就会触发该事件),但是当我们调用write或者read的时候都会去判断我们是否注册了connect事件,如果注册了则先调用finishConnect方法。

注意如果我们在finishconnect成功之后还不取消注册该事件,则会导致我们的不停的注册该事件,其类似于write事件,必须在需要使用的时候再注册。

可以看到我们的ServerSocketChannel使用一个单独的selector,而对于socketchannel则可能多个channel共用一个selector。

tomcat使用selector

tomcat使用selector:使用accpetor线程持有ServerSocketChannel(一般都是一个acceptor线程就可以了多个线程也只是持有相同的ServerSocketChannel),而ServerSocketChannel并没有采用selector来管理,而是直接阻塞等待链接!

当accept接收到socketChannel,然后设置SocketChannel的属性和其socket,将channel注册到poller线程上的selector,其中注册的事件是read而属性是NioEndpoint.NioSocketWrapper而NioSocketWrapper含有一个shared的selector,当tomcat接收到请求之后,我们先将该channel取消注册在poller的selector上面,然后下面将将socketchannel注册到sharedSelector上面(此时候socketchannel是有read或者write事件发生的)。

我们注册socketChannel到sharedSelector是为了对阻塞读写进行timeout监控(netty可以使用idlestatehandler进行监控)。

NioSelectorPool和BlockPoller:NioSelectorPool根据条件选择是NioBlockingSelector还是selector pool。而NioBlockingSelector主要是采用shared的selector,其timeout通过CountDownLatch来等待锁都释放(即事件发生了)而BlockPoller则是专门用来处理如果有读写事件发生则释放锁。对于selector的pool中获取pool,然后通过selector的select方法进行timeout监控。

Http11InputBuffer和Http11OutputBuffer:这两个类非常简单就是一个buffer还有一个对应的NioSocketWrapper,然后读写socket就是底层依靠buffer传递数据。NioSocketWrapper的底层就是直接通过socketchannel进行读写,并依靠shared selector或者selector pool 进行读写超时监控!

tomcat的selector的总结就是-----通过阻塞方式获取socketchannel,然后将其注册到poller上的selector--注册read事件,如果poller的selector的read事件发生然后将该socketchannel从poller上的selector取消注册(这样读写就不会触发poller的selector),然后同步阻塞通过socketchannel进行读写(采用线程池或者直接使用poller线程操作),但是读写的时候有timeout超时,且为了避免需要不停的判断是否有读写事件发生所以讲socketchannel注册到selector上面,同时对于采用shared selector或者selector pool中的selector 分别通过countdownlatch或者selector的select方法同步阻塞获取读写事件发生。

tomcat nio并不是完全的非阻塞 只有poller是 而acceptor和worker都说同步阻塞,而我们使用netty可以做到异步非阻塞,所以netty性能比tomcat的nio好不少

你可能感兴趣的:(Selector在netty和tomcat中的使用)