• 四、Netty模块组件


    四、Netty模块组件

        1、功能特性

            (1)、传输服务:支持BIO和NIO。

            (2)、容器集成:支持OSGI、JBossMC、Spring、Guice容器。

            (3)、协议支持:HTTP、Protobuf、二进制、文本、WebSocket等一系列常见协议都支持,还支持通过实行编码解码逻辑来实现自定义协议。

            (4)、Core核心:可扩展事件模型、通用通信API、支持零拷贝的ByteBuf缓冲对象。

        2、Bootstrap、ServerBootstrap

            (1)、概述

                ①、Bootstrap意思是引导,一个Netty应用通常由一个Bootstrap开始,主要作用是配置整个Netty程序,串联各个组件,Netty中Bootstrap类是客户端程序的启动引导类,ServerBootstrap是服务端启动引导类。

                ②、类Bootstrap、类ServerBootstrap均继承抽象类AbstractBootstrap。

            (2)、常用方法

                ①、public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {...}

                    该方法用于服务器端,用来设置两个EventLoop,parentGroup负责处理连接请求,childGroup负责处理和客户端相关的业务。

                ②、public B group(EventLoopGroup group) {...}

                    该方法是抽象类AbstractBootstrap的方法,用于客户端,用来设置EventLoop。

                ③、public B channel(Class channelClass) {...}

                    该方法是抽象类AbstractBootstrap的方法,用来设置一个服务器端、客户端的通道实现。

                ④、public B option(ChannelOption option, T value) {...}

                    该方法是抽象类AbstractBootstrap的方法,用来给ServerChannel 添加配置。

                ⑤、public ServerBootstrap childOption(ChannelOption childOption, T value) {...}

                    用来给接收到的通道添加配置。

                ⑥、public ServerBootstrap childHandler(ChannelHandler childHandler) {...}

                    该方法用来设置业务处理类(自定义的handler),对应childGroup。

                ⑦、public B handler(ChannelHandler handler) {...}

                    该方法是抽象类AbstractBootstrap的方法,用来设置服务端处理类(自定义的handler),对应parentGroup。

                ⑧、public ChannelFuture bind(int inetPort) {...}

                    该方法是抽象类AbstractBootstrap的方法,用于服务器端,用来设置占用的端口号。

                ⑨、public ChannelFuture connect(String inetHost, int inetPort) {...}

                    该方法用于客户端,用来连接服务器端。

        3、ChannelFuture

            接口ChannelFuture继承Netty的Future接口,Netty的Future接口继承JUC的Future接口。

            (1)、概述

                ①、异步和同步相对,当一个异步调用发出后,调用者不能立刻得到结果。实际处理这个调用的组件在完成后,通过状态、通知和回调来通知调用者。

                ②、Netty中的I/O操作是异步的,包括Bind、Write、Connect等操作会首先返回一个ChannelFuture对象。

                ③、因为调用者并不能立刻获得结果,而是通过Future-Listener机制,用户可以方便的主动获取或者通过通知机制获得IO操作结果。

                ④、Netty的异步模型是建立在future和callback之上的,callback就是回调。

            (2)、Future-Listener机制

                ①、当Future对象刚刚创建时,处于非完成状态,调用者可以通过返回的ChannelFuture来获取操作执行的状态,注册监听函数来执行完成后的操作。

                ②、假设一个方法fun,计算过程可能非常耗时,等待fun返回显然不合适。那么可以在调用fun的时候,立马返回一个Future,后续可以通过Future去监控方法fun的处理过程。

            (3)、Future常用方法

                ①、Channel channel();

                    ChannelFuture的方法,返回当前正在进行IO操作的通道。

                ②、ChannelFuture sync() throws InterruptedException;

                    ChannelFuture的方法,阻塞等待异步操作执行完毕,重写Netty的Future接口的方法。

                ③、addListener方法

                    注册监听器,当操作已完成(isDone方法返回完成),将会通知指定的监听器;如果Future对象已完成,则通知指定的监听器。

                ④、removeListener方法

                    移除监听。

                ⑤、isDone方法

                    判断当前操作是否完成。

                ⑥、isSuccess方法

                    判断已完成的当前操作是否成功。

                ⑦、isCancelled方法

                    判断已完成的当前操作是否被取消。

                ⑧、getCause方法

                    获取已完成的当前操作失败的原因。

        4、Channel

            (1)、概述

                ①、Netty网络通信的组件,能够用于执行网络I/O操作。

                ②、通过Channel可获得当前网络连接的通道的状态。

                ③、通过Channel可获得网络连接的配置参数(例如接收缓冲区大小)。

                ④、Channel提供异步的网络I/O操作(如建立连接,读写,绑定端口),异步调用意味着任何I/O调用都将立即返回,并且不保证在调用结束时所请求的I/O操作已完成。调用立即返回一个ChannelFuture实例,通过注册监听器到ChannelFuture上,可以I/O操作成功、失败或取消时回调通知调用方。

                ⑤、支持关联I/O操作与对应的处理程序。

            (2)、Channel类型

                不同协议、不同的阻塞类型的连接都有不同的Channel类型与之对应,这些Channel涵盖了UDP和TCP网络IO以及文件IO。

                ①、NioSocketChannel:异步的客户端TCP Socket连接。

                ②、NioServerSocketChannel:异步的服务器端TCP Socket连接。

                ③、NioDatagramChannel:异步的UDP连接。

                ④、NioSctpChannel:异步的客户端Sctp连接。

                ⑤、NioSctpServerChannel:异步的Sctp服务器端连接。

        5、Selector

            (1)、概述

                ①、Netty基于Selector对象实现I/O多路复用,通过Selector,一个线程可以监听多个连接的Channel事件。

                ②、当向一个Selector中注册Channel后,Selector内部的机制就可以自动不断地查询(select)这些注册的Channel是否有已就绪的I/O事件(例如可读、可写、网络连接完成等),这样程序就可以很简单地使用一个线程高效地管理多个Channel。

        6、NioEventLoop

            (1)、概述

                ①、NioEventLoop中维护了一个线程和任务队列,支持异步提交执行任务,线程启动时会调用NioEventLoop的run方法,执行I/O任务和非I/O任务。两种任务的执行时间比由变量ioRatio控制,默认为50,则表示允许非IO任务执行的时间与IO任务的执行时间相等。

                ②、NioEventLoop表示一个不断循环执行处理任务的线程,每个NioEventLoop都有一个Selector,用于监听绑定在其上的socket网络通道。

                ③、NioEventLoop内部采用串行化设计,从消息的读取->解码->处理->编码->发送,始终由IO线程NioEventLoop负责。

            (2)、任务类型

                ①、I/O任务:即selectionKey中ready的事件,如accept、connect、read、write等,由processSelectedKeys方法触发。

                ②、非IO任务:添加到taskQueue中的任务,如register0、bind0等任务,由runAllTasks方法触发。

            (3)、TaskQueue3种典型使用场景

                ①、自定义的普通任务,放入taskQueue队列中

                ②、自定义定时任务,放入scheduledTaskQueue队列中

                ③、非当前reactor线程调用channel的各种方法

            (4)、与其他组件的对应关系

                ①、NioEventLoopGroup下包含多个NioEventLoop

                ②、每个 NioEventLoop 中包含有一个Selector,一个taskQueue

                ③、每个 NioEventLoop的Selector上可以注册监听多个NioChannel

                ④、每个NioChannel只会绑定在唯一的NioEventLoop上

                ⑤、每个NioChannel都绑定有一个自己的ChannelPipeline

        7、NioEventLoopGroup

            (1)、概述

                NioEventLoopGroup是接口EventLoopGroup的实现类。

                ①、EventLoopGroup是一组EventLoop的抽象,NioEventLoopGroup主要管理EventLoop的生命周期,可以理解为一个线程池,内部维护了一组线程,每个线程(NioEventLoop)维护着一个Selector实例,负责处理多个Channel上的事件,而一个Channel只对应于一个线程。

                ②、EventLoopGroup提供next接口,可以从组里面按照一定规则获取其中一个EventLoop来处理任务。Netty为了更好的利用多核CPU资源,一般会有多个EventLoop同时工作,在Netty服务器端编程中,我们一般都需要提供两个EventLoopGroup,例如:BossEventLoopGroup和WorkerEventLoopGroup。

                ③、通常一个服务端口即一个ServerSocketChannel对应一个Selector和一个EventLoop线程。BossEventLoop负责接收客户端的连接并将SocketChannel交给WorkerEventLoopGroup来进行IO处理。

            (2)、过程

                ①、BossEventLoopGroup通常是一个单线程的EventLoop,EventLoop维护着一个注册了ServerSocketChannel的Selector实例,BossEventLoopGroup不断轮询Selector将连接事件分离出来。

                ②、通常是OP_ACCEPT事件,然后将接收到的SocketChannel交给WorkerEventLoopGroup

                ③、WorkerEventLoopGroup会由next选择其中一个EventLoop来将这个SocketChannel住蹙到其维护的Selector并对其后续的IO事件进行处理。

            (3)、常用方法

                ①、public NioEventLoopGroup()

                    构造方法

                ②、public Future shutdownGracefully() {...}

                    断开连接,关闭线程,AbstractEventExecutorGroup中实现了EventExecutorGroup的方法。

            (3)、

        8、ChannelHandler

            ChannelHandler是一个接口,处理I/O事件或拦截I/O操作,并将其转发到其ChannelPipeline(业务处理链)中的下一个处理程序。

            (1)、子类

                ChannelHandler本身并没有提供很多方法,因为这个接口有许多的方法需要实现,方便使用期间,可以继承它的子类或者使用适配器类。

                ①、ChannelInboundHandler:用于处理入站I/O事件。

                ②、ChannelOutboundHandler:用于处理出站I/O操作。

            (2)、适配器

                ChannelHandler本身并没有提供很多方法,因为这个接口有许多的方法需要实现,方便使用期间,可以继承它的子类或者使用适配器类。

                ①、ChannelInboundHandlerAdapter:用于处理入站I/O事件。

                ②、ChannelOutboundHandlerAdapter:用于处理出站I/O操作。

                ③、ChannelDuplexHandler:用于处理入站和出站事件。

            (3)、ChannelInboundHandlerAdapter常用方法

                ①、public void channelRegistered(ChannelHandlerContext ctx) throws Exception {...}

                    通道注册事件。

                ②、public void channelActive(ChannelHandlerContext ctx) throws Exception {...}

                    通道就绪事件。

                ③、public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {...}

                    通道读取数据事件。

                ④、public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {...}

                    数据读取完毕事件。

                ⑤ 、public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {...}

                    通道发生异常事件。

                ⑥、public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {...}

                    监听事件,并做相应处理。例:IdleStateEvent

            (4)、ChannelHandler类型

                ①、IdleStateHandler

                    是Netty空闲处理状态的处理器,可实现心跳检测机制。当IdleStateEvent触发后,就会传递给管道的下一个handler去处理,会调用下一个handler的userEventTiggered方法, 在该方法中去处理IdleStateEvent(读空闲,写空闲,读写空闲)。

                    a、readerIdleTime : 表示多长时间没有读, 就会发送一个心跳检测包检测是否连接。

                    b、writerIdleTime : 表示多长时间没有写, 就会发送一个心跳检测包检测是否连接。

                    c、allIdleTime : 表示多长时间没有读写, 就会发送一个心跳检测包检测是否连接。

        9、ChannelHandlerContext 

            保存Channel相关的所有上下文信息,同时关联一个ChannelHandler对象。

            (1)、概述

                ①、保存Channel相关的所有上下文信息,同时关联一个ChannelHandler对象。

                ②、ChannelHandlerContext中包含一个具体的事件处理器ChannelHandler,同时ChannelHandlerContext中也绑定了对应的pipeline和Channel的信息,方便对ChannelHandler进行调用。

            (2)、ChannelHandlerContext常用方法

                ChannelHandlerContext接口继承ChannelInboundInvoker接口和ChannelOutboundInvoker接口,默认实现类DefaultChannelHandlerContext。

                ①、ChannelFuture close();

                    关闭通道。

                ②、ChannelOutboundInvoker flush();

                    刷新通道。

                ③、ChannelFuture writeAndFlush(Object msg, ChannelPromise promise);

                    将数据写到ChannelPipeline中,当前ChannelHandler的下一个ChannelHandler开始处理(出站)。

        10、ChannelPipline

            (1)、概述

                ①、ChannelPipeline是一个 Handler的集合,它负责处理和拦截inbound或者outbound的事件和操作,相当于一个贯穿Netty的链。(也可以这样理解:ChannelPipeline是保存ChannelHandler的集合,用于处理或拦截Channel的入站事件和出站操作)。

                ②、ChannelPipeline实现了一种高级形式的拦截过滤器模式,使用户可以完全控制事件的处理方式,以及Channel中各个的ChannelHandler如何交互。

            (2)、Channel和ChannelPipeline对应关系

                ①、在Netty中每个Channel都有且仅有一个ChannelPipeline与之对应。

                ②、一个Channel包含了一个ChannelPipeline,而ChannelPipeline中又维护了一个由ChannelHandlerContext组成的双向链表,并且每个ChannelHandlerContext中又关联着一个ChannelHandler。

                ③、入站事件和出站事件在一个双向链表中,入站事件会从链表head往后传递到最后一个入站的handler,出站事件会从链表tail往前传递到最前一个出站的handler,两种类型的handler互不干扰。

                ④、I/O事件由ChannelInboundHandler或ChannelOutboundHandler处理,并通过调用ChannelHandlerContext中定义的事件传播方法(例如ChannelHandlerContext.fireChannelRead(Object)和ChannelOutboundInvoker.write(Object))转发到其最近的处理程序。

                    a、入站事件由自下而上方向通过入站处理程序处理,如图左侧所示。入站Handler处理程序通常处理由图底部的I/O线程生成的入站数据,通常通过实际输入操作(例如SocketChannel.read(ByteBuffer))从远程读取入站数据。

                    b、出站事件由自上而下方向处理,如图右侧所示。 出站Handler处理程序通常会生成或转换出站传输,例如write请求,I/O线程通常执行实际的输出操作,例如SocketChannel.write(ByteBuffer)。

            (3)、常用方法

                ChannelPipeline接口继承ChannelInboundInvoker接口和ChannelOutboundInvoker接口,默认实现类DefaultChannelPipeline。

                ①、ChannelPipeline addFirst(String name, ChannelHandler handler);

                    把一个业务处理类(handler)添加到双向链的第一个位置。

                ②、ChannelPipeline addLast(String name, ChannelHandler handler);

                    把一个业务处理类(handler)添加到双向链表中的最后一个位置。

        11、ChannelOption

            (1)、概述

                Netty在创建Channel实例后,一般都需要设置ChannelOption参数。

            (2)、参数

                ①、ChannelOption.SO_BACKLOG

                    对应TCP/IP协议listen函数中的backlog参数,用来初始化服务器可连接队列大小。服务端处理客户端连接请求是顺序处理的,所以同一时间只能处理一个客户端连接。多个客户端来的时候,服务端将不能处理的客户端连接请求放在队列中等待处理,backlog参数指定了队列的大小。

                ②、ChannelOption.SO_KEEPALIVE

                    一直保持连接活动状态。

        12、Unpooled类

            (1)、概述

                Netty提供一个专门用来操作缓冲区(即 Netty 的数据容器ByteBuf)的工具类

            (2)、常用方法

                ①、public static ByteBuf copiedBuffer(byte[] array) {...}

                    通过给定的数据和字符编码返回一个Netty的ByteBuf对象。

        13、ByteBuf类

            (1)、JAVA NIO ByteBuffer缺点

                JAVA NIO提供了ByteBuffer等七种容器来提升传输时的效率,但是在使用时比较复杂,并且要进行读写切换。

                ①、ByteBuffer长度固定,一旦分配完成,它的容量不能动态扩展和收缩。

                ②、ByteBuffer只有一个标识位置的指针position,读写的时候需要手工调用flip()和rewind()等。

                ③、ByteBuffer的API功能有限,一些高级和实用的特性它不支持,需要使用者自己编程实现。

            (2)、特点

                ①、读和写使用不同的索引readerIndex和writerIndex。

                ②、读和写可以随意的切换,不需要调用flip()方法。

                ③、容量能够被动态扩展和StringBuilder一样。

                ④、用其内置的复合缓冲区可实现透明的零拷贝。

                ⑤、支持方法链。

                ⑥、支持引用计数。count == 0,release。

                ⑦、支持池。

            (3)、读写操作

                ①、ByteBuf通过两个位置指针来协助缓冲区的读写操作,分别是读操作(使用readerIndex)、写操作(使用writerIndex)。

                ②、readerIndex和writerIndex的取值一开始都是0,随着数据的写入writerIndex会增加,读取数据会使readerIndex增加,但是它不会超过writerIndex。

                    注:buffer.readByte()方法会使readerIndex增加,而buffer.getByte(i)方法不会。

                ③、在读取之后,0~readerIndex的就被视为discard的,调discardReadBytes方法,可以释放这部分空间,它的作用类似ByteBuffer的compact方法。

                ④、readerIndex和writerIndex之间的数据是可读取的,等价于ByteBuffer position和limit之间的数据。

                ⑤、writerIndex和capacity之间的空间是可写的,等价于ByteBuffer limit和capacity之间的可用空间。

                ⑥、由于写操作不修改readerIndex指针,读操作不修改writerIndex指针,因此读写之间不再需要调整位置指针,这极大地简化了缓冲区的读写操作,避免了由于遗漏或者不熟悉flip()操作导致的功能异常。

    1. +—————————————————————————————————————————————————————+
    2. | discardable bytes | readable bytes | writable bytes |
    3. | | (CONTENT) | |
    4. +—————————————————————————————————————————————————————+
    5. | | | |
    6. 0 <= readerIndex <= writerIndex <= capacity

    参考资料记录:Netty基础概念_L_D_Y_K的博客-CSDN博客

  • 相关阅读:
    Docker的配置与容器的拉取镜像、端口映射
    【数据结构】堆的实现及排序
    解决 MyBatis-Plus + PostgreSQL 中的 org.postgresql.util.PSQLException 异常
    ubuntu编译sqlite3并使用
    计算机毕业设计Java网上作业提交与批改系统(源码+系统+mysql数据库+Lw文档)
    1区TOP计算机SCI,CCF推荐,对国人友好,正在征稿中~
    【Spring】spring中存储Bean(对象)的相关注解及相关用法
    consul安装
    go初识iris框架(六) - session使用和控制
    Git基础使用
  • 原文地址:https://blog.csdn.net/L_D_Y_K/article/details/126507705