Netty第二部

一、EventLoop和EventLoopGroup

一个Channel可以近似的理解成一个Socket的包装,EventLoop管理这些Channel的

1、EventLoop

EventLoop作为线程,具体Channel由EventLoop管理,在AbstractChannel类的register()方法可以体现

@Override
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
    ObjectUtil.checkNotNull(eventLoop, "eventLoop");
    if (isRegistered()) {
        promise.setFailure(new IllegalStateException("registered to an event loop already"));
        return;
    }
    if (!isCompatible(eventLoop)) {
        promise.setFailure(
            new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName()));
        return;
    }

    AbstractChannel.this.eventLoop = eventLoop;

    // 判断是否为当前线程
    if (eventLoop.inEventLoop()) {
        register0(promise);
    } else {
        try {
            // 不是的话会交给eventLoop执行
            eventLoop.execute(new Runnable() {
                @Override
                public void run() {
                    register0(promise);
                }
            });
        } catch (Throwable t) {
            logger.warn(
                "Force-closing a channel whose registration task was not accepted by an event loop: {}",
                AbstractChannel.this, t);
            closeForcibly();
            closeFuture.setClosed();
            safeSetFailure(promise, t);
        }
    }
}

EventLoop类结构图

EventLoop对应的实现类

看看之前用到的NioEventLoop,在NioEventLoop类中定义如下两个属性,Java中线程和队列的组件就会想到线程池

private final Queue<Runnable> taskQueue;

private volatile Thread thread;

2、EventLoopGroup

EventLoopGroup主要是每个新建的Channel分配一个EventLoop以及管理EventLoop

public interface EventLoopGroup extends EventExecutorGroup {
    /**
     * Return the next {@link EventLoop} to use
     */
    @Override
    EventLoop next();

    /**
     * Register a {@link Channel} with this {@link EventLoop}. The returned {@link ChannelFuture}
     * will get notified once the registration was complete.
     */
    ChannelFuture register(Channel channel);

    /**
     * Register a {@link Channel} with this {@link EventLoop} using a {@link ChannelFuture}. The passed
     * {@link ChannelFuture} will get notified once the registration was complete and also will get returned.
     */
    ChannelFuture register(ChannelPromise promise);

    /**
     * Register a {@link Channel} with this {@link EventLoop}. The passed {@link ChannelFuture}
     * will get notified once the registration was complete and also will get returned.
     *
     * @deprecated Use {@link #register(ChannelPromise)} instead.
     */
    @Deprecated
    ChannelFuture register(Channel channel, ChannelPromise promise);
}

线程的分配

服务于 Channel 的 I/O 和事件的 EventLoop 包含在 EventLoopGroup 中。

异步传输实现只使用了少量的EventLoop(以及和它们相关联的Thread),而且在当前的线程模型中,它们可能会被多个 Channel 所共享。这使得可以通过尽可能少量的 Thread 来 支撑大量的Channel,而不是每个Channel分配一个Thread。EventLoopGroup负责为每个 新创建的 Channel 分配一个 EventLoop。在当前实现中,使用顺序循环(round-robin)的方 式进行分配以获取一个均衡的分布,并且相同的 EventLoop 可能会被分配给多个 Channel。 一旦一个 Channel 被分配给一个 EventLoop,它将在它的整个生命周期中都使用这个EventLoop(以及相关联的 Thread)。

线程管理

在内部,当提交任务到如果(当前)调用线程正是支撑EventLoop的线程,那么所提交 的代码块将会被(直接)执行。否则,EventLoop将调度该任务以便稍后执行,并将它放入 到内部队列中。当EventLoop下次处理它的事件时,它会执行队列中的那些任务/事件。

二、Channel接口

  1. Netty 的 Channel 接口所提供的 API,被用于所有的 I/O 操作(bind、connect、read和 write),它大大地降低了直接使用 Socket 类的复杂性
  2. 由于 Channel 是独一无二的,所以为了保证顺序将 Channel 声明为 java.lang.Comparable 的一个子接口。因此,如果两个不同的 Channel 实例都返回了相同的散列码,那么 AbstractChannel 中的 compareTo()方法的实现将会抛出一个 Error。

Channel 的生命周期状态

  • ChannelUnregistered :Channel 已经被创建,但还未注册到 EventLoop
  • ChannelRegistered :Channel 已经被注册到了 EventLoop
  • ChannelActive :Channel 处于活动状态(已经连接到它的远程节点)。它现在可以接 收和发送数据了
  • ChannelInactive :Channel 没有连接到远程节点

当这些状态发生改变时,将会生成对应的事件。这些事件将会被转发给 ChannelPipeline 中的 ChannelHandler,其可以随后对它们做出响应。在我们的编程中,关注 ChannelActive 和 ChannelInactive 会更多一些。

重要 Channel 的方法

  • eventLoop: 返回分配给 Channel 的 EventLoop
  • pipeline: 返回 Channel 的 ChannelPipeline,也就是说每个 Channel 都有自己的 ChannelPipeline。
  • isActive: 如果 Channel 是活动的,则返回 true。活动的意义可能依赖于底层的传输。 例如,一个 Socket 传输一旦连接到了远程节点便是活动的,而一个 Datagram 传输一旦被 打开便是活动的。
  • localAddress: 返回本地的 SokcetAddress
  • remoteAddress: 返回远程的 SocketAddress
  • write: 将数据写到远程节点,注意,这个写只是写往 Netty 内部的缓存,还没有真正 写往 socket。
  • flush: 将之前已写的数据冲刷到底层 socket 进行传输。
  • writeAndFlush: 一个简便的方法,等同于调用 write()并接着调用 flush()

三、ChannelHandlerContext、ChannelPipeline和ChannelHandler

ChannelPipeline接口

当Channel被创建时,它将会被自动地分配一个新的ChannelPipeline,每个Channel都有自己的ChannelPipeline

ChannelPipeline提供了ChannelHandler链的容器,并定义了用于在该链上传播入站(也就是从网络到业务处理)和出站(也就是从业务处理到网络),ChannelHandler都是放在ChannelPipeline中的

ChannelHandler 的生命周期

在ChannelHandler被添加到ChannelPipeline中或者被从ChannelPipeline中移除时会调用下面这些方法。这些方法中的每一个都接受一个ChannelHandlerContext参数。

  • handlerAdded 当把 ChannelHandler 添加到 ChannelPipeline 中时被调用
  • handlerRemoved 当从 ChannelPipeline 中移除 ChannelHandler 时被调用
  • exceptionCaught 当处理过程中在 ChannelPipeline 中有错误产生时被调用

ChannelPipeline中的ChannelHandler

入站(ChannelInboundHandler)和出站(ChannelOutboundHandler)ChannelHandler被安装到同一个 ChannelPipeline中,ChannelPipeline以双向链表的形式进行维护管理

如果此时有入站事件被触发,就会从ChannelPipeline头部开始流动,到达ChannelPipeline的尾部,中间只会经过定义入站的ChannelHandler;反之,出战就会从ChannelPipeline的尾部到头部,中间中间只会经过定义出站的ChannelHandler

Netty能区分入站事件的 Handler和出站事件的Handler,并确保数据只会在具有相同定向类型的两个ChannelHandler之间传递。

ChannelPipeline上的方法

既然 ChannelPipeline 以双向链表的形式进行维护管理 Handler,自然也提供了对应的方 法在 ChannelPipeline 中增加或者删除、替换 Handler。

  • addFirst、addBefore、addAfter、addLast 将一个 ChannelHandler添加到ChannelPipeline中
  • remove 将一个ChannelHandler从ChannelPipeline中移除
  • replace将ChannelPipeline中的一个ChannelHandler替换为另一个ChannelHandler
  • get 通过类型或者名称返回ChannelHandler
  • context返回和ChannelHandler绑定的ChannelHandlerContext
  • names返回ChannelPipeline中所有ChannelHandler的名称
  • ChannelPipeline的API公开了用于调用入站和出站操作的附加方法。

ChannelHandlerContext

ChannelHandlerContext代表了ChannelHandler和ChannelPipeline之间的关联,每当有ChannelHandler添加到ChannelPipeline中时,都会创建ChannelHandlerContext

ChannelHandlerContext 的主要作用就和LinkedList 内部的类Node类似。

Channel、ChannelPipeline 和 ChannelHandlerContext 上的事件传播

channel.write()、channelpipeline.write()会经过所有出站Handler

channelHandlerContext.write()只会经过下一个出站的Handler

ChannelHandlerContext 的 API

alloc 返回和这个实例相关联的 Channel 所配置的 ByteBufAllocator

bind 绑定到给定的 SocketAddress,并返回 ChannelFuture

channel 返回绑定到这个实例的 Channel

close 关闭 Channel,并返回 ChannelFuture

connect 连接给定的 SocketAddress,并返回 ChannelFuture

deregister 从之前分配的 EventExecutor 注销,并返回 ChannelFuture

disconnect 从远程节点断开,并返回 ChannelFuture executor 返回调度事件的 EventExecutor

fireChannelActive 触发对下一个 ChannelInboundHandler 上的

channelActive()方法(已 连接)的调用

fireChannelInactive 触发对下一个 ChannelInboundHandler 上的channelInactive()方法 (已关闭)的调用

fireChannelRead 触发对下一个 ChannelInboundHandler 上的 channelRead()方法(已接 收的消息)的调用

fireChannelReadComplete 触发对下一个 ChannelInboundHandler 上的 channelReadComplete()方法的调用

fireChannelRegistered 触发对下一个 ChannelInboundHandler 上的 fireChannelRegistered()方法的调用

fireChannelUnregistered 触发对下一个 ChannelInboundHandler 上的 fireChannelUnregistered()方法的调用

fireChannelWritabilityChanged 触发对下一个 ChannelInboundHandler 上的 fireChannelWritabilityChanged()方法的调用

fireExceptionCaught 触发对下一个 ChannelInboundHandler 上的 fireExceptionCaught(Throwable)方法的调用

fireUserEventTriggered 触发对下一个 ChannelInboundHandler 上的 fireUserEventTriggered(Object evt)方法的调用

handler 返回绑定到这个实例的 ChannelHandler

isRemoved 如果所关联的 ChannelHandler 已经被从 ChannelPipeline 中移除则返回 true

name 返回这个实例的唯一名称

pipeline 返回这个实例所关联的 ChannelPipeline

read 将数据从 Channel 读取到第一个入站缓冲区;如果读取成功则触发一个 channelRead 事件,并(在最后一个消息被读取完成后)通知 ChannelInboundHandler 的 channelReadComplete(ctx)方法

write 通过这个实例写入消息并经过 ChannelPipeline

writeAndFlush 通过这个实例写入并冲刷消息并经过 ChannelPipeline

当使用 ChannelHandlerContext 的 API 的时候,有以下两点:

  1. ChannelHandlerContext 和ChannelHandler 之间的关联(绑定)是永远不会改变的, 所以缓存对它的引用是安全的;
  2. 相对于其他类的同名方法,ChannelHandlerContext 的方法将产生更短的事件流,应该 尽可能地利用这个特性来获得最大的性能

入站的handler时经过会读到一个buffer中,如果中间不释放会造成内存泄漏;正常情况netty会处理,但是如果handler执行异常这个buffer就永远不会被netty处理,可以实现SimpleChannelInboundHandler接口重写channelRead0()方法,在此方法处理具体业务逻辑

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

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

相关文章

观察者模式——解决解耦的钥匙

● 观察者模式介绍 观察者模式是一个使用频率非常高的模式&#xff0c;它最常用的地方是GUI系统、订阅——发布系统。因为这个模式的一个重要作用就是解耦&#xff0c;将被观察者和观察者解耦&#xff0c;使得它们之间依赖性更小&#xff0c;甚至做到毫无依赖。以CUI系统来说&a…

Linux安装配置awscli命令行接口工具及其从aws上传下载数据

官网技术文档有全面介绍&#xff1a;安装或更新 AWS CLI 的最新版本 - AWS Command Line Interface在系统上安装 AWS CLI。https://docs.aws.amazon.com/zh_cn/cli/latest/userguide/getting-started-install.html#getting-started-install-instructionsawscli常用命令参考&…

uni-app学习笔记

目录 一、前期准备 1、项目认识 2、pages.json基本配置 3、创建页面 二、tabBar 1、获取图标 2、代码配置 三、基础认识 1、页面生命周期 2、App.vue应用生命周期 四、基础组件 1、scroll-view可滚动视图区域 2、提示框 3、swiper滑块视图容器 4、form表单组件 一…

番外---9.0 firewall 网络

### 网络配制方式&#xff1a; 00&#xff1a;依据图形界面形式配置&#xff08;nmtui&#xff09;&#xff1b; 01&#xff1a;命令形式配置(nmcli)&#xff1b; 02&#xff1a;使用系统菜单配置&#xff1b; 00&#xff1a;依据图形界面形式配置&#xff08;nmtui&#xff0…

解决方案中word中分页符的使用

在投标方案中要善于使用“分页符”&#xff0c;尽可能少使用分节符号&#xff0c;没有分页符前&#xff0c;你每次修改你的标书或者文件&#xff0c;增加或者修改内容后。你的格式字段前后都是会发生变化&#xff0c;如何稳定的保证结构呢&#xff0c;那就是分页符的使用&#…

C语言映射表在串口数据解析中的应用

一、映射表在串口数据解析中的应用 1、数据结构 typedef struct {char CMD[CMDLen];unsigned char (*cmd_operate)(char *data); }Usart_Tab; 2、指令、函数映射表 static const Usart_Tab InstructionList[CMDMax] {{"PWON",PowOn},{"PWOFF",PowOff}…

antd的Table组件使用rowSelection属性实现多选时遇到的bug

前言 前端样式框架采用AntDesign时&#xff0c;经常会使用到Table组件&#xff0c;如果要有实现多选或选择的需求时往往就会用到rowSelection属性&#xff0c;效果如下 rowSelection属性属性值如下 问题 文档中并没有说明选择时以数据中的哪个属性为准&#xff0c;看官方案例…

docker 下安装mysql8.0

在docker中查询mysql镜像 PS C:\Users\admin> docker search mysql NAME DESCRIPTION STARS OFFICIAL AUTOMATED mysql MySQL is a widely used, open-source relation……

答题测评考试小程序的效果如何

在线答题系统是一种在线练习、考试、测评的智能答题系统&#xff0c;适用于企业培训、测评考试、知识竞赛、模拟考试等场景&#xff0c;管理员可任意组题、随机出题&#xff0c;答题者成功提交后&#xff0c;系统自动判分。 多种题目类型&#xff0c;两种答题模式 练习模式&a…

Linux shell编程学习笔记20:case ... esac、continue 和break语句

一、case ... esac语句说明 在实际编程中&#xff0c;我们有时会请到多条件多分支选择的情况&#xff0c;用if…else语句来嵌套处理不烦琐&#xff0c;于是JavaScript等语言提供了多选择语句switch ... case。与此类似&#xff0c;Linux Shell脚本编程中提供了case...in...esa…

《人工智能算法图解》书籍推荐

书籍介绍 今天&#xff0c;人工智能在我们的生活中随处可见。它能推送我们喜欢的电视节目&#xff0c;帮助我们诊断疑难杂症&#xff0c;还能向我们推荐商品。因此&#xff0c;让我们掌握人工智能的核心算法&#xff0c;拥抱日新月异的智能世界吧。 与那些充斥着公式和术语的教…

麒麟KYLINIOS软件仓库搭建02-软件仓库添加新的软件包

原文链接&#xff1a;麒麟KYLINIOS软件仓库搭建02-软件仓库添加新的软件包 hello&#xff0c;大家好啊&#xff0c;今天给大家带来麒麟桌面操作系统软件仓库搭建的文章02-软件仓库添加新的软件包&#xff0c;本篇文章主要给大家介绍了如何在麒麟桌面操作系统2203-x86版本上&…

应用安全四十二:SSO安全

一、什么是SSO SSO是单点登录(Single Sign On)的缩写,是指在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。这种方式减少了由登录产生的时间消耗,辅助了用户管理,是比较流行的企业业务整合的解决方案之一。 身份验证过程依赖于双方之间的信任关…

【Git】git的下载安装与使用

目录 目录 一.下载安装 官方下载 淘宝镜像下载 安装 二.创建本地仓库 三.git的基本操作命令 git status git add . git commit -m " " 四.gitee(码云&#xff09;的使用 配置ssh公钥 ​编辑 查看公钥 gitee创建仓库 将本地仓库的文件上传到远程仓库…

使用 ChatGPT 提升 LeetCode 刷题效率

文章目录 1 背景2 操作步骤 1 背景 在做 LeetCode 的 SQL 题库时, 想在本地调试, 需要在本地的数据库上创建表以及准备测试数据, 大家都是有经验的开发人员, 简单粗暴的办法就不讲了 可以借助 ChatGPT 的能力, 生产数据库的表以及测试数据的 sql, 提升刷题效率 2 操作步骤 将…

【计算机组成与设计】Chisel取指和指令译码设计

本次试验分为三个部分&#xff1a; 目录 设计译码电路 设计寄存器文件 实现一个32个字的指令存储器 设计译码电路 输入位32bit的一个机器字&#xff0c;按照课本MIPS 指令格式&#xff0c;完成add、sub、lw、sw指令译码&#xff0c;其他指令一律译码成nop指令。输入信号名…

【UE 材质】简单的闪闪发光材质

效果 节点 参考视频&#xff1a; https://www.bilibili.com/video/BV1uK411y737/?vd_source36a3e35639c44bb339f59760641390a8

【Liunx系统编程】命令模式3

目录 一&#xff0c;zip/unzip压缩指令 二&#xff0c;tar打包/压缩/解包指令 三&#xff0c;uname获取系统信息指令 四&#xff0c;Liunx下常用且重要的按键和关机指令 五&#xff0c;文件之间的互传 1&#xff0c;Windows与Linux之间的互传 2&#xff0c;Linux系统之间…

0xGame Web 2023

0xGame Web 2023 [Week 1] signin 这题直接看源码就行&#xff0c;easy [Week 1] baby_php OST /?aQNKCDZO&b240610708 HTTP/1.1 Host: 120.27.148.152:50014 Content-Length: 11 Pragma: no-cache Cache-Control: no-cache Upgrade-Insecure-Requests: 1 Origin: htt…

K8s学习笔记——认识理解篇

1. K8s诞生背景 回顾应用的部署&#xff0c;经历了以下几个阶段&#xff1a; 传统部署&#xff1a;物理服务器上运行应用程序。虚拟机部署&#xff1a;物理服务器上安装虚拟机&#xff0c;在虚拟机上运行应用程序。容器部署&#xff1a;物理服务器上安装容器运行时&#xff0…