前一篇blog,讲解了如何快速启动netty服务,并通过telnet命令来访问的简单过程。其中用到了netty中常用的几个类和方法,本文将做一一介绍(其中翻译了netty的api文档,同时结合自己的理解)。
首先,看类:ServerBootstrap,Server的启动过程就是从这里开始的。通过简单的构造方法注入ChannelFactory后设置ChannelPiplineFactory,再调用bind方法,服务器便启动起来了。这里重点关注一下两个工厂类,从类名可以看出是用来产出Channel和ChannelPipline的。Channel和ChannelPipline都是netty的核心概念,贯穿了服务的整个过程。
NIO中的通道
那么Channel在netty中扮演了一个怎么样的角色呢?顾名思义,Channel即是通道的意思。提到Channel,首先会想到NIO。在Nio中,废弃了面向Socket和ServerSocket编程的方式,引入了通道和字节缓冲区(ByteBuffer)的概念。通道关联着某个文件描述符(FD)和字节缓冲区,来将缓冲区的数据写入FD关联的文件或者套接字,或将文件或套接字的内容读入ByteBuffer。所以通道分为读、写通道,分别实现了ReadableByteChannel、WritableByteChannel,当然同时实现了两个接口后便是双向的通道了。不过NIO中已经为我们定义好了很多好用的通道,能够解决我们遇到的大多数问题,不用自己去重新实现。那么在众多的实现中,与网络通信相关联的通道有哪些呢,来看看类图。

从下往上看,ServerSocketChannel、SocketChannel、DatagramChannel是我们需要打交道的面向连接和无连接的通道。它们的继承关系是这样的:AbstractSelectableChannel
—-> … --> InterruptibleChannel –-> Channel。
这里又需要引入一个概念:selector,它是Channel的最佳搭档。我们知道NIO与OIO相比,很大优势在于NIO可以选择非阻塞模式处理I/O事件,从而避免了线程阻塞的情况。尤其是在高并发的情况下,使用传统Socket,往往需要为每一个连接创建一个线程,如果不这样,当工作线程都阻塞了,来了新的请求就没人干活了。然而,创建大量的线程带来的消耗是巨大的,例如:上下文切换等。而在NIO中,可以启用非阻塞的模式来进行,例如,在某个连接(Connect1)中读取消息时,如果此时没有消息到达,读取线程可以立即返回,无需等待读取成功,这样就不怕工作线程都阻塞而导致没有工作人员的情况了。但是仅仅靠非阻塞来处理高并发是不够的,当工作线程去处理Connect2时,将Connect1放在哪里呢、当Connect1中有了新的消息时怎么通知到工作线程呢?selector的加入,完美的解决了这个问题。Selector融合了linux中的select、poll或者epoll模型,通过reactor模式来达到I/O多路复用的目的(在下一篇文章中将会做详细的介绍)。在初始化时,告知selector管理器,当前的通道是哪一个(即关联的socket)、当该通道上面发生了xx事件时需要被记录下来。这个时候,与该通道关联的线程可以去做其他事情(例如:处理其他通道的消息),在必要的时候,该线程去询问selector管理器,自己感兴趣的事件中哪些已经发生了,如果发生了就加入到自己的处理队列中,做好处理的就绪工作。当然,你也可以选择netty中OIO的实现,不过对高并发的处理上,性能相对会低很多。
通道接口InterruptibleChannel表示该通道是可以被中断的,当工作线程在某通道上被阻塞的时候,该线程被中断了,那么通道将会关闭,此线程也会产生一个ClosedByInterruptException异常。假设一个线程的中断状态被设置后,再去访问某通道,此时通道也会被关闭,同时抛出ClosedByInterruptException异常。如果一个通道被关闭,休眠在该通道上面的所有线程都会被唤醒,同时收到一个AsynchronousCloseException异常。从上图可以看出,我们用到的几个socket相关的通道都是可中断的通道。
在类的继承中,ServerSocketChannel与SocketChannel、DatagramChannel是有所不同的。SocketChannel、DatagramChannel同时继承了ReadableByteChannel、WritableByteChannel,可用于读和写。这是由于ServerSocketChannel本身并不会读写,专门用于接收connect,收到connect后,由SocketChannel来处理消息。
Netty中的通道
同样netty中也引入了通道的概念,netty框架在其nio的实现过程中,实际上是对nio的通道进行了上层的封装,它是关联网络socket或者能够用于I/O操作(比如:读、写、连接和绑定)的组件。先看一下NioServerSocketChannel的继承关系:

1.
ServerChannel:用于接收连接请求的通道,它通过accept()方法来创建子通道,例如其子类ServerSocketChannel。
2.
SocketChannel:TCP/IP socket 通道,它通常被serverSocketChannel的accept()方法或者ClientSocketChannelFactory类创建。
3.
AbstractChannel:Channel的抽象实现。
4.
DatagramChannel:UDP/IP通道,通过DatagramChannelFactory创建。
5. LocalChannel:用于本地传输的通道。
Channel给我们提供了:
1.
通道目前的状态(如:是否打开?是否已连接?)
2.
通道的配置参数(如:用于接收消息的buffer的大小)
3.
通道提供的I/O操作(如:写、连接、绑定等)
4.
还提供了用于处理与通道相关联的I/O事件和I/O请求的ChannelPipline
通道中所有的I/O操作都是异步进行的
这意味着所有的I/O调用在结束的时候都不能保证该I/O操作已经完成了。相反的,这个时候用户需要返回一个ChannelFuture实例,当这个请求成功、失败或者取消的时候,Futrue就会通知你。
通道是分层级的
一个通道是否有父通道取决于它的创建方式。例如:通过ServerSocketChannel收到连接时(ServerSocketChannel.accepted()方法)创建的SocketChannel,在channel的getParent()方法中就会返回他的父通道ServerSocketChannel。
分层结构的意义在于你需要的通道是属于哪种传输方式。例如:你可以写一个新通道的实现方式,这个通道和它的子通道共享一个socket连接,例如BEEP协议和SSH协议的实现。
向下转换解决特殊的传输方式
一些网络传输需要附加一些特殊的操作。这时可以通过继承的方式,在子类中去实现这些操作。例如:用OIO的方式处理报文传输,在DatagramChannel中就实现了广播join和leave的操作。
感兴趣事件(InterestOps)
通道有一个被称作InterestOps的属性,这和NIO中的SelectionKey相似。它是由两个标志组成的bit
field来表示的。
1. OP_READ:如果设置了这个标志,那么从远端发送来的消息将会被立即读到。相反,如果没有设置,就有等到被设置过后才能读取远端的消息了。
2. OP_WRITE:如果设置了这个标志,写请求就不会发送到远端,而是停留在队列中,直到清除了这个标志为止。如果没有设置,写请求就会被尽快的进行出队列的操作。
3. OP_READ_WRITE:这个标志关联了OP_READ和OP_WRITE,含义是只有写请求才会被挂起。
4. OP_NONE:这个标志关联了非OP_READ和非OP_WRITE,含义是只有读请求才会被挂起。
用户可以通过setReadable(boolean)函数来设置或者清除OP_READ来挂起和恢复读操作。
需要注意的是,不能像设置或者清除OP_READ一样来处理OP_WRITE,它是只读的,用于告诉应用挂起的写请求是否达到了临界值,避免放入过多挂起的写请求导致内存溢出。比如:在用NIO传输的NioSocketChannelConfig中使用writeBufferLowWaterMark和writeBufferHighWaterMark属性来决定何时可以放入或者清除OP_WRITE标志。
事件
通道封装了NIO的Channel,用于接收连接或者读取消息,收到连接或者消息就代表一个事件发生了,在netty中同样做了相应的映射,抽象出ChannelEvent的概念,表示:和某通道关联的I/O事件或者I/O请求。来看看事件的类图结构:

事件分为UpStream事件和downStream事件,一个事件的处理流向如果是从ChannelPipline中的第一个(head)Handler(后文讲解)开始到最后一个(tail)Handler,那么就称这个事件为UpStream事件,相反,如果一个事件的处理流向是从ChannelPipline中的最后一个Handler开始到第一个Handler,就称这个事件为downStream事件。
当服务器端收到来自客户端的消息时,携带消息的事件是一个Upstream事件。当服务器端向客户端发送消息或者回应客户端的时候,这个事件就为downStream事件。当然,站在客户端的角度看也是一样。Upstream事件往往是由外向内获取资源等操作后触发的,例如:InputStream.read(byte[])等事件发生后通知handler去处理读到的消息,downStream事件往往是由内向外发送请求时所触发的,例如:OutputStream.write(byte[]),Socket.connect
(SocketAddress), and Socket.close()等请求会触发handler进行写、连接、关闭socket等操作。
个人理解:upStream事件是事件发生之后,用于通知handler做相应的处理,这时事件已经发生;downStream事件是通知handler去做相应的请求操作,是为了处理该事件所发起的请求。
UpStream事件包括:
事件名称
|
事件类型与发生条件
|
含义
|
备注
|
messageReceived
|
MessageEvent
|
表示从远端接收到了消息(eg:ChannelBuffer)
|
|
exceptionCaught
|
ExceptionEvent
|
表示在某handler或者I/O线程中发生了异常
|
|
channelOpen
|
ChannelStateEvent
(state=OPEN,value=true)
|
表示某通道打开了,但是还没有绑定或者链接成功
|
注意:这个事件是由Boss 线程内部触发的,所以不要对它做一些重量级的操作,否则会阻塞其他worker线程的调度
|
channelClosed
|
ChannelStateEvent
(state=OPEN,value=false)
|
表示关联的通道已经关闭和相关资源已经释放
|
|
channelBound
|
ChannelStateEvent
(state=BOUND,value=socketAddress)
|
表示通道已经绑定到本地地址,但还没有连接
|
注意:同channelOpen
|
channelUnbound
|
ChannelStateEvent
(state=BOUND,value=null)
|
表示已从当前地址解除绑定
|
|
channelConnected
|
ChannelStateEvent
(state=CONNECTED,value=socketAddress)
|
表示当前通道已经打开、绑定了本地地址、并与远程地址连接成功
|
注意:同channelOpen
|
writeComplete
|
WriteCompletionEvent
|
表示有消息被写到了远端
|
|
channelDisconnected
|
ChannelStateEvent
(state=CONNECTED,value=socketAddress)
|
表示通道与远端的连接断开
|
|
channelInterestChanged
|
ChannelStateEvent
(state= INTEREST_OPS)
|
表示修改了通道感兴趣的事件
|
|
有两种事件只被用于有子通道的通道,比如:ServerSocketChannel
事件名称
|
事件类型与发生条件
|
含义
|
备注
|
childChannelOpen
|
ChildChannelStateEvent
(childChannel.isOpen() = true)
|
当子通道发生OPEN事件的时候,例如:当serverChannel接到连接时
|
|
childChannelClosed
|
ChildChannelStateEvent
(childChannel.isOpen() = false)
|
当子通道发生CLOSE事件的时候,例如:接收到的连接关闭
|
|
downStream事件包括:
事件名称
|
事件类型与发生条件
|
含义
|
备注
|
write
|
MessageEvent
|
向通道发送消息
|
|
bind
|
ChannelStateEvent
(state=BOUND,value=socketAddress)
|
将通道绑定到value所指向的地址
|
|
unbind
|
ChannelStateEvent
(state=BOUND,value=null)
|
请求解除与关联地址的绑定关系
|
|
connect
|
ChannelStateEvent
(state=CONNECTED,value=socketAddress)
|
请求连接到value所指定的地址
|
|
unconnect
|
ChannelStateEvent
(state=CONNECTED,value=null)
|
请求与当前地址解除连接关系
|
|
close
|
ChannelStateEvent
(state=OPEN,value=false)
|
关闭通道
|
|
需要注意的是在downStream事件中没有提到open事件,这是因为ChannelFactory在创建通道的时候它就处于open状态了。
Handler
当接收到一个ChannelEvent时,我们应该做怎么样的处理,比如:在消息被Channel读入的时候我们应该怎么处理,在回复客户端之前应该干点什么,这些都是应该由我们的应用程序来控制的业务逻辑。可以看到,在上一篇文章中,Server中包含了一个内部类MyChannelHandler,在接收到连接时输出当前Channel的信息、接收到消息时回复客户端等操作就是我们的业务逻辑。在netty中,为我们封装了ChannelHandler接口,用于处理或拦截ChannelEvent,并且传递这个事件给所在ChannelPipline中的下一个handler。
子类
ChannelHandler接口没有实现任何方法。用于处理事件的Handler需要去继承它的子接口。以下的两个子接口用于处理接收到的事件,一个是处理upStream事件的,另一个是用来处理downStream事件的。
1.
ChannelUpstreamHandler:用于处理upStream事件。
通常被用于工作者线程拦截到I/O请求中转换(编码等处理)消息或者其它相关的业务逻辑。
SimpleChannelUpstreamHandler是实现中最常用的一个类,因为它已经实现了关于各个事件最基础的方法。当然,遇到特殊的需求,也可以直接实现这个接口来做处理。
2.
ChannelDownstreamHandler:用于处理downStream事件。
ChannelPipline
前面介绍了handler,通过在Handler中注入业务逻辑。但是我们对业务逻辑的处理往往不像前一篇文章中讲到的那么简单,例如:在接收到消息时,先进行解码,得到我们需要的数据结构,再对该数据结构进行真正的逻辑处理等。这时,我们就可以将这两个逻辑放到两个handler中,一个用于解码,另一个用于处理业务,并且规定handler的执行顺序,先解码后处理业务。这样我们就可以把工作拆分开来,代码看起来干净、简洁。尤其是在我们需要做的事情很多时,将任务拆解是一种很好的方式。这就是即将隆重推出的ChannelPipline。在ChannelPipline中注入我们实现好的handler,netty就会在谋事件发生的时候依次执行handler。

其中head和tail对应的类:DefaultChannelHandlerContext,是整个处理流程的上下文。以下为类图:

Context中定义了当前的handler实例,并且根据ChannelHandler的类型记录是用于处理upstream事件还是downstream事件的,分别以两个boolean变量表示。再看看next、prev成员变量,很明显这是一个双向链表的结构,通过next找到下一个handler,通过prev找到上一个handler。
Context是ChannelPipline中的重要角色,被定义为两个变量:head、tail。也就是说可以从ChannelPipline中找到头部和尾部的context即可找到对应的handler。而通过该context的sendUpstream(ChannelEvent)和sendDownstream(ChannelEvent)方法又可以将事件传递给其上下的handler处理,从而串起了upstream事件和downstream事件的整个流程。

- 大小: 63.7 KB

- 大小: 52.3 KB

- 大小: 61.8 KB

- 大小: 28.5 KB

- 大小: 26.9 KB
分享到:
相关推荐
Netty入门教程文档 Netty是Java的网络编程框架,广泛应用于数据采集服务中,本文将对Netty的基本概念和应用进行详细介绍,并将其与ETL技术结合,讲解如何使用Netty进行数据流转和处理。 1. ETL概述 ETL(Extract...
在日常的开发和使用中,我们经常需要借助各种小工具来提高工作效率,例如快速启动常用的应用程序、管理文件等。一个简单但功能强大的集成工具箱可以帮助用户快速访问、启动并管理程序。今天,我们将以Python为基础,结合Tkinter和Win32API,开发一个类似Windows快捷方式的工具箱应用,能够让你轻松集成各种常用程序并一键启动
django自建博客app
《基于YOLOv8的智慧校园实验室高压灭菌锅安全联锁系统》(包含源码、可视化界面、完整数据集、部署教程)简单部署即可运行。功能完善、操作简单,适合毕设或课程设计
用于hifi测序数据的基因组组装程序
Microsoft Access 2010 数据库引擎可再发行程序包AccessDatabaseEngine-X64解压后的文件AceRedist
从大模型、智能体到复杂AI应用系统的构建——以产业大脑为例
自然语言处理之TF-IDF算法与TextRank算法的缠绵_textrank,tf-idf和两者的组合-CSDN博客.html
内容概要:2023版《科学智能 (AI4S)全球发展观察与展望》阐述了AI for Science(AI4S)在全球范围内的最新进展及其对科学和工业的深远影响。文章首先回顾了AI4S在过去一年中的快速发展,特别是在药物研发、材料科学、地质学、污染治理等多个领域的应用实例。AI4S通过结合深度学习、机器学习和其他AI技术,加速了从基础研究到实际应用的转化过程。例如,在药物研发中,AI4S帮助科学家克服了“反摩尔定律”的挑战,提高了新药研发的成功率;在材料科学中,AI4S实现了复杂材料的高效模拟,如人造钻石、石墨烯、碳纳米管等;在地质学中,AI4S通过模拟地球内部结构和物理过程,为地震学研究提供了新视角。此外,文章还探讨了大语言模型(LLMs)与科学方法的结合,指出LLMs不仅能辅助科学研究,还能生成新的科学假设并进行逻辑推理。 适合人群:具备一定科研背景或对AI技术感兴趣的科研人员、工程师、政策制定者及高校师生。
这个数据集包含了日常步数统计、睡眠时长、活跃分钟数以及消耗的卡路里,是个人健康与健身追踪的一部分。 该数据集非常适合用于以下实践: 数据清洗:现实世界中的数据往往包含缺失值、异常值或不一致之处。例如,某些天的步数可能缺失,或者存在不切实际的数值(如10,000小时的睡眠或负数的卡路里消耗)。通过处理这些问题,可以学习如何清理和准备数据进行分析。 探索性分析(发现日常习惯中的模式):可以通过分析找出日常生活中的模式和趋势,比如一周中哪一天人们通常走得最多,或是睡眠时间与活跃程度之间的关系等。 构建可视化图表(步数趋势、睡眠与活动对比图):将数据转换成易于理解的图形形式,有助于更直观地看出数据的趋势和关联。例如,绘制步数随时间变化的趋势图,或是比较睡眠时间和活动量之间的关系图。 数据叙事(将个人风格的追踪转化为可操作的见解):通过讲述故事的方式,把从数据中得到的洞察变成具体的行动建议。例如,根据某人特定时间段内的活动水平和睡眠质量,提供改善健康状况的具体建议。
框架结构天城商业办公楼5200平米(建筑图 结构图 计算书 开题报告 任务书 文献翻.zip
柴油机连杆加工工艺及夹具设计.zip
读书网首页的HTML信息
文字渐变颜色代码生成器:让文字绽放多彩魅力,演示:在信息交流日益丰富的今天,个性化的文字展示成为吸引目光的关键。这款文字渐变颜色代码生成器,便是为满足这一需求而生的绿色软件,无需安装,便捷实用。 它的操作极为简便。用户只需在软件界面中输入想要转换的文字内容,接着从丰富的色彩选项里挑选心仪的起始颜色与结束颜色,随后轻轻按下 “转换按钮”,神奇的事情就此发生 —— 适用于论坛、网页、QQ 空间等多种平台,以及自定义格式的渐变颜色代码便会即刻生成。不仅如此,生成的代码还能自动复制到剪切板,极大地节省了用户手动复制的时间。当你在论坛回帖、更新网页内容或是装扮 QQ 空间时,只需轻松粘贴代码,原本单调的文字瞬间就能拥有绚丽的渐变色彩,瞬间脱颖而出,为你的表达增添独特魅力,让文字不再平凡,轻松成为视觉焦点。 一款可以轻松把一段文字生成渐变颜色代码的绿色软件,当你在软件中输入完要转换的文字后,只需要挑选自己喜欢的起始颜色、结束颜色后,按一下―转换按钮即可生成相应的论坛/网页/QQ空间以及自定义格式代码,并且代码可以自动复制到剪切板中,回帖时直接粘贴代码即可不错得文字代码生成器,让你得文字更加漂亮.
1.【锂电池剩余寿命预测】Transformer锂电池剩余寿命预测(Matlab完整源码和数据) 2.数据集:NASA数据集,已经处理好,B0005电池训练、B0006测试; 3.环境准备:Matlab2023b,可读性强; 4.模型描述:Transformer在各种各样的问题上表现非常出色,现在被广泛使用。 5.领域描述:近年来,随着锂离子电池的能量密度、功率密度逐渐提升,其安全性能与剩余使用寿命预测变得愈发重要。本代码实现了Transformer在该领域的应用。 6.作者介绍:机器学习之心,博客专家认证,机器学习领域创作者,2023博客之星TOP50,主做机器学习和深度学习时序、回归、分类、聚类和降维等程序设计和案例分析,文章底部有博主联系方式。从事Matlab、Python算法仿真工作8年,更多仿真源码、数据集定制私信。
资源内项目源码是来自个人的毕业设计,代码都测试ok,包含源码、数据集、可视化页面和部署说明,可产生核心指标曲线图、混淆矩阵、F1分数曲线、精确率-召回率曲线、验证集预测结果、标签分布图。都是运行成功后才上传资源,毕设答辩评审绝对信服的保底85分以上,放心下载使用,拿来就能用。包含源码、数据集、可视化页面和部署说明一站式服务,拿来就能用的绝对好资源!!! 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、大作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.txt文件,仅供学习参考, 切勿用于商业用途。
资源内项目源码是来自个人的毕业设计,代码都测试ok,包含源码、数据集、可视化页面和部署说明,可产生核心指标曲线图、混淆矩阵、F1分数曲线、精确率-召回率曲线、验证集预测结果、标签分布图。都是运行成功后才上传资源,毕设答辩评审绝对信服的保底85分以上,放心下载使用,拿来就能用。包含源码、数据集、可视化页面和部署说明一站式服务,拿来就能用的绝对好资源!!! 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、大作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.txt文件,仅供学习参考, 切勿用于商业用途。
Android项目原生java语言课程设计,包含LW+ppt