首頁 > 軟體

Netty分散式NioEventLoop優化selector原始碼解析

2022-03-25 16:00:14

優化selector

selector的建立過程

在剖析selector輪詢之前, 我們先講解一下selector的建立過程

回顧之前的小節, 在建立NioEventLoop中初始化了唯一系結的selector:

NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider, 
             SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler) {
    super(parent, executor, false, DEFAULT_MAX_PENDING_TASKS, rejectedExecutionHandler);
    //程式碼省略
    provider = selectorProvider;
    selector = openSelector();
    selectStrategy = strategy;
}

這裡 selector = openSelector() 初始化了selector

我們跟到openSelector()中:

private Selector openSelector() {
    final Selector selector;
    try {
        //呼叫jdk底層的api
        selector = provider.openSelector();
    } catch (IOException e) {
        throw new ChannelException("failed to open a new selector", e);
    }
    //判斷是否需要關閉優化(預設false, 也就是預設需要優化)
    if (DISABLE_KEYSET_OPTIMIZATION) {
        return selector;
    }
    //用這個資料結構替換原生的SelectionKeySet
    final SelectedSelectionKeySet selectedKeySet = new SelectedSelectionKeySet();
    Object maybeSelectorImplClass = AccessController.doPrivileged(new PrivilegedAction<Object>() {
        @Override
        public Object run() {
            try {
                //通過反射拿到sun.nio.ch.SelectorImpl這個類的class物件
                return Class.forName("sun.nio.ch.SelectorImpl", false, PlatformDependent.getSystemClassLoader());
            } catch (ClassNotFoundException e) {
                return e;
            } catch (SecurityException e) {
                return e;
            }
        }
    });
    //判斷拿到的是不是class物件並且是不是Selector的實現類
    if (!(maybeSelectorImplClass instanceof Class) ||!((Class<?>) maybeSelectorImplClass).isAssignableFrom(selector.getClass())) {
        if (maybeSelectorImplClass instanceof Exception) {
            Exception e = (Exception) maybeSelectorImplClass;
            logger.trace("failed to instrument a special java.util.Set into: {}", selector, e);
        }
        //如果不是他的實現, 就直接返回原生select
        return selector;
    }
    //如果是它的實現, 就拿到其class物件
    final Class<?> selectorImplClass = (Class<?>) maybeSelectorImplClass;
    Object maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() {
        @Override
        public Object run() {
            try {
                //通過反射拿到selectedKeys和publicSelectedKeys兩個屬性, 預設這兩個屬性底層都是hashSet方式實現的
                Field selectedKeysField = selectorImplClass.getDeclaredField("selectedKeys");
                Field publicSelectedKeysField = selectorImplClass.getDeclaredField("publicSelectedKeys");
                //設定成可修改的
                selectedKeysField.setAccessible(true);
                publicSelectedKeysField.setAccessible(true);
                //將selector的這兩個屬性替換成Netty的selectedKeySet
                selectedKeysField.set(selector, selectedKeySet);
                publicSelectedKeysField.set(selector, selectedKeySet);
                return null;
            } catch (NoSuchFieldException e) {
                return e;
            } catch (IllegalAccessException e) {
                return e;
            } catch (RuntimeException e) {
                if ("java.lang.reflect.InaccessibleObjectException".equals(e.getClass().getName())) {
                    return e;
                } else {
                    throw e;
                }
            }
        }
    });
    if (maybeException instanceof Exception) {
        selectedKeys = null;
        Exception e = (Exception) maybeException;
        logger.trace("failed to instrument a special java.util.Set into: {}", selector, e);
    } else {
        //將優化後的keySet儲存成NioEventLoop的成員變數
        selectedKeys = selectedKeySet;
        logger.trace("instrumented a special java.util.Set into: {}", selector);
    }
    return selector;
}

程式碼剖析

這裡程式碼比較長, 我們一點一點的剖析:

首先 selector = provider.openSelector() 這裡建立了jdk底層的selector

if (DISABLE_KEYSET_OPTIMIZATION) {
    return selector;
}

這裡判斷了是否關閉優化功能, 預設是false, 也就是需要優化, 這裡的意思就是netty需要對jdk原生的selector進行了優化, 我們知道selector在select()操作時候, 會通過selector.selectedKeys()操作返回一個Set<SelectionKey>, 這個是Set型別, netty對這個set進行了處理, 使用SelectedSelectionKeySet的資料結構進行替換, 當在select()操作時將key存入一個SelectedSelectionKeySet的資料結構中

final SelectedSelectionKeySet selectedKeySet = new SelectedSelectionKeySet();

這裡一步建立了這個優化後的資料結構

簡單跟一下SelectedSelectctionKeySet這個類的構造方法:

SelectedSelectionKeySet() {
    keysA = new SelectionKey[1024];
    keysB = keysA.clone();
}

初始化了兩個屬性keysA和keysB, 說明這類其實底層是通過陣列實現的, 通過運算元組下標會有更高的效率

這個類的的flip()方法, 則返SelectionKey[]陣列

SelectionKey[] flip() {
    if (isA) {
        isA = false;
        keysA[keysASize] = null;
        keysBSize = 0;
        return keysA;
    } else {
        isA = true;
        keysB[keysBSize] = null;
        keysASize = 0;
        return keysB;
    }
}

再看下其他方法:

@Override
public boolean remove(Object o) {
    return false;
}
@Override
public boolean contains(Object o) {
    return false;
}
@Override
public Iterator<SelectionKey> iterator() {
    throw new UnsupportedOperationException();
}

我們看到remove()方法, contains()方法都返回了false, 說明其不支援刪除方法和包含方法, iterator()方法則直接丟擲異常, 說明其不支援迭代器操作

回到openSelector()中:

再往下看, 這裡通過 Class.forName("sun.nio.ch.SelectorImpl", false, PlatformDependent.getSystemClassLoader()) 建立了一個SelectorImpl的class物件

 if(!(maybeSelectorImplClass instanceof Class) ||!((Class<?>) maybeSelectorImplClass).isAssignableFrom(selector.getClass())) 

這裡判斷拿到的物件是否為class物件並且是否為Selector的實現類, 如果不是, 則直接返回jdk的selector

如果是, 就繼續轉化成class物件

然後就做了真正的替換操作:

//通過反射拿到selectedKeys和publicSelectedKeys兩個屬性, 預設這兩個屬性底層都是hashSet方式實現的
Field selectedKeysField = selectorImplClass.getDeclaredField("selectedKeys");
Field publicSelectedKeysField = selectorImplClass.getDeclaredField("publicSelectedKeys");
//設定成可修改的
selectedKeysField.setAccessible(true);
publicSelectedKeysField.setAccessible(true);
//將selector的這兩個屬性替換成Netty的selectedKeySet
selectedKeysField.set(selector, selectedKeySet);
publicSelectedKeysField.set(selector, selectedKeySet);

通過註釋我們不難看出, 這裡將新建立selectedKeySet替換了selector物件中的selectedKeysField, 和selectedKeysField兩個屬性

最後通過 selectedKeys = selectedKeySet 將優化的資料結構selectedKeySet儲存在NioEventLoop的成員變數中

最後返回優化後的selector

這樣, selector在select()操作的過程中, 如果有就緒事件則會將返回的key存放在selectedKeySet所對應的陣列中

以上就是Netty分散式NioEventLoop優化selector原始碼解析的詳細內容,更多關於Netty分散式NioEventLoop優化selector的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com