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)基本都是這個套路,具體實現略有差別。