1. 程式人生 > >高併發程式設計thirft原始碼解析之Selector

高併發程式設計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 received
Iterator<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大部分生態元件中都廣泛使用到了。