Netty入门(三):EventLoop
前言
Netty系列索引:
IO相关:

上文提到,早期基于线程的网络模型,处理高并发的能力非常差,随着请求数量的增多,必须不断新建线程,随之带来的问题就是服务器资源被占满、上下文切换成本过高以及IO阻塞导致的CPU浪费。
而Netty则使用了经典Reactor模型,并重新进行了封装,包括EventLoop、EventGroup等。
EventLoopGroup
EventLoopGroup是一个接口,继承自线程池EventExecutorGroup,并允许注册channel到自身所持有的EventLoop,同时支持按一定规则获取下一个EventLoop。
EventLoopGroup的具体实现有很多,下面以DefaultEventLoopGroup为例,简述一下我的理解

1.ScheduledExecutorService
JDK接口,一个延迟或定时任务的执行器,其实现类ScheduledThreadPoolExecutor主要是利用了延时队列及设置下次执行时间来实现的,这里不再赘述(可以单独开个专题0.0)
2.EventExecutorGroup
接口,Netty自定义的一个线程池,负责复用EventExecutor和执行任务

3.EventLoopGroup
核心接口,EventLoopGroup继承自EventExecutorGroup,代表他是一个线程池。同时他具备将channel注册到EventExecutorGroup的功能,代表他是一个能够真正处理Channel的特殊线程池

4.MultithreadEventExecutorGroup(AbstractEventExecutorGroup)
抽象类,实现自EventExecutorGroup接口,提供了一个简易线程池的实现,其只有一个抽象方法newChild(创建EventExecutor)供子类实现
protected MultithreadEventExecutorGroup(int nThreads, Executor executor, EventExecutorChooserFactory chooserFactory, Object... args)4.1 nThreads
    private final EventExecutor[] children;
该线程池通过数组存储线程,入参nThreads指定数组大小,并循环调用newChild创建线程。当创建过程中有异常时,会自动调用已创建完成线程的shutdownGracefully方法,进行优雅关闭
        for (int i = 0; i < nThreads; i ++) {
            boolean success = false;
            try {
                children[i] = newChild(executor, args);
                success = true;
            } catch (Exception e) {
                // TODO: Think about if this is a good exception type
                throw new IllegalStateException("failed to create a child event loop", e);
            } finally {
                if (!success) {
                    for (int j = 0; j < i; j ++) {
                        children[j].shutdownGracefully();
                    }
                    for (int j = 0; j < i; j ++) {
                        EventExecutor e = children[j];
                        try {
                            while (!e.isTerminated()) {
                                e.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);
                            }
                        } catch (InterruptedException interrupted) {
                            // Let the caller handle the interruption.
                            Thread.currentThread().interrupt();
                            break;
                        }
                    }
                }
            }
        }4.2 EventExecutorChooserFactory
EventExecutorChooserFactory是一个工厂接口,负责创建EventExecutorChooser
其默认实现DefaultEventExecutorChooserFactory会判断当前线程数是否2的n次幂,如果是则返回PowerOfTwoEventExecutorChooser,否则返回GenericEventExecutorChooser
4.3 EventExecutorChooser
    private final EventExecutorChooserFactory.EventExecutorChooser chooser;
EventExecutorChooser负责根据一定规则从线程池children数组中取得下一个线程
PowerOfTwoEventExecutorChooser:通过&运算,将超出executors.length的位置为0
        public EventExecutor next() {
            return executors[idx.getAndIncrement() & executors.length - 1];
        }GenericEventExecutorChooser:通过求余运算,获取有效index
        public EventExecutor next() {
            return executors[Math.abs(idx.getAndIncrement() % executors.length)];
        }可以看出当线程数是2的n次幂时,Netty通过与运算优化了效率
5.MultithreadEventLoopGroup
抽象类,继承自MultithreadEventExecutorGroup并实现了EventLoopGroup接口,代表此抽象类是一个可以注册并处理channel的线程池

值得关注的是next方法,他把返回值的类型,进一步限定为EventLoop
    public EventLoop next() {
        return (EventLoop) super.next();
    }6.DefaultEventLoopGroup
MultithreadEventLoopGroup的一个默认实现
其核心就是实现了newChild方法返回一个EventLoop extends EventExecutor实例
    protected EventLoop newChild(Executor executor, Object... args) throws Exception {
        return new DefaultEventLoop(this, executor);
    }7.总结
说白了EventLoopGroup核心方法,register负责将channel与线程池中某一线程绑定,next负责返回下一个线程供调用方执行任务

EventLoop
EventLoop直译为事件循环,他的职责简单来说就是绑定一个唯一的线程,去执行或调度被分配的任务。
可见一个EventLoop实例可以为多个channel服务,而为了最大化利用资源,Netty使用池化技术将EventLoop放入EventLoopGroup中管理。
EventLoop的具体实现有很多,先看下DefaultEventLoop的类图,会发现他和DefaultEventLoopGroup的类图很像,都继承了EventLoopGroup接口,但其最大的不同是红框所示,他还继承了EventExecutor,下面主要讲一下多出来的这部分到底是干了什么

1.EventExecutor
接口,定义了一个事件执行器,主要方法如下
    /**
     * 直接返回自身
     */
    @Override
    EventExecutor next();
    /**
     * 返回所属线程池
     */
    EventExecutorGroup parent();
    /**
     * 判断当前线程是否是当前EventLoop绑定的线程
     */
    boolean inEventLoop();
    /**
     * 判断传入线程是否是当前EventLoop绑定的线程
     */
    boolean inEventLoop(Thread thread);(还涉及一些Future异步编程的一些东西,太复杂了后续再填坑吧0.0)
2.AbstractScheduledEventExecutor (AbstractEventExecutor)
抽象类,简单定义了一个支持延迟(定时)任务的执行器
    //延时队列
    PriorityQueue<ScheduledFutureTask<?>> scheduledTaskQueue;
	//下一个任务id
    long nextTaskId;重要方法scheduled
    private <V> ScheduledFuture<V> schedule(final ScheduledFutureTask<V> task) {
        if (inEventLoop()) {
        	//如果和执行器绑定的线程一致,直接放入延时队列中
            scheduleFromEventLoop(task);
        } else {
        	//获取任务最晚执行时间
            final long deadlineNanos = task.deadlineNanos();
            // task will add itself to scheduled task queue when run if not expired
            if (beforeScheduledTaskSubmitted(deadlineNanos)) {
            	//放入线程池执行
                execute(task);
            } else {
            	//与execute类似,但不保证任务会在执行非延迟任务或执行程序关闭之前运行,默认实现只是委托给execute(Runnable)
                lazyExecute(task);
                // Second hook after scheduling to facilitate race-avoidance
                if (afterScheduledTaskSubmitted(deadlineNanos)) {
                    execute(WAKEUP_TASK);
                }
            }
        }
        return task;
    }3.SingleThreadEventExecutor
抽象类,定义了一个单线程顺序执行器
    private void execute(Runnable task, boolean immediate) {
        boolean inEventLoop = inEventLoop();
        //添加到任务队列
        addTask(task);
        if (!inEventLoop) {
        	//启动线程
            startThread();
            //如果线程池已经关闭,调用拒绝方法
            if (isShutdown()) {
                boolean reject = false;
                try {
                    if (removeTask(task)) {
                        reject = true;
                    }
                } catch (UnsupportedOperationException e) {
                    // The task queue does not support removal so the best thing we can do is to just move on and
                    // hope we will be able to pick-up the task before its completely terminated.
                    // In worst case we will log on termination.
                }
                if (reject) {
                    reject();
                }
            }
        }
		//唤醒线程
        if (!addTaskWakesUp && immediate) {
            wakeup(inEventLoop);
        }
    }4.SingleThreadEventLoop
public abstract class SingleThreadEventLoop extends SingleThreadEventExecutor implements EventLoop EventLoop的抽象基类,负责在单线程中执行所有被提交的任务,同时具有注册和处理channle的能力
5.DefaultEventLoop
单线程任务执行器的默认实现,主要就是其实现的run方法
    protected void run() {
        //循环阻塞的获取任务,知道被通知关闭
        for (;;) {
            Runnable task = takeTask();
            if (task != null) {
                task.run();
                updateLastExecutionTime();
            }
            if (confirmShutdown()) {
                break;
            }
        }
    }6.总结
通过以上分析,不难看出Netty首先定义了自己的线程池(EventExectorGroup)和执行器(EventExector),然后通过继承的方式定义了线程池(EventLoopGroup)和执行器(EventLoop),从而添加了处理(注册)channel的能力。
You gotta grab what you can when you can.
机不可失,时不我待。
Netty入门(三):EventLoop的更多相关文章
- Netty入门(三)之web服务器
		Netty入门(三)之web服务器 阅读前请参考 Netty入门(一)之webSocket聊天室 Netty入门(二)之PC聊天室 有了前两篇的使用基础,学习本文也很简单!只需要在前两文的基础上稍微改 ... 
- Netty 系列(三)Netty 入门
		Netty 系列(三)Netty 入门 Netty 是一个提供异步事件驱动的网络应用框架,用以快速开发高性能.高可靠性的网络服务器和客户端程序.更多请参考:Netty Github 和 Netty中文 ... 
- netty入门(一)
		1. netty入门(一) 1.1. 传统socket编程 在任何时候都可能有大量的线程处于休眠状态,只是等待输入或者输出数据就绪,这可能算是一种资源浪费. 需要为每个线程的调用栈都分配内存,其默认值 ... 
- 【原创】NIO框架入门(三):iOS与MINA2、Netty4的跨平台UDP双向通信实战
		前言 本文将演示一个iOS客户端程序,通过UDP协议与两个典型的NIO框架服务端,实现跨平台双向通信的完整Demo.服务端将分别用MINA2和Netty4进行实现,而通信时服务端你只需选其一就行了.同 ... 
- Netty入门之客户端与服务端通信(二)
		Netty入门之客户端与服务端通信(二) 一.简介 在上一篇博文中笔者写了关于Netty入门级的Hello World程序.书接上回,本博文是关于客户端与服务端的通信,感觉也没什么好说的了,直接上代码 ... 
- Netty入门之HelloWorld
		Netty系列入门之HelloWorld(一) 一. 简介 Netty is a NIO client server framework which enables quick and easy de ... 
- Netty入门
		一.NIO Netty框架底层是对NIO的高度封装,所以想要更好的学习Netty之前,应先了解下什么是NIO - NIO是non-blocking的简称,在jdk1.4 里提供的新api,他的他的特性 ... 
- 脑残式网络编程入门(三):HTTP协议必知必会的一些知识
		本文原作者:“竹千代”,原文由“玉刚说”写作平台提供写作赞助,原文版权归“玉刚说”微信公众号所有,即时通讯网收录时有改动. 1.前言 无论是即时通讯应用还是传统的信息系统,Http协议都是我们最常打交 ... 
- Netty入门(一)之webSocket聊天室
		一:简介 Netty 是一个提供 asynchronous event-driven (异步事件驱动)的网络应用框架,是一个用以快速开发高性能.高可靠性协议的服务器和客户端. 换句话说,Netty 是 ... 
随机推荐
- SpringCloud Alibaba实战(10:分布式配置中心)
			源码地址:https://gitee.com/fighter3/eshop-project.git 持续更新中-- 在我们前面介绍Nacos的时候,说到,Nacos除了可以作为注册中心,还可以作为配置 ... 
- kubernetes ceph-csi分析
			概述 最近在做分布式存储ceph接入kubernetes,用的是csi这一套,在开发的过程中,自己也用有道云笔记做过一些ceph-csi相关的源码分析.知识总结之类的记录,刚好自己又萌生了发博的想法, ... 
- 精尽Spring Boot源码分析 - 剖析 @SpringBootApplication 注解
			该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring Boot 源码分析 GitHub 地址 进行阅读 Sprin ... 
- 9.5、zabbix高级操作(2)
			4.zabbix的分布式监控: 使用zabbix-proxy主动方式(被动也可),使用zabbix-agent的主动方式(被动也可): Zabbix Server <- Zabbix Proxy ... 
- ROS2学习之旅(1)——初识ROS2
			本系列用来记录ROS2的学习过程,有错误或者不合理的地方请大家指正.由于博主具有ROS1的学习经历,会添加一些与ROS1的一些对比,当然这对于ROS2本身的学习内容没有丝毫影响,欢迎大家积极与我在评论 ... 
- Mybatis学习(3)实现数据的增删改查
			前面已经讲到用接口的方式编程.这种方式,要注意的一个地方就是.在User.xml 的配置文件中,mapper namespace="com.yihaomen.mybatis.inter.I ... 
- 南京大学计算机基础 ELF和可执行文件格式
			1.可重定位目标文件格式 主要是由ELF头,一些节比如.text节,.rodata节,.data节,.bss节等,前面是只读的,后面是可读可写的,加上一个节头表 1.1 ELF头里面主要包含了16字节 ... 
- DawgCTF wp(re和crypto)
			简单写写思路,想看详解的..我脚本有些丢失了..师傅请移步. 挂了个vpn,算正式打这种国际赛,全是英文.上去打了两天,昨晚晚上划水了一晚上补作业...,re那时候写出来三道,Potentially ... 
- C语言:输出数字各个位的数字及和
			#include <stdio.h> int main() { char sh[13][5]={"个","十","百",&quo ... 
- ADB打开快手APP
			aa="adb -s {0} shell am start -n com.kuaishou.nebula/com.yxcorp.gifshow.HomeActivity".form ... 
