您当前的位置: 首页 > 

恐龙弟旺仔

暂无认证

  • 0浏览

    0关注

    282博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

Netty源码解析-EventLoop与EventLoopGroup初探

恐龙弟旺仔 发布时间:2021-12-14 19:18:39 ,浏览量:0

前言:

    在之前的ServerBootstrap示例中,有两句代码比较独特

NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
NioEventLoopGroup workerGroup = new NioEventLoopGroup(DEFAULT_EVENT_LOOP_THREADS);

通过其实现接口EventLoopGroup的注解我们了解到,        

/**
 * Special {@link EventExecutorGroup} which allows registering {@link Channel}s that get
 * processed for later selection during the event loop.
 *
 */
public interface EventLoopGroup extends EventExecutorGroup {}

EventLoopGroup主要提供给channel,供其注册使用。(之前我们都是将channel注册到selector上的),那么说明EventLoopGroup应该提供了注册方法和selector的相关实现。

later selection during the event loop,根据这句话,我们能够理解到真正让我们注册channel的应该是EventLoopGroup中的一个叫做EventLoop的东东。

通过注释,我们能够大概明白EventLoopGroup与EventLoop的关系,就是EventLoopGroup是一组EventLoop的集合,同时EventLoopGroup提供了选择方法来选择一个EventLoop供channel注册使用。

究竟是不是这样呢?我们继续看。

1.EventLoopGroup类结构图分析

 我们从上往下看。

Executor、ExecutorService接口属于JDK本身就有的接口,我们之前比较常用的其实现类就是 ThreadPoolExecutor,用于线程池的实现;

Iterable接口就不用说了,遍历的时候常用;

ScheduleExecutorService接口,类似于ExecutorService,只不过是一种提供定时任务执行的线程池;

以上都是JDK自带的接口。

真正Netty的接口是EventExecutorGroup和EventLoopGroup

能肉眼看到的与其他接口不同的方法就是以下两个:

// 获取下一个EventLoop
EventLoop next()
    
// 注册channel
ChannelFuture register(Channel channel)

就是在前言中提到的,作为EventLoop的集合,EventLoopGroup会提供一个选择方法用于获取一个EventLoop,目前看来就是next方法了;

同时提供一个针对channel的注册方法,可以将channel注册到对应的EventLoop上(理论上EventLoop应该有一个Selector的实现类)

总结:通过对其类结构图的分析,EventLoopGroup主要提供了以下三点功能:

1)类似于线程池的任务执行

2)根据一定的规则获取一个EventLoop

3)提供register方法,让Channel可以注册到EventLoopGroup上

2.EventLoop类结构图分析

 蛮有意思的一点是,EventLoop继承了EventLoopGroup接口,也就是上述我们所提到的EventLoopGroup的方法,在EventLoop中都有所实现;

EventLoop自己带有的接口也仅仅是EventExecutor和OrderedEventEexcutor,如下所示

 OrderedEventEexcutor只是一个标记接口;

EventExecutor中的方法基本都是自有方法;

所以那EventLoop基本与EventLoopGroup没啥区别了,都有next()方法,都可以执行提交任务,EventLoop相较EventLoopGroup多的方法就是EventExecutor中提供的方法了。

下面我们通过源码来比较下EventLoop与EventLoopGroup的关键性区别

我们来关注下NioEventLoopGroup和NioEventLoop这两个实现(当然,也有别的实现,我们在这里只关注NIO的实现)

3.NioEventLoop重点方法释义
public final class NioEventLoop extends SingleThreadEventLoop {
    private Selector selector;
    ...
    NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
                 SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler,
                 EventLoopTaskQueueFactory queueFactory) {
        super(parent, executor, false, newTaskQueue(queueFactory), newTaskQueue(queueFactory),
                rejectedExecutionHandler);
        this.provider = ObjectUtil.checkNotNull(selectorProvider, "selectorProvider");
        this.selectStrategy = ObjectUtil.checkNotNull(strategy, "selectStrategy");
        final SelectorTuple selectorTuple = openSelector();
        this.selector = selectorTuple.selector;
        this.unwrappedSelector = selectorTuple.unwrappedSelector;
    }
    
    // 注册方法
    public void register(final SelectableChannel ch, final int interestOps, final NioTask task) {
        ObjectUtil.checkNotNull(ch, "ch");
        if (interestOps == 0) {
            throw new IllegalArgumentException("interestOps must be non-zero.");
        }
        if ((interestOps & ~ch.validOps()) != 0) {
            throw new IllegalArgumentException(
                    "invalid interestOps: " + interestOps + "(validOps: " + ch.validOps() + ')');
        }
        ObjectUtil.checkNotNull(task, "task");

        if (isShutdown()) {
            throw new IllegalStateException("event loop shut down");
        }

        if (inEventLoop()) {
            register0(ch, interestOps, task);
        } else {
            ...
        }
    }
    
    // register0
    private void register0(SelectableChannel ch, int interestOps, NioTask task) {
        try {
            ch.register(unwrappedSelector, interestOps, task);
        } catch (Exception e) {
            throw new EventLoopException("failed to register a channel", e);
        }
    }
}

// next方法在其父类AbstractEventExecutor中实现
public abstract class AbstractEventExecutor extends AbstractExecutorService implements EventExecutor {
    ...
    @Override
    public EventExecutor next() {
        return this;
    }
}

从源码上看:

NioEventLoop.next()方法返回自身

NioEventLoop.register()方法,提供了一个注册方法注册到Selector上(从其构造方法上看,每一个NioEventLoop都有一个Selector的实现)

4.NioEventLoopGroup重点方法释义
// NioEventLoopGroup构造方法
public class NioEventLoopGroup extends MultithreadEventLoopGroup {
    public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider,
                             final SelectStrategyFactory selectStrategyFactory) {
        // 这里的super最终到达MultithreadEventExecutorGroup中
        super(nThreads, executor, selectorProvider, selectStrategyFactory, RejectedExecutionHandlers.reject());
    }
}

// MultithreadEventExecutorGroup构造方法
protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
                                            EventExecutorChooserFactory chooserFactory, Object... args) {
	...
    // 这里的children集合就是EventLoop的集合
    children = new EventExecutor[nThreads];

    for (int i = 0; i < nThreads; i ++) {
        boolean success = false;
        try {
            // 创建EventLoop对象,保存到数组中
            children[i] = newChild(executor, args);
            success = true;
        } catch (Exception e) {
            ...
        }
    }

    // next方法中的选择器
    chooser = chooserFactory.newChooser(children);
    ...

    for (EventExecutor e: children) {
        e.terminationFuture().addListener(terminationListener);
    }
    ...
}


// next方法的实现在其父类MultithreadEventExecutorGroup中实现
public abstract class MultithreadEventExecutorGroup extends AbstractEventExecutorGroup {
    @Override
    public EventExecutor next() {
        return chooser.next();
    }
}

// 注册方法在其父类MultithreadEventLoopGroup
public abstract class MultithreadEventLoopGroup extends MultithreadEventExecutorGroup implements EventLoopGroup {
    ...
    @Override
    public ChannelFuture register(Channel channel) {
        // 通过next方法选择一个EventLoop之后,在调用EventLoop.register方法,将channel注册上
        return next().register(channel);
    }
    ...
}

// 选择器类工厂DefaultEventExecutorChooserFactory
public final class DefaultEventExecutorChooserFactory implements EventExecutorChooserFactory {
    @Override
    public EventExecutorChooser newChooser(EventExecutor[] executors) {
        // 默认提供了两种选择器
        if (isPowerOfTwo(executors.length)) {
            return new PowerOfTwoEventExecutorChooser(executors);
        } else {
            return new GenericEventExecutorChooser(executors);
        }
    }
}

总结:

NioEventLoopGroup的构造方法,用于创建指定数量的EventLoop,存放到数组中;

根据EventLoop的数量,并创建一个选择器;

next方法,通过创建的选择器Chooser,来选择一个EventLoop使用;

register方法,通过选择出来的EventLoop,调用其注册方法将channel注册其上(也就是将Channel注册到EventLoop的selector上);

总结:

以上就是笔者在学习Netty中关于EventLoop和EventLoopGroup时的心得。

Netty框架实在太大了,代码量居多。初学者很容易陷入无尽的代码细节中,然后就放弃了。

针对框架而言,应该先观察其大概结构,不要陷入细节(尤其初学时),当对它的结构有一个大致了解时,再去深入其细节。

所以,笔者在本文中,也只是介绍了其最重要的几个方法,比如如何将Channel注册到EventLoop上,如何从EventLoopGroup中选择一个合适的EventLoop,这才是我们的学习重点。

对于其他方法,比如线程池的方法执行、Chooser的实现等,建议读者自行阅读,笔者不再详述。

后续,我们对EventLoop的一些关键运行方法再继续介绍。

关注
打赏
1655041699
查看更多评论
立即登录/注册

微信扫码登录

0.0349s