Donald_Draper 阅读(64) 评论(0)
netty 事件执行器组和事件执行器定义及抽象实现:http://donald-draper.iteye.com/blog/2391257
netty 多线程事件执行器组:http://donald-draper.iteye.com/blog/2391270
netty 多线程事件循环组:http://donald-draper.iteye.com/blog/2391276
netty 抽象调度事件执行器:http://donald-draper.iteye.com/blog/2391379
netty 单线程事件执行器初始化:http://donald-draper.iteye.com/blog/2391895
netty 单线程事件执行器执行任务与graceful方式关闭:http://donald-draper.iteye.com/blog/2392051
netty 单线程事件循环:http://donald-draper.iteye.com/blog/2392067
引言:
前面一篇文章我们看了单线程事件循环,来简单回顾一下:
单线程事件循环SingleThreadEventLoop,继承了单线程事件执行器,实现了事件循环接口,内部一个事件循环任务队列,我们可以把单线程事件循环看为一个简单的事件执行器,单线程事件循环中多了一个通道注册的方法,实际注册工作委托给通道关联的UnSafe。
今天我们来看一下Nio事件循环:
package io.netty.channel.nio;

import io.netty.channel.Channel;
import io.netty.channel.ChannelException;
import io.netty.channel.EventLoop;
import io.netty.channel.EventLoopException;
import io.netty.channel.SelectStrategy;
import io.netty.channel.SingleThreadEventLoop;
import io.netty.util.IntSupplier;
import io.netty.util.concurrent.RejectedExecutionHandler;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.ReflectionUtil;
import io.netty.util.internal.SystemPropertyUtil;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;

import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.channels.CancelledKeyException;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.spi.SelectorProvider;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * {@link SingleThreadEventLoop} implementation which register the {@link Channel}'s to a
 * {@link Selector} and so does the multi-plexing of these in the event loop.
 Nio单线程事件循环,注册关联通道到同一个选择器,以便复用事件循环。
 *
 */
public final class NioEventLoop extends SingleThreadEventLoop {

    private static final InternalLogger logger = InternalLoggerFactory.getInstance(NioEventLoop.class);
    //取消选择key计数器,清理间隔,当取消的选择key达到256个时,重置计数器,并重新进行选择操作
    private static final int CLEANUP_INTERVAL = 256; // XXX Hard-coded value, but won't need customization.
    //是否优化选择器key集合,默认为不优化
    private static final boolean DISABLE_KEYSET_OPTIMIZATION =
            SystemPropertyUtil.getBoolean("io.netty.noKeySetOptimization", false);

    private static final int MIN_PREMATURE_SELECTOR_RETURNS = 3;//最小的选择器重构阈值
    private static final int SELECTOR_AUTO_REBUILD_THRESHOLD;//选择器自动重构阈值,默认选择操作发生512次,重构

    private final IntSupplier selectNowSupplier = new IntSupplier() {
        @Override
        public int get() throws Exception {
            return selectNow();
        }
    };
    //Nio事件循环任务数量Callable
    private final Callable<Integer> pendingTasksCallable = new Callable<Integer>() {
        @Override
        public Integer call() throws Exception {
            return NioEventLoop.super.pendingTasks();
        }
    };

    // Workaround for JDK NIO bug.
    //
    // See:
    // - http://bugs.sun.com/view_bug.do?bug_id=6427854
    // - https://github.com/netty/netty/issues/203
    static {
        //获取java的bug等级
        final String key = "sun.nio.ch.bugLevel";
        final String buglevel = SystemPropertyUtil.get(key);
        if (buglevel == null) {
	    //如果bug等级为空,则在当前任务线程相同访问控制权限下,设置nio的bug等级为空
            try {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    @Override
                    public Void run() {
                        System.setProperty(key, "");
                        return null;
                    }
                });
            } catch (final SecurityException e) {
                logger.debug("Unable to get/set System Property: " + key, e);
            }
        }
       //初始选择器重构阈值
        int selectorAutoRebuildThreshold = SystemPropertyUtil.getInt("io.netty.selectorAutoRebuildThreshold", 512);
        if (selectorAutoRebuildThreshold < MIN_PREMATURE_SELECTOR_RETURNS) {
	    //不需要重构选择器
            selectorAutoRebuildThreshold = 0;
        }

        SELECTOR_AUTO_REBUILD_THRESHOLD = selectorAutoRebuildThreshold;

        if (logger.isDebugEnabled()) {
            logger.debug("-Dio.netty.noKeySetOptimization: {}", DISABLE_KEYSET_OPTIMIZATION);
            logger.debug("-Dio.netty.selectorAutoRebuildThreshold: {}", SELECTOR_AUTO_REBUILD_THRESHOLD);
        }
    }

    /**
     * The NIO {@link Selector}.
     */
    private Selector selector;//就绪选择key集合优化后的选择器
    private Selector unwrappedSelector;//没有包装过的选择器,即选择器提供者打开的原始选择器
    private SelectedSelectionKeySet selectedKeys;//选择key就绪集合

    private final SelectorProvider provider;//选择器提供者

    /**
     * Boolean that controls determines if a blocked Selector.select should
     * break out of its selection process. In our case we use a timeout for
     * the select method and the select method will block for that time unless
     * waken up.
     当选择器的选择操作阻塞时,wakenUp属性决定是否应该break选择操作过程。在我们的实现中,
     我们给选择操作一个超时时间, 除非选择操作被wakeup,否则选择操作达到超时时间,则break选择操作。
    
     */
    private final AtomicBoolean wakenUp = new AtomicBoolean();

    private final SelectStrategy selectStrategy;//选择策略

    private volatile int ioRatio = 50;//Nio处理Io事件的时间占比,以便可以处理器其他非IO事件
    private int cancelledKeys;//取消选择key计数器
    private boolean needsToSelectAgain;//是否需要重新选择
}

来看一下Nio事件循环中,变量的类型声明:
//IntSupplier
package io.netty.util;

/**
 * Represents a supplier of {@code int}-valued results.
  表示一个int值供应者
 */
public interface IntSupplier {

    /**
     * Gets a result.
     *
     * @return a result
     */
    int get() throws Exception;
}

再来看Nio事件循环内部实现:
private final IntSupplier selectNowSupplier = new IntSupplier() {
   //获取选择操作返回值,用于判断注册到当前选择器的选择通道是否有IO事件就绪
    @Override
    public int get() throws Exception {
        return selectNow();
    }
};

再来关注一下选择Now操作:
int selectNow() throws IOException {
    try {
        //直接委托个Nio事件循环选择器
        return selector.selectNow();
    } finally {
        // restore wakeup state if needed
	//如果选择操作后,需要唤醒等待选择操作的线程,则唤醒
        if (wakenUp.get()) {
            selector.wakeup();
        }
    }
}

再来看选择策略:
private final SelectStrategy selectStrategy;//选择策略


package io.netty.channel;
import io.netty.util.IntSupplier;

/**
 * Select strategy interface.
 *
 * Provides the ability to control the behavior of the select loop. For example a blocking select
 * operation can be delayed or skipped entirely if there are events to process immediately.
 选择策略接口提供了控制选择循环行为的方法。比如,如果有事件需要立刻处理,则可以阻塞选择操作或
 完全直接跳过。
 */
public interface SelectStrategy {

    /**
     * Indicates a blocking select should follow.
     阻塞选择操作
     */
    int SELECT = -1;
    /**
     * Indicates the IO loop should be retried, no blocking select to follow directly.
     如果没有选择操作阻塞,预示着应该重试IO事件循环,处理IO事件
     */
    int CONTINUE = -2;

    /**
     * The {@link SelectStrategy} can be used to steer the outcome of a potential select
     * call.
     *选择策略可以用于控制潜在的选择操作结果
     * @param selectSupplier The supplier with the result of a select result.
     选择结果提供者
     * @param hasTasks true if tasks are waiting to be processed.
     是否有任务待处理
     * @return {@link #SELECT} if the next step should be blocking select {@link #CONTINUE} if
     *         the next step should be to not select but rather jump back to the IO loop and try
     *         again. Any value >= 0 is treated as an indicator that work needs to be done.
     如果返回结果为-1,则下一步应该阻塞选择操作,如果返回结果为-2,则下一步应该调回IO事件循环,处理
     IO事件,而不是继续执行选择操作,返回值大于0,表示需要有工作要做,即注册到选择器的选择通道有IO事件
     就绪。
     */
    int calculateStrategy(IntSupplier selectSupplier, boolean hasTasks) throws Exception;
}

从上面可以看出,Nio事件循环内部有一个取消选择key计数器清理间隔CLEANUP_INTERVAL,用于当取消的选择key达到256个时,重置取消选择key计数器cancelledKeys(int),并重新进行选择操作;选择器自动重构阈值SELECTOR_AUTO_REBUILD_THRESHOLD,默认选择操作发生512次,用于控制当选择器发生多少次选择操作时,重构选择器;选择器状态判断器selectNowSupplier,用于获取Nio事件循环内部选择器的选择操作结果;同时有一个选择器selector,未包装过的选择器unwrappedSelector和一个选择器提供者provider,一个选择key就绪集合selectedKeys(SelectedSelectionKeySet);当选择器的选择操作阻塞时,wakenUp(AtomicBoolean)属性决定是否应该break选择操作过程;一个Nio处理Io事件的时间占比ioRatio(int),默认为50,即IO事件处理时间和其他事件处理时间各占Nio事件循环一半;一个选择策略selectStrategy用于控制选择循环,如果返回结果为-1,则下一步应该阻塞选择操作,如果返回结果为-2,则下一步应该调回IO事件循环,处理IO事件,而不是继续执行选择操作,返回值大于0,表示需要有工作要做,即注册到选择器的选择通道有IO事件就绪。

回到Nio事件循环构造:
NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
             SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler) {
    super(parent, executor, false, DEFAULT_MAX_PENDING_TASKS, rejectedExecutionHandler);
    if (selectorProvider == null) {
        throw new NullPointerException("selectorProvider");
    }
    if (strategy == null) {
        throw new NullPointerException("selectStrategy");
    }
    provider = selectorProvider;
    //打开选择器
    final SelectorTuple selectorTuple = openSelector();
    selector = selectorTuple.selector;
    unwrappedSelector = selectorTuple.unwrappedSelector;
    selectStrategy = strategy;
}

先看一下选择器元组SelectorTuple的定义:
private static final class SelectorTuple {
    final Selector unwrappedSelector;
    final Selector selector;
    SelectorTuple(Selector unwrappedSelector) {
        this.unwrappedSelector = unwrappedSelector;
        this.selector = unwrappedSelector;
    }
    SelectorTuple(Selector unwrappedSelector, Selector selector) {
        this.unwrappedSelector = unwrappedSelector;
        this.selector = selector;
    }
}

选择器元组SelectorTuple实际为选择器和未包装选择器的包装类
再来看打开选择器:
//打开选择器
final SelectorTuple selectorTuple = openSelector();

private SelectorTuple openSelector() {
    final Selector unwrappedSelector;
    try {
        //从选择器提供者打开一个选择器,刚打开的选择器是未包装的选择器,裸选择器
        unwrappedSelector = provider.openSelector();
    } catch (IOException e) {
        throw new ChannelException("failed to open a new selector", e);
    }
    //如果key集合不优化,则选择器默认为选择器提供者打开的选择器
    if (DISABLE_KEYSET_OPTIMIZATION) {
        return new SelectorTuple(unwrappedSelector);
    }
    //下面是优化选择器集合
    final SelectedSelectionKeySet selectedKeySet = new SelectedSelectionKeySet();
    //在当前线程访问控制选择下,加载选择器实现类,不初始化
    Object maybeSelectorImplClass = AccessController.doPrivileged(new PrivilegedAction<Object>() {
        @Override
        public Object run() {
            try {
                return Class.forName(
                        "sun.nio.ch.SelectorImpl",
                        false,
                        PlatformDependent.getSystemClassLoader());
            } catch (Throwable cause) {
                return cause;
            }
        }
    });
    //如果从系统类加载器加载的选择key实现类不是Class实例,或不是裸选择器类型,不进行选择器key集合优化
    if (!(maybeSelectorImplClass instanceof Class) ||
            // ensure the current selector implementation is what we can instrument.
            !((Class<?>) maybeSelectorImplClass).isAssignableFrom(unwrappedSelector.getClass())) {
        if (maybeSelectorImplClass instanceof Throwable) {
            Throwable t = (Throwable) maybeSelectorImplClass;
            logger.trace("failed to instrument a special java.util.Set into: {}", unwrappedSelector, t);
        }
        return new SelectorTuple(unwrappedSelector);
    }

    final Class<?> selectorImplClass = (Class<?>) maybeSelectorImplClass;

    Object maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() {
        @Override
        public Object run() {
            try {
	        //在当前线程相同访问控制权限下,获取系统选择器实现类的
		//选择器就绪key集合selectedKeysField及其代理publicSelectedKeysField
                Field selectedKeysField = selectorImplClass.getDeclaredField("selectedKeys");
                Field publicSelectedKeysField = selectorImplClass.getDeclaredField("publicSelectedKeys");
                //设置选择器就绪key集合selectedKeysField及其代理publicSelectedKeysField访问控制权限
                Throwable cause = ReflectionUtil.trySetAccessible(selectedKeysField);
                if (cause != null) {
                    return cause;
                }
                cause = ReflectionUtil.trySetAccessible(publicSelectedKeysField);
                if (cause != null) {
                    return cause;
                }
                //将系统选择器的就绪key集合selectedKeysField及其代理publicSelectedKeysField
		//设置为selectedKeySet
                selectedKeysField.set(unwrappedSelector, selectedKeySet);
                publicSelectedKeysField.set(unwrappedSelector, selectedKeySet);
                return null;
            } catch (NoSuchFieldException e) {
                return e;
            } catch (IllegalAccessException e) {
                return e;
            }
        }
    });

    if (maybeException instanceof Exception) {
        selectedKeys = null;
        Exception e = (Exception) maybeException;
        logger.trace("failed to instrument a special java.util.Set into: {}", unwrappedSelector, e);
        return new SelectorTuple(unwrappedSelector);
    }
    //初始化选择key集合
    selectedKeys = selectedKeySet;
    logger.trace("instrumented a special java.util.Set into: {}", unwrappedSelector);
    return new SelectorTuple(unwrappedSelector,
                             new SelectedSelectionKeySetSelector(unwrappedSelector, selectedKeySet));
}

再来看选择器包装类
//SelectedSelectionKeySetSelector
package io.netty.channel.nio;

import java.io.IOException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.spi.SelectorProvider;
import java.util.Set;

final class SelectedSelectionKeySetSelector extends Selector {
    private final SelectedSelectionKeySet selectionKeys;//就绪的选择key集合
    private final Selector delegate;//选择器代理
    SelectedSelectionKeySetSelector(Selector delegate, SelectedSelectionKeySet selectionKeys) {
        this.delegate = delegate;
        this.selectionKeys = selectionKeys;
    }
    @Override
    public boolean isOpen() {
        return delegate.isOpen();
    }

    @Override
    public SelectorProvider provider() {
        return delegate.provider();
    }

    @Override
    public Set<SelectionKey> keys() {
        return delegate.keys();
    }

    @Override
    public Set<SelectionKey> selectedKeys() {
        return delegate.selectedKeys();
    }
    //每次选择操作,重置就绪选择key集合
    @Override
    public int selectNow() throws IOException {
        selectionKeys.reset();
        return delegate.selectNow();
    }

    @Override
    public int select(long timeout) throws IOException {
        selectionKeys.reset();
        return delegate.select(timeout);
    }

    @Override
    public int select() throws IOException {
        selectionKeys.reset();
        return delegate.select();
    }

    @Override
    public Selector wakeup() {
        return delegate.wakeup();
    }

    @Override
    public void close() throws IOException {
        delegate.close();
    }
}

选择器包装类的每次选择操作,首先重置就绪选择key集合,再讲相应的操作委托给内部选择器代理,其他打开唤醒等方法直接委托给内部选择器代理。

从上面可以看出,打开选择器,主要是委托给选择器提供者,如果需要优化选择器的,
在当前线程访问控制选择下,加载选择器实现类,不初始化,如果从系统类加载器加载的选择key实现类不是Class实例,或不是裸选择器类型,不进行选择器key集合优化,及选择器为选择器提供者打开的裸选择器;否则在当前线程相同访问控制权限下,获取系统选择器实现类的,选择器就绪key集合selectedKeysField及其代理publicSelectedKeysField,设置选择器就绪key集合selectedKeysField及其代理publicSelectedKeysField访问控制权限,将系统选择器的就绪key集合selectedKeysField及其代理publicSelectedKeysField设值为
selectedKeySet(SelectedSelectionKeySet),并将选择器selector包装为SelectedSelectionKeySetSelector。
     Nio事件循环初始化,主要是将Nio事件循环组和事件执行器及任务拒绝策略传给父类,同时打开一个选择器。

总结:

   Nio事件循环内部有一个取消选择key计数器清理间隔CLEANUP_INTERVAL,用于当取消的选择key达到256个时,重置取消选择key计数器cancelledKeys(int),并重新进行选择操作;选择器自动重构阈值SELECTOR_AUTO_REBUILD_THRESHOLD,默认选择操作发生512次,用于控制当选择器发生多少次选择操作时,重构选择器;选择器状态判断器selectNowSupplier,用于获取Nio事件循环内部选择器的选择操作结果;同时有一个选择器selector,未包装过的选择器unwrappedSelector和一个选择器提供者provider,一个选择key就绪集合selectedKeys(SelectedSelectionKeySet);当选择器的选择操作阻塞时,wakenUp(AtomicBoolean)属性决定是否应该break选择操作过程;一个Nio处理Io事件的时间占比ioRatio(int),默认为50,即IO事件处理时间和其他事件处理时间各占Nio事件循环一半;一个选择策略selectStrategy用于控制选择循环,如果返回结果为-1,则下一步应该阻塞选择操作,如果返回结果为-2,则下一步应该调回IO事件循环,处理IO事件,而不是继续执行选择操作,返回值大于0,表示需要有工作要做,即注册到选择器的选择通道有IO事件就绪。
    Nio事件循环初始化,主要是将Nio事件循环组和事件执行器及任务拒绝策略传给父类单线程事件循环(单线程事件执行器),同时打开一个选择器。
    打开选择器过程,委托给选择器提供者打开一个选择器,如果需要优化选择器的,在当前线程访问控制选择下,加载选择器实现类,不初始化,如果从系统类加载器加载的选择key实现类不是Class实例,或不是裸选择器类型,不进行选择器key集合优化,及选择器为选择器提供者打开的裸选择器;否则在当前线程相同访问控制权限下,获取系统选择器实现类的,选择器就绪key集合selectedKeysField及其代理publicSelectedKeysField,设置选择器就绪key集合selectedKeysField及其代理publicSelectedKeysField访问控制权限,将系统选择器的就绪key集合selectedKeysField及其代理publicSelectedKeysField设值为
selectedKeySet(SelectedSelectionKeySet),并将选择器selector包装为SelectedSelectionKeySetSelector。



附:
下面的选择key集合和反射工具,简单看看就行。
//SelectedSelectionKeySet
package io.netty.channel.nio;

import java.nio.channels.SelectionKey;
import java.util.AbstractSet;
import java.util.Arrays;
import java.util.Iterator;

final class SelectedSelectionKeySet extends AbstractSet<SelectionKey> {

    SelectionKey[] keys;
    int size;

    SelectedSelectionKeySet() {
        keys = new SelectionKey[1024];
    }

    @Override
    public boolean add(SelectionKey o) {
        if (o == null) {
            return false;
        }

        keys[size++] = o;
        if (size == keys.length) {
            increaseCapacity();
        }

        return true;
    }

    @Override
    public int size() {
        return size;
    }

    @Override
    public boolean remove(Object o) {
        return false;
    }

    @Override
    public boolean contains(Object o) {
        return false;
    }

    @Override
    public Iterator<SelectionKey> iterator() {
        throw new UnsupportedOperationException();
    }

    void reset() {
        reset(0);
    }

    void reset(int start) {
        Arrays.fill(keys, start, size, null);
        size = 0;
    }

    private void increaseCapacity() {
        SelectionKey[] newKeys = new SelectionKey[keys.length << 1];
        System.arraycopy(keys, 0, newKeys, 0, size);
        keys = newKeys;
    }
}



//ReflectionUtil
package io.netty.util.internal;

import java.lang.reflect.AccessibleObject;

public final class ReflectionUtil {

    private ReflectionUtil() { }

    /**
     * Try to call {@link AccessibleObject#setAccessible(boolean)} but will catch any {@link SecurityException} and
     * {@link java.lang.reflect.InaccessibleObjectException} and return it.
     * The caller must check if it returns {@code null} and if not handle the returned exception.
     */
    public static Throwable trySetAccessible(AccessibleObject object) {
        try {
            object.setAccessible(true);
            return null;
        } catch (SecurityException e) {
            return e;
        } catch (RuntimeException e) {
            return handleInaccessibleObjectException(e);
        }
    }

    private static RuntimeException handleInaccessibleObjectException(RuntimeException e) {
        // JDK 9 can throw an inaccessible object exception here; since Netty compiles
        // against JDK 7 and this exception was only added in JDK 9, we have to weakly
        // check the type
        if ("java.lang.reflect.InaccessibleObjectException".equals(e.getClass().getName())) {
            return e;
        }
        throw e;
    }
}