Netty笔记

Netty的介绍

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


前言

  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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值