高併發程式設計thirft原始碼解析之Selector
Selector作用
關於套接字程式設計,有一套經典的IO模型需要提前介紹一下:.
同步IO模型:
阻塞式IO模型
非阻塞式IO模型
IO複用模型 使用selector
訊號驅動式IO模型
非同步IO模型
使用aio_read
thrift裡面用到IO模型就是IO複用模型,《Unix網路程式設計》一書中說它是同步IO模型,selector用法是阻塞的。
實際上selector模型中的套接字可以通過如下方式設定位非阻塞方式。
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
Selector就是一個事件選擇器。是個作業系統核心實現的套接字連線選擇器。
早期的linux系統是用poll模式,新的模式是epoll。
目前用C開發套接字程式設計仍然可以使用這兩種方式。
而用JDK開發套接字程式設計時,linux JDK預設使用epoll作為selector的事件觸發機制。
Thrift是如何使用Selector
thirft裡面使用了兩個Selector,一個是監聽socket連線是否進來
另外一個是監聽socketChanell是否可讀或者可寫。
第一處TThreadedSelectorServer.AcceptThread
/** * Select and process IO events appropriately: If there are connections to * be accepted, accept them. */ private void select() { try { // wait for connect events. acceptSelector.select(); // process the io events we receivedIterator<SelectionKey> selectedKeys = acceptSelector.selectedKeys().iterator(); while (!stopped_ && selectedKeys.hasNext()) { SelectionKey key = selectedKeys.next(); selectedKeys.remove(); // skip if not valid if (!key.isValid()) { continue; } if (key.isAcceptable()) { handleAccept(); } else { LOGGER.warn("Unexpected state in select! " + key.interestOps()); } } } catch (IOException e) { LOGGER.warn("Got an IOException while selecting!", e); } }
這個selector主要用來接受客戶端發過來的tcp連線請求,每當進來一個連線,就用handleaccept方法分配到一個執行緒裡面去處理,
將對應socketchannel加入BlockingQueue進行緩衝。然後在消費執行緒run裡面慢慢消費處理。
第二處:TThreadedSelectorServer.SelectorThread
/** * Select and process IO events appropriately: If there are existing * connections with data waiting to be read, read it, buffering until a * whole frame has been read. If there are any pending responses, buffer * them until their target client is available, and then send the data. */ private void select() { try { // wait for io events. selector.select(); // process the io events we received 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 (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); } }
第二處selector是用來監聽已經進來的連線是否可讀可寫的,並進行對應處理。
thirft高效能的原因
thrift之所以能在高併發時維持高效能,我認為主要是因為:
1、使用了同步非阻塞IO
2、使用了多執行緒和佇列來處理套接字併發連線
3、支援資料壓縮,節省頻寬
4、程式碼封裝得比較成熟,比如一些channel抽象還有processor的封裝等。
5、易用性。支援跨語言,使用簡單方便
目前thrift在hadoop大部分生態元件中都廣泛使用到了。