当前位置: 首页 > news >正文

Netty笔记

Netty的介绍

Netty 是由 JBOSS 提供的一个 Java 开源框架,基于NIO事件驱动的网络应用框架

文章目录

  • Netty的介绍
  • 前言
  • 一、Java BIO
  • 二、Java NIO
  • 三、Netty
    • 1、引入Netty
    • 2、Reactor 模式
    • 3、Netty模型
  • 四、Netty 核心源码剖析
  • 五、用Netty 自己 实现 dubbo RPC


前言

  1. I/O 模型简单的理解:就是用什么样的通道进行数据的发送和接收,很大程度上决定了程
    序通信的性能
  2. Java共支持3种网络编程模型/IO模式:BIO(传统阻塞型)、NIO非阻塞、AIO异步非阻塞

一、Java BIO

BIO(blocking I/O) : 同步阻塞,服务器实现模式为一个连接一个线程,即客户端有连
接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造
成不必要的线程开销
在这里插入图片描述

二、Java NIO

  1. Java NIO 全称 java non-blocking IO,是指 JDK 提供的新API。
  2. NIO 相关类都被放在 java.nio 包及子包下,并且对原 java.io包中的很多类进行改写。
  3. NIO 有三大核心部分:Channel(通道),Buffer(缓冲区),Selector(选择器)
  4. NIO是 面向缓冲区 ,或者面向 块 编程的
  5. Java NIO的非阻塞模式,使一个线程从某通道发送请求或者读取数据,但是它仅能得
    到目前可用的数据,如果目前没有数据可用时,就什么都不会获取,而不是保持线程阻塞
  6. 通俗理解:NIO是可以做到用一个线程来处理多个Channel的。
    在这里插入图片描述
    在这里插入图片描述

三、Netty

1、引入Netty

  1. 原生NIO存在的问题
    1. NIO 的类库和 API 繁杂,使用麻烦。
    2. 需要具备其他的额外技能。
    3. 开发工作量和难度都非常大。
    4. JDK NIO 的 Bug
  2. Netty 对 JDK 自带的 NIO 的 API 进行了封装,解决了上述问题
  3. Netty5出现重大bug,已经被官网废弃了,目前推荐使用的是Netty4.x的稳定版

2、Reactor 模式

  1. 单 Reactor 单线程 在这里插入图片描述

  2. 单 Reactor 多线程 在这里插入图片描述

  3. 主从 Reactor 多线程在这里插入图片描述

3、Netty模型

在这里插入图片描述
核心组件说明

  1. Netty 抽象出两组线程池,BossGroup 专门负责接收客户端连接,WorkerGroup 专门负
    责网络读写操作。
  2. NioEventLoopGroup 下包含多个 NioEventLoop
  3. NioEventLoop 表示一个不断循环执行处理任务的线程,每个 NioEventLoop 都有一个
    selector,用于监听绑定在其上的 socket 网络通道。采用串行化设计,从消息的读取->解码->处理->编码->发送。
    • 每个 NioEventLoop 中包含有一个 Selector,一个 taskQueue
    • 每个 NioEventLoop 的 Selector 上可以注册监听多个 NioChannel
    • 每个 NioChannel 只会绑定在唯一的 NioEventLoop 上
    • 每个 NioChannel 都绑定有一个自己的 ChannelPipeline
    在这里插入图片描述

• 一个 Channel 包含了一个 ChannelPipeline,而 ChannelPipeline 中又维护了一个由 ChannelHandlerContext
组成的双向链表,并且每个 ChannelHandlerContext 中又关联着一个 ChannelHandler
• 入站事件和出站事件在一个双向链表中,入站事件会从链表 head 往后传递到最后一个入站的 handler,
出站事件会从链表 tail 往前传递到最前一个出站的 handler,两种类型的 handler 互不干扰

异步模型

  1. Netty 中的 I/O 操作是异步的,包括 Bind、Write、Connect 等操作会简单的返回一个
    ChannelFuture。
  2. 调用者并不能立刻获得结果,而是通过 Future-Listener 机制,用户可以方便的主动获
    取或者通过通知机制获得 IO 操作结果。Netty 的异步模型是建立在 future 和 callback 的之上的。

Netty的handler链的调用机制
在这里插入图片描述

四、Netty 核心源码剖析

  1. Netty 启动过程源码剖析

    1. 创建2个 EventLoopGroup 线程池数组。数组默认大小CPU*2,方便chooser选择
      线程池时提高性能
    2. BootStrap 将 boss 设置为 group属性,将 worker 设置为 childer 属性
    3. 通过 bind 方法启动,内部重要方法为 initAndRegister 和 dobind 方法
    4. initAndRegister 方法会反射创建 NioServerSocketChannel 及其相关的 NIO 的对象,
      pipeline , unsafe,同时也为 pipeline 初始了 head 节点和 tail 节点。
    5. 在register0 方法成功以后调用在 dobind 方法中调用 doBind0 方法,该方法会 调
      用 NioServerSocketChannel 的 doBind 方法对 JDK 的 channel 和端口进行绑定,
      完成 Netty 服务器的所有启动,并开始监听连接事件
  2. Netty 接受请求过程源码剖析
    总体流程:接受连接----->创建一个新的NioSocketChannel----------->注册到一个worker EventLoop 上--------> 注册selecot Read 事件。

    1. 服务器轮询 Accept 事件,获取事件后调用 unsafe 的 read 方法,这个 unsafe 是 ServerSocket 的内部类,该方
      法内部由2部分组成
    2. doReadMessages 用于创建 NioSocketChannel 对象,该对象包装 JDK 的 Nio Channel 客户端。该方法会像创建
      ServerSocketChanel 类似创建相关的 pipeline , unsafe,config
    3. 随后执行 执行 pipeline.fireChannelRead 方法,并将自己绑定到一个 chooser 选择器选择的 workerGroup 中的
      一个 EventLoop。
  3. Pipeline Handler HandlerContext创建源码剖析

    1. 每当创建 ChannelSocket 的时候都会创建一个绑定的 pipeline,一对一的关系,创建
      pipeline 的时候也会创建 tail 节点和 head 节点,形成最初的链表。
    2. 在调用 pipeline 的 addLast 方法的时候,会根据给定的 handler 创建一个 Context,
      然后,将这个 Context 插入到链表的尾端(tail 前面)。
    3. Context 包装 handler,多个 Context 在 pipeline 中形成了双向链表
    4. 入站方向叫 inbound,由 head 节点开始,出站方法叫 outbound ,由 tail 节点开始
  4. Netty 心跳(heartbeat)服务源码剖析在这里插入图片描述

  5. Netty 核心组件 EventLoop 源码剖析
    在这里插入图片描述

  6. handler 中加入线程池和Context 中添加线程池的源码剖析
    在这里插入图片描述

    1. 当我们在调用 addLast 方法添加线程池后,handler 将优先使用这个线程池,如果不添加,将使用 IO 线程
static void invokeChannelRead(finalAbstractChannelHandlerContext next, Object msg) {
    final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next);
    EventExecutor executor = next.executor();
    if (executor.inEventLoop()) {//判断是否为当前线程
        next.invokeChannelRead(m);//同步执行
    } else {
        executor.execute(new Runnable() { // 使用handler的线程池异步执行
            @Override
            public void run() {
                next.invokeChannelRead(m);
            }
        });
    }
}
  1. 当耗时任务执行完毕再执行 pipeline write 方法的时候 ,最终会将write工作 交给IO 线程处理
private void write(Object msg, boolean flush, ChannelPromise promise) {
    ...
    final AbstractChannelHandlerContext next = findContextOutbound(flush ?
            (MASK_WRITE | MASK_FLUSH) : MASK_WRITE);
    final Object m = pipeline.touch(msg, next);
    EventExecutor executor = next.executor();
    // 判断是否为当前线程
    if (executor.inEventLoop()) {
        if (flush) {
            next.invokeWriteAndFlush(m, promise);
        } else {
            next.invokeWrite(m, promise);
        }
    } else {
    	//非IO线程异步执行的,再创建任务提交给IO线程
        final WriteTask task = WriteTask.newInstance(next, m, promise, flush);
        if (!safeExecute(executor, task, promise, m, !flush)) {
            task.cancel();
        }
    }
}

小结:耗时业务可以异步执行,但IO读写始终在Worker Group的IO线程执行。

五、用Netty 自己 实现 dubbo RPC

相关文章:

  • 【300+精选大厂面试题持续分享】大数据运维尖刀面试题专栏(十五)
  • 【毕业设计】基于机器学习的跌倒检测系统 - 图像识别
  • paddlepaddle
  • 视频怎么转音频?推荐使用这几种方法
  • 欧洲玩具EN 71-3:2019的安全性某些元素的迁移
  • 【学习教程】遥感数据与DSSAT作物生长模型同化及在作物长势监测与估产中的应用
  • 海量机器类通信场景终端安全问题研究
  • Element el-table 等表格组件超出隐藏(show-overflow-tooltip)按需展示且可鼠标移入
  • 在线健康信息替代搜寻过程实验研究:特征分析及阶段识别
  • 中国精炼铜行业发展监测及投资战略研究报告
  • nr_micro_shell在STM32+FreeRTOS平台的移植和使用
  • 一种基于区块链的物联网架构设计
  • 面向对象的封装、继承、多态
  • 时序数据库太爽了TDengine3.0
  • H265码流RTP封装方式详解
  • [分享]iOS开发 - 实现UITableView Plain SectionView和table不停留一起滑动
  • 《用数据讲故事》作者Cole N. Knaflic:消除一切无效的图表
  • 【402天】跃迁之路——程序员高效学习方法论探索系列(实验阶段159-2018.03.14)...
  • CentOS 7 修改主机名
  • HTTP中GET与POST的区别 99%的错误认识
  • Java 多线程编程之:notify 和 wait 用法
  • java8 Stream Pipelines 浅析
  • Java超时控制的实现
  • js 实现textarea输入字数提示
  • Laravel 中的一个后期静态绑定
  • Redis 懒删除(lazy free)简史
  • Terraform入门 - 3. 变更基础设施
  • vue:响应原理
  • 给github项目添加CI badge
  • 坑!为什么View.startAnimation不起作用?
  • 免费小说阅读小程序
  • 前端每日实战:70# 视频演示如何用纯 CSS 创作一只徘徊的果冻怪兽
  • 入手阿里云新服务器的部署NODE
  • 新书推荐|Windows黑客编程技术详解
  • 怎么把视频里的音乐提取出来
  • 转载:[译] 内容加速黑科技趣谈
  • scrapy中间件源码分析及常用中间件大全
  • 如何正确理解,内页权重高于首页?
  • 组复制官方翻译九、Group Replication Technical Details
  • ​2020 年大前端技术趋势解读
  • #单片机(TB6600驱动42步进电机)
  • $(document).ready(function(){}), $().ready(function(){})和$(function(){})三者区别
  • (2009.11版)《网络管理员考试 考前冲刺预测卷及考点解析》复习重点
  • (Arcgis)Python编程批量将HDF5文件转换为TIFF格式并应用地理转换和投影信息
  • (C语言)fgets与fputs函数详解
  • (LeetCode 49)Anagrams
  • (阿里云万网)-域名注册购买实名流程
  • (超详细)语音信号处理之特征提取
  • (分布式缓存)Redis分片集群
  • (黑客游戏)HackTheGame1.21 过关攻略
  • (三)Pytorch快速搭建卷积神经网络模型实现手写数字识别(代码+详细注解)
  • (四)【Jmeter】 JMeter的界面布局与组件概述
  • (小白学Java)Java简介和基本配置
  • (原)本想说脏话,奈何已放下
  • .equals()到底是什么意思?