netty前传:从传统I/O到Reactor的三种模型
作者:互联网
netty前传:从传统I/O到Reactor的三种模型
对最近学习netty的一些知识的一个汇总,对前人知识总结的一些个人理解。
一. 传统阻塞I/O
传统的阻塞I/O的网络服务端架构如下:
这里衍生出一些问题:
1. 我们在处理客户端读到的数据时,会发生阻塞。
2. 虽然,我们可以使用多线程的方式,来避免第一个问题,但每次收到一个client端的连接请求,就创建一个线程,如果client过多,那必然导致线程也很多,线程的创建、切换、销毁,消耗都是很大的。
因此,这种模式,不能适用于一些高并发的场景,只适用于一些连接不多,且固定的场景。
二. NIO
正由于传统I/O的缺点,引申出了NIO,NIO的大致架构如下:
详解:
1. 使用ServerSocketChannel的对象,监听对应端口,并注册到Selector中,用于监听OP_ACCEPT事件。
2. Selector获取到Client端连接请求,这个过程不阻塞,没有请求到达的时候,selector.select(time)可以直接返回,然后程序可以去干自己的事
3. 获取到Client请求后,通过ServerSocketChannel对象,去生成一个SocketChannel对象,并将它注册到Selector中,用于监听OP_READ事件。
4. 同样,Selector监听Client发消息的读事件,此过程也不阻塞。收到消息后,server端读数据直接从Buffer中获取,不需要像I/O那样,使用read(),在那阻塞等待client端的数据。
5. server端拿到数据后,就可以去处理这些数据了,处理完后,Selector继续监听新的事件。
Server端的代码也很简单,如下
public class Test { public static void main(String[] args) throws Exception { new Test().server(); } public void server() throws Exception { Selector selector = Selector.open(); ServerSocketChannel server = ServerSocketChannel.open(); server.bind(new InetSocketAddress(5678)); server.configureBlocking(false); server.register(selector, SelectionKey.OP_ACCEPT); while (true) { int keys = selector.select(); if (keys <= 0) { continue; } Set slKeys = selector.selectedKeys(); Iterator iterator = slKeys.iterator(); while (iterator.hasNext()) { SelectionKey key = (SelectionKey) iterator.next(); deal(key, server, selector); iterator.remove(); } } } public void deal(SelectionKey key, ServerSocketChannel server, Selector selector) throws IOException { if (key.isAcceptable()) { SocketChannel channel = server.accept(); channel.configureBlocking(false); channel.register(selector, SelectionKey.OP_READ); } else if (key.isReadable()) { SocketChannel channel = (SocketChannel) key.channel(); //实际业务数据操作 } } }
这种方式,解决了I/O的几个问题
1. 不需要每个Client连接就创建新的线程,一个selector,就可以轮询的去获取到client的连接事件,且不阻塞。
2. 数据读取不需要阻塞,有一个Buffer和SocketChannel绑定了,OP_READ事件到达的时候,直接从Buffer中取数据即可。
但是,NIO依旧存在一些问题:
1. 编码过于繁琐
2. selector还是有瓶颈,如果Client数量过多,那每次拿到请求事件,然后再去把SocketChannel注册到selector,然后selector得到读事件,然后再去读取数据,处理业务,这个过程是同步执行的,如果数据量大,处理业务复杂,那还是会影响到其他的client端
因此这种方式适用的场景依旧是,client不是特别多,并且业务逻辑,数据处理相对简单的场景,比如聊天系统等。
针对NIO的一些问题,就产生了netty。针对netty,又有必要介绍reactor的三种线程模型
三. Reactor的三种线程模型
1. 单Reactor单线程模型
一个线程采用selecor执行所有的accept请求、处理所有业务逻辑。
前面介绍的NIO用的就是这种
缺点:1)单个线程不能利用多核cpu优势
2)不适用高并发场景
3)如果线程异常或者有死循环,会导致整个系统通信模块不可用。
2. 单Reactor多线程模型
一个线程通过selector不断轮询接收新的请求,将连接交给线程池中某个线程来处理。
这种方式,其实就是将处理具体业务部分抽离出来,放到线程中去处理,但是数据的读取和发送,还是在那一个主线程中。
缺点同样是,如果client太多,那么accept的那个线程处理不过来。
3. 主从Reactor多线程模型(netty就是使用这种)
就是在单reactor多线程基础之上,再弄线程,一个线程(其实是线程池)来专门处理连接请求,一个线程(其实是线程池)用来处理read和send请求,然后具体的业务,再按照单reactor多线程的方式处理。
介绍了这么多,其实Reactor的这些模型就是在原始NIO基础之上形成的,总结来说,就是,一个线程池用来处理连接请求(mainReactor),一个线程池用来处理read和write(subReactor),另外的线程池用来处理具体业务逻辑
标签:netty,前传,处理,Selector,NIO,selector,线程,server,Reactor 来源: https://www.cnblogs.com/hliy032/p/13889849.html