手撕netty源码(一)- NioEventLoopGroup

文章目录

  • 前言
  • 一、NIO 与 netty
  • 二、NioEventLoopGroup 对象的创建过程
    • 2.1 创建流程图


前言

本文是手撕netty源码系列的开篇文章,会先介绍一下netty对NIO关键代码的封装位置,主要介绍 NioEventLoopGroup 对象的创建过程,看看new一个对象可以做哪些事情。


一、NIO 与 netty

平时使用NIO的主要步骤:

/*创建选择器的实例*/
Selector selector = Selector.open();
/*创建ServerSocketChannel的实例*/
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

/*设置通道为非阻塞模式*/
serverSocketChannel.configureBlocking(false);
/*绑定端口*/
serverSocketChannel.socket().bind(new InetSocketAddress(port));
/*注册事件,表示关心客户端连接*/
serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);
while(true){
	/*获取当前有哪些事件*/
    selector.select(1000);
    /*获取事件的集合*/
    Set<SelectionKey> selectionKeys = selector.selectedKeys();
    Iterator<SelectionKey> iterator = selectionKeys.iterator();
    while(iterator.hasNext()){
         SelectionKey key = iterator.next();
         /*我们必须首先将处理过的 SelectionKey 从选定的键集合中删除。
         如果我们没有删除处理过的键,那么它仍然会在主集合中以一个激活
         的键出现,这会导致我们尝试再次处理它。*/
         iterator.remove();
         handleInput(key);
    }
}

/*处理事件的发生*/
private void handleInput(SelectionKey key) throws IOException {
     if(key.isValid()){
         /*处理新接入的客户端的请求*/
         if(key.isAcceptable()){
             /*获取关心当前事件的Channel*/
             ServerSocketChannel ssc
                     = (ServerSocketChannel) key.channel();
             /*接受连接*/
             SocketChannel sc = ssc.accept();
             System.out.println("==========建立连接=========");
             sc.configureBlocking(false);
             /*关注读事件*/
             sc.register(selector,SelectionKey.OP_READ);
         }
         /*处理对端的发送的数据*/
         if(key.isReadable()){
             SocketChannel sc = (SocketChannel) key.channel();
             /*创建ByteBuffer,开辟一个缓冲区*/
             ByteBuffer buffer = ByteBuffer.allocate(1024);
             /*从通道里读取数据,然后写入buffer*/
             int readBytes = sc.read(buffer);
             if(readBytes>0){
                 /*将缓冲区当前的limit设置为position,position=0,
                 用于后续对缓冲区的读取操作*/
                 buffer.flip();
                 /*根据缓冲区可读字节数创建字节数组*/
                 byte[] bytes = new byte[buffer.remaining()];
                 /*将缓冲区可读字节数组复制到新建的数组中*/
                 buffer.get(bytes);
                 String message = new String(bytes,"UTF-8");
                 System.out.println("服务器收到消息:"+message);
                 /*处理数据*/
                 String result = Const.response(message);
                 、、、、、

             }else if(readBytes<0){
                 /*取消特定的注册关系*/
                 key.cancel();
                 /*关闭通道*/
                 sc.close();
             }
         }
         、、、、
     }
 }

平时使用netty的主要步骤:

// 配置服务端的NIO线程组
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
	.channel(NioServerSocketChannel.class)
    .option(ChannelOption.SO_BACKLOG, 1024)
    .childHandler(new ServerInit());

// 绑定端口,同步等待成功
b.bind(NettyConstant.SERVER_PORT).sync();

那么,netty 对 NIO 的封装具体体现在哪里呢?先揭晓答案,后续一点点细嚼慢咽

  1. 创建选择器的实例
    io/netty/channel/nio/NioEventLoop.java
NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
             SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler,
             EventLoopTaskQueueFactory taskQueueFactory, EventLoopTaskQueueFactory tailTaskQueueFactory) {
    super(parent, executor, false, newTaskQueue(taskQueueFactory), newTaskQueue(tailTaskQueueFactory),
            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;
}
  1. 创建ServerSocketChannel的实例
    io/netty/channel/socket/nio/NioServerSocketChannel.java
private static ServerSocketChannel newChannel(SelectorProvider provider, InternetProtocolFamily family) {
    try {
        ServerSocketChannel channel =
                SelectorProviderUtil.newChannel(OPEN_SERVER_SOCKET_CHANNEL_WITH_FAMILY, provider, family);
        return channel == null ? provider.openServerSocketChannel() : channel;
    } catch (IOException e) {
        throw new ChannelException("Failed to open a socket.", e);
    }
}
  1. 设置通道为非阻塞模式
    io/netty/channel/nio/AbstractNioChannel.java
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
        super(parent);
        this.ch = ch;
        this.readInterestOp = readInterestOp;
        try {
            ch.configureBlocking(false);
        } catch (IOException e) {
            try {
                ch.close();
            } catch (IOException e2) {
                logger.warn(
                            "Failed to close a partially initialized socket.", e2);
            }

            throw new ChannelException("Failed to enter non-blocking mode.", e);
        }
    }
  1. 绑定端口
    io/netty/bootstrap/AbstractBootstrap.java
private static void doBind0(
            final ChannelFuture regFuture, final Channel channel,
            final SocketAddress localAddress, final ChannelPromise promise) {

        // This method is invoked before channelRegistered() is triggered.  Give user handlers a chance to set up
        // the pipeline in its channelRegistered() implementation.
        channel.eventLoop().execute(new Runnable() {
            @Override
            public void run() {
                if (regFuture.isSuccess()) {
                    channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
                } else {
                    promise.setFailure(regFuture.cause());
                }
            }
        });
    }
  1. 注册事件,表示关心客户端连接
    io/netty/channel/nio/AbstractNioChannel.java
protected void doRegister() throws Exception {
        boolean selected = false;
        for (;;) {
            try {
                selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
                return;
            } catch (CancelledKeyException e) {
                if (!selected) {
                    // Force the Selector to select now as the "canceled" SelectionKey may still be
                    // cached and not removed because no Select.select(..) operation was called yet.
                    eventLoop().selectNow();
                    selected = true;
                } else {
                    // We forced a select operation on the selector before but the SelectionKey is still cached
                    // for whatever reason. JDK bug ?
                    throw e;
                }
            }
        }
    }
  1. 获取当前事件的集合
  2. 处理事件
    io/netty/channel/nio/NioEventLoop.java
private int select(long deadlineNanos) throws IOException {
   if (deadlineNanos == NONE) {
        return selector.select();
    }
    // Timeout will only be 0 if deadline is within 5 microsecs
    long timeoutMillis = deadlineToDelayNanos(deadlineNanos + 995000L) / 1000000L;
    return timeoutMillis <= 0 ? selector.selectNow() : selector.select(timeoutMillis);
}

private void processSelectedKeys() {
    if (selectedKeys != null) {
        processSelectedKeysOptimized();
    } else {
        processSelectedKeysPlain(selector.selectedKeys());
    }
}

其实,netty 也不难对吧
学习netty,主要学习它的设计思想和对性能优化的巧妙处理,当工作需要时,能够灵活运用

二、NioEventLoopGroup 对象的创建过程

2.1 创建流程图

在这里插入图片描述
可以看到,其实我们传的线程数量实际控制的是NioEventLoop对象创建的数量,而每个 NioEventLoop 其实是一个Executor执行器,那么至此,我们只是相当于创建了两个 NioEventLoopGroup 对象,他们分别有自己的children执行器 NioEventLoop 数组,同一个数组内的 NioEventLoop 共享一个ThreadPerTaskExecutor执行器,但是现在这个执行器后续如何处理事件和如何调度还不知道,后续会讲到,本文先看看创建NioEventLoopGroup对象都做了什么
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/568118.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

使用mapinfo软件的在线地图插件运行错误解决

使用mapinfo软件的在线地图插件运行错误解决 一、如何解决win10/win11家庭版运行MapInfo中的在线地图插件报错【unexpected error&#xff1b;quitting】问题&#xff1f;二、如何解决在线地图切换地图源时的报错问题&#xff1f; 一、如何解决win10/win11家庭版运行MapInfo中的…

Linux中进程和计划任务管理(2)

一.进程命令 1.lsof lsof 命令&#xff0c;“list opened files”的缩写&#xff0c;直译过来&#xff0c;就是列举系统中已经被打开的文件。通过 lsof 命令&#xff0c;我们就可以根据文件找到对应的进程信息&#xff0c;也可以根据进程信息找到进程打开的文件。 格式&…

Jackson 2.x 系列【31】Spring Boot 集成之字典翻译

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Jackson 版本 2.17.0 本系列Spring Boot 版本 3.2.4 源码地址&#xff1a;https://gitee.com/pearl-organization/study-jaskson-demo 文章目录 1. 场景描述2. 案例演示2.1 修改枚举2.2 定义注解…

【python】Python学生信息管理系统(源码+报告+本地存储)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

游戏黑灰产识别和溯源取证

参考&#xff1a;游戏黑灰产识别和溯源取证 1. 游戏中的黑灰产 1. 黑灰产简介 黑色产业&#xff1a;从事具有违法性活动且以此来牟取利润的产业&#xff1b; 灰色产业&#xff1a;不明显触犯法律和违背道德&#xff0c;游走于法律和道德边缘&#xff0c;以打擦边球的方式为“…

电磁仿真--基本操作-CST-(2)

目录 1. 回顾基操 2. 操作流程 2.1 创建工程 2.2 修改单位 2.3 创建 Shape 2.4 使用拉伸 Extrude 2.5 修改形状 Modify Locally 2.6 导入材料 2.7 材料解释 2.8 材料分配 2.9 查看已分配的材料 2.10 设置频率、背景和边界 2.11 选择 Edge&#xff0c;设置端口 2.…

深度解读半导体测试解决方案,4月25日精彩直播即将来袭!

半导体测试需求日益复杂、量产测试要求不断提升&#xff0c;行业工程师应该如何应对上述难题&#xff0c;提升测试的质量与效率&#xff0c;真正做到紧跟技术前沿&#xff0c;掌握创新应用&#xff0c;有效优化测试过程并降低测试成本&#xff1f; 针对上述痛点&#xff0c;加速…

Linux进阶篇:Centos7安装与配置mysql(rpm安装方式)

Linux服务搭建篇&#xff1a;Centos7安装与配置mysql&#xff08;rpm安装方式&#xff09; MySQL是一个开源的关系型数据库管理系统&#xff0c;由瑞典MySQL AB公司开发&#xff0c;现在属于Oracle公司。MySQL是最流行的关系型数据库管理系统之一&#xff0c;在WEB应用方面&am…

232 基于matlab的MIMO雷达模型下一种子空间谱估计方法

基于matlab的MIMO雷达模型下一种子空间谱估计方法&#xff0c;采用过估计的方法&#xff0c;避免了信源数估计的问题&#xff0c;对数据协方差矩阵进行变换&#xff0c;构造信号子空间投影矩阵和噪声子空间投影矩阵&#xff0c;不需要像经典的MUSIC一样对其进行特征分解&#x…

【网络编程】Java网络编程中的基本概念及实现UDP、TCP客户端服务器程序(万字博文)

系列文章目录 【网络通信基础】网络中的常见基本概念 【网络编程】Java网络编程中的基本概念及实现UDP、TCP客户端服务器程序&#xff08;万字博文&#xff09; 【网络原理】UDP协议的报文结构 及 校验和字段的错误检测机制&#xff08;CRC算法、MD5算法&#xff09; 目录 …

SpringCloud系列(12)--服务提供者(Service Provider)集群搭建

前言&#xff1a;在上一章节中我们成功把微服务注册进了Eureka集群&#xff0c;但这还不够&#xff0c;虽然注册服务中心Eureka已经是服务配置了&#xff0c;但服务提供者目前只有一个&#xff0c;如果服务提供者宕机了或者流量过大&#xff0c;都会影响到用户即服务使用者的使…

线上相亲竟然有三大好处,你认同吗?

当今社会高速的经济增长和急剧的社会变迁&#xff0c;使人们的生活水平极大提高&#xff0c;社会生活环境发生重大变化&#xff0c;人们的态度与观念也随之改变。这导致代际现象日益突出。以“80后”和“90后”为代表的青年群体成为社会新事物和新潮流的代言者&#xff0c;也自…

Jmeter04:关联

1 Jmeter组件&#xff1a;关联 概括&#xff1a;2个请求之间不是独立的&#xff0c;一个请求响应的结果是作为另一个请求提交的数据&#xff0c;存在数据交互 1.1 是什么&#xff1f; 就是一个请求的结果是另一个请求提交的数据&#xff0c;二者不再是独立 1.2 为什么&#x…

Nacos原理简单介绍

注册中心原理 官网&#xff1a;Nacos 注册中心的设计原理 | Nacos nacos注册中心采用了 &#xff1a;pull &#xff08;客户端的轮询&#xff09;和push &#xff08;服务端主动push&#xff09;策略 客户端启动时会将当前服务的信息包含ip、端口号、服务名、集群名等信息封装…

Valentina Studio Pro 13.10激活 专业的数据库管理软件

Valentina Studio Pro 支持连接所有主要数据库&#xff0c;可以帮助用户轻松创建、管理、查询和浏览Valentina DB、MySQL、MariaDB、PostgreSQL和SQLite数据库。 Valentina Studio Pro for Mac 13.10激活版下载 Valentina Studio Pro 是一款可以在 Mac 电脑上使用的数据库管理工…

水牛社靠谱吗,水牛社可以当做副业来做吗?

水牛社这个平台是否靠谱&#xff0c;能否作为副业的选择&#xff0c;一直是网友们热议的话题。实际上&#xff0c;水牛社是一个集合了众多网上赚钱活动任务和提供资源项目教程的综合性平台&#xff0c;它并非只局限于某一特定的项目&#xff0c;而是展现出多样化的特点。随着网…

Acrobat Pro DC 2021:强大的PDF编辑软件

Acrobat Pro DC 2021是Adobe公司推出的一款全面而强大的PDF编辑软件&#xff0c;凭借其卓越的性能和丰富的功能&#xff0c;成为了PDF编辑领域的领军者。 Acrobat Pro DC 2021中文激活版下载 这款软件具备全面的PDF编辑功能&#xff0c;包括文本编辑、图片处理、页面组织等&…

计算机组成原理—数据的表示和运算

二进制&#xff1a;2进1 八进制里的1位对应二进制里的三位 重点&#xff1a;转换为10进制

基于表面法线法的二维人脸图构建三维人脸模型matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 ............................................................................for j 1 : …

QA测试开发工程师面试题满分问答21: 单元测试、集成测试、系统测试的侧重点是什么?

单元测试、集成测试和系统测试是软件测试中的不同层次和阶段&#xff0c;每个阶段侧重于不同的测试目标和范围。以下是它们的侧重点的简要说明&#xff1a; 单元测试&#xff1a; 单元测试是针对软件中最小的可测试单元&#xff08;通常是函数、方法或模块&#xff09;进行的测…