IO多路复用
就是经过一种机制,一个进程能够监听多个文件描述符,一个某个描述符就绪(通常是读就绪或写就绪),就可以通知程序进行相应的读写操做。select、poll、epoll本质上都是同步IO,由于他们须要在读写事件就绪后本身负责读写,即这个读写过程是阻塞的,而异步IO则无需本身负责读写,异步IO的实现会把数据从内核拷贝到用户空间。linux
select 函数监视的文件描述符分3类,分别是writefds、readfds、和exceptfds。调用后select函数会阻塞,直到有描述符就绪(有数据 可读、可写、或者有except),或者超时(timeout指定等待时间,若是当即返回设为null便可),函数返回。当select函数返回后,能够经过遍历fdset,来找到就绪的描述符。segmentfault
poll与select相似,略过。网络
epoll是在linux 2.6内核中提出的,是select和poll的加强版本。数据结构
epoll支持水平触发和边缘触发,最大的特色在于边缘触发,它只告诉进程哪些fd刚刚变为就绪态,而且只会通知一次。还有一个特色是,epoll使用“事件”的就绪通知方式,一旦该fd就绪,内核就会采用相似callback的回调机制来激活该fd。并发
thrift提供的网络服务模型有阻塞服务模型、非阻塞服务模型:负载均衡
该模式采用最简单的阻塞IO,一次只能接收并处理一个socket,处理流程以下:
此种模式效率低下,生产不会使用,略过。异步
TThreadPoolServer模式采用阻塞socket方式工做,主线程负责阻塞式
(划重点,不是select的方式)监听是否有新socket到来,具体的业务处理交由一个线程池来处理。socket
accept部分的代码以下:函数
protected TSocket acceptImpl() throws TTransportException { if (serverSocket_ == null) { throw new TTransportException(TTransportException.NOT_OPEN, "No underlying server socket."); } try { // 阻塞式监听新的链接 Socket result = serverSocket_.accept(); TSocket result2 = new TSocket(result); result2.setTimeout(clientTimeout_); return result2; } catch (IOException iox) { throw new TTransportException(iox); } }
具体模型以下:高并发
TThreadPoolServer本质是One Thread Per Connection
模型。模型受限于线程池的最大线程数,在链接数很大话,请求只能排队,对于高并发的场景,此模型并不合适。
TNonblockingServer模式也是单线程工做,可是采用NIO的模式,借助Channel/Selector机制, 采用IO事件模型来处理。本质是一种event-loop
模型。
具体模型以下:
event-loop的核心代码以下:
private void select() { try { // 等待事件,jdk7以前的版本存在问题,会存在会将CPU打满的状况,没有事件,select却返回,从而将CPU打满;Netty中经过threshold,解决了该问题 selector.select(); // 获取IO事件 Iterator<SelectionKey> selectedKeys = selector.selectedKeys().iterator(); while (!stopped_ && selectedKeys.hasNext()) { SelectionKey key = selectedKeys.next(); selectedKeys.remove(); // skip if not valid if (!key.isValid()) { cleanupSelectionKey(key); continue; } // if the key is marked Accept, then it has to be the server // transport. // 处理链接事件 if (key.isAcceptable()) { handleAccept(); } else if (key.isReadable()) { // deal with reads // 处理读事件 handleRead(key); } else if (key.isWritable()) { // deal with writes // 处理写事件 handleWrite(key); } else { LOGGER.warn("Unexpected state in select! " + key.interestOps()); } } } catch (IOException e) { LOGGER.warn("Got an IOException while selecting!", e); } }
这个模型通常由一个event dispatcher等待各种事件,待事件发生后原地调用对应的event handler,所有调用完后等待更多事件,故为"loop"。这个模型的实质是把多段逻辑按事件触发顺序交织在一个系统线程中。一个event-loop只能使用一个核,故此类程序要么是IO-bound,要么是每一个handler有肯定的较短的运行时间(好比http server),不然一个耗时漫长的回调就会卡住整个程序,产生高延时。在实践中这类程序不适合多开发者参与,一我的写了阻塞代码可能就会拖慢其余代码的响应。因为event handler不会同时运行,不太会产生复杂的race condition,一些代码不须要锁。此类程序主要靠部署更多进程增长扩展性。
THsHaServer继承于TNonblockingServer,引入了线程池提升了任务处理的并发能力。THsHaServer是半同步半异步(Half-Sync/Half-Async)的处理模式,Half-Aysnc用于IO事件处理(Accept/Read/Write),Half-Sync用于业务handler对rpc的同步处理上。
具体模型以下:
THsHaServer与TNonblockingServer模式相比,THsHaServer在完成数据读取以后,将业务处理过程交由一个线程池来完成,主线程直接返回进行下一次循环操做,效率大大提高。
可是,主线程仍然须要处理accpet、read、write时间,当并发量很是大,读取或者发送的数据量比较大时,会将主线程阻塞住,新的链接没法被及时处理。
TThreadedSelectorServer是对THsHaServer的一种改进,它将selector中的read/write事件从主线程中剥离出来。
TThreadedSelectorServer是thrift提供的最高效的网络模型。具体模型以下:
构成以下:
总结:TThreadedSelectorServer模式,其实就是标准的Reactor模式,Tomcat7之后的版本、Cobar、MyCat(分库分表proxy)基本都是这个套路,具体实现略有差别。