同步与异步
同步和异步关注的是消息通信机制 (synchronous communication/ asynchronous communication)
- 所谓同步,就是在发出一个*调用*时,在没有得到结果之前,该*调用*就不返回。但是一旦调用返回,就得到返回值了。
- 而异步则是相反,*调用*在发出之后,这个调用就直接返回了,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在*调用*发出后,*被调用者*通过状态、通知来通知调用者,或通过回调函数处理这个调用。
阻塞与非阻塞
阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态.
- 阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。
- 非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。
- 同步机制 发送方发送请求之后,需要等接收方发回响应后才接着发
- 异步机制 发送方发送一个请求之后不等待接收方响应这个请求,就继续发送下个请求。
- 阻塞调用调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回,该线程在此过程中不能进行其他处理
- 非阻塞调用调用结果不能马上返回,当前线程也不会被挂起,而是立即返回执行下一个调用。(网络通信中主要指的是网络套接字Socket的阻塞和非阻塞方式,而soket 的实质也就是IO操作)
- 同步阻塞方式 发送方发送请求之后一直等待响应。接收方处理请求时进行的IO操作如果不能马上等到返回结果,就一直等到返回结果后,才响应发送方,期间不能进行其他工作
- 同步非阻塞方式发送方发送请求之后,一直等待响应,接受方处理请求时进行的IO操作如果不能马上的得到结果,就立即返回,取做其他事情。但是由于没有得到请求处理结果,不响应发送方,发送方一直等待。一直等到IO操作完成后,接收方获得结果响应发送发后,接收方才进入下一次请求过程。(实际不应用)
- 异步阻塞方式 发送方向接收方请求后,不等待响应,可以继续其他工作,接收方处理请求时进行IO操作如果不能马上得到结果,就一直等到返回结果后,才响应发送方,期间不能进行其他操作。 (实际不应用)
- 异步非阻塞方式发送方向接收方请求后,不等待响应,可以继续其他工作,接收方处理请求时进行IO操作如果不能马上得到结果,也不等待,而是马上返回取做其他事情。当IO操作完成以后,将完成状态和结果通知接收方,接收方在响应发送方。(效率最高)
基于异步的事件驱动的网络应用框架
事件和ChannelHandler
通过事件通知状态的改变或操作的状态。每个事件都会分发到ChannelHandler的具体实现方法。
每个ChannelHandler都是一种为了响应特定事件而被执行的回调。
选择器、EventLoop
通过事件触发将selector从应用程序中抽象出来,消除手动派发,在内部,将会为每个channel分配一个eventLoop,处理所有事件。EventLoop通过一个线程驱动,处理一个channel的所有I/O事件,并且在整个生命周期不会改变,
ChannelInboundHandler 响应入站事件
基础组件
Channel接口
基本的IO操作(bind()、connect()、read()、write())都是依赖底层网络传输提供的原语,基本的就是Socket。Netty 的 Channel 接口所提供的 API,大大地降低了直接使用 Socket 类的复杂性。
EventLoop接口
用于处理连接生命周期中所发生的事件,
一个 EventLoopGroup 包含一个或者多个 EventLoop ; 一个 EventLoop 在它的生命周期内只和一个 Thread 绑定; 所有由 EventLoop 处理的 I/O 事件都将在它专有的 Thread 上被处理; 一个 Channel 在它的生命周期内只注册于一个 EventLoop。一个 EventLoop 可能会被分配给一个或多个 Channel。
ChannelFuture接口所有的IO都是异步的,因此一个操作可能不会立即返回,所以我们需要一种用于在之后的某个时间点确定其结果的方法,ChannelHandler接口所有处理入站和出站数据的应用程序逻辑容器,他的方法由网络事件触发进行回调,可专门用于几乎任何类型的动作,例如将数据从一种格式转换为另外一种格式,或者处理转换过程中所抛出的异常。ChannelPipeline接口提供了ChannelHandler链的容器,并定义了用于在该链上传播入站和出站事件流的API,当Channel被创建时,会被自动分配到专属的ChannelPipeline。![]()
通过使用作为参数传递到每个方法的 ChannelHandlerContext ,事件可以被传递给当前 ChannelHandler 链中的下一个 ChannelHandler 。因为你有时会忽略那些不感兴趣的事件,所以 Netty 提供了抽象基类 ChannelInboundHandlerAdapter 和 ChannelOutboundHandlerAdapter 。通过调 用 ChannelHandlerContext 上的对应方法,每个都提供了简单地将事件传递给下一个 ChannelHandler 的方法的实现。随后,你可以通过重写你所感兴趣的那些方法来扩展这些类。ChannelHandler的适配类:
- ChannelHandlerAdapter
- ChannelInboundHandlerAdapter
- ChannelOutboundHandlerAdapter
- ChannelDuplexHandler
编码器和解码器
通过 Netty 发送或者接收一个消息的时候,就将会发生一次数据转换。入站消息会被 解码;也就是说,从字节转换为另一种格式,通常是一个 Java 对象。如果是出站消息,则会发生相反方向的转换:它将从它的当前格式被编码 为字节。这两种方向的转换的原因很简单:网络数据总是一系列的字节。启动类Boostrap
建立网络连接的启动类
服务端两个 EventLoopGroup,客户端一个?,服务端需要两个,一个用来只包含ServerChnnel,代表服务器自身的已经绑定到某个本地端口的正在监听的套接字上,第二个包含所有已经创建的用来处理传入客户端连接的Channel。
![]()
当有新连接进入时,会为每个请求创建Channel的EvenLoop,一旦接受请求,就会把它放到第二个EventLoopGroup中。
ChannelPipeline 持有所有将应用于入站和出站数据以及事件的 ChannelHandler 实例,这些 ChannelHandler 实现了应用程序用于处理状态变化以及数据处理的逻辑。
- ChannelHandler 的典型用途包括:
- 将数据从一种格式转换为另一种格式;
- 提供异常的通知;
- 提供 Channel 变为活动的或者非活动的通知;
- 提供当 Channel 注册到 EventLoop 或者从 EventLoop 注销时的通知;
- 提供有关用户自定义事件的通知。
采用 拦截过滤器设计模式
![]()
零拷贝零拷贝(zero-copy)是一种目前只有在使用 NIO 和 Epoll 传输时才可使用的特性。它使你可以快速高效地将数据从文件系统移动到网络接口,而不需要将其从内核空间复制到用户空间。
ByteBuf和ByteBufHolder
优点:
- 它可以被用户自定义的缓冲区类型扩展;
- 通过内置的复合缓冲区类型实现了透明的零拷贝;
- 容量可以按需增长(类似于 JDK 的 StringBuilder);
- 在读和写这两种模式之间切换不需要调用 ByteBuffer 的 flip()方法;
- 读和写使用了不同的索引;
- 支持方法的链式调用;
- 支持引用计数;
支持池化。会维护两个不同的索引:一个用于读,一个用于写,当你从 ByteBuf 读取时,它的 readerIndex 将会被递增已经被读取的字节数。同样地,当你写入ByteBuf 时,它的 writerIndex 也会被递增。
堆缓冲区:
将数据存储在JVM的堆空间,能在没有池化的情况下提供快速的分配和释放。
直接缓冲区:
JVM堆之外的内存区域,直接缓冲区的主要缺点是,相对于基于堆的缓冲区,它们的分配和释放都较为昂贵
复合缓冲区:
多个缓冲区表示为单个合并缓冲区的虚拟表示。
ByteBufAllocator
Unpooled
ByteBufUtil
Channel 的生命周期
ChannelHandler 的生命周期
ChannelInboundHandler ——处理入站数据以及各种状态变化;ChannelOutboundHandler——处理出站数据并且允许拦截所有的操作。
ChannelInboundHandler 接口
ChannelOutboundHandler 接口(推迟操作或事件
ChannelPromise 与 ChannelFutureChannelOutboundHandler 中的大部分方法都需要一个 ChannelPromise参数,以便在操作完成时得到通知。 ChannelPromise 是 ChannelFuture 的一个子类,其定义了一些可写的方法,如setSuccess() 和 setFailure() ,从而使 ChannelFuture 不可变。
ChannelHandler 适配器
资源管理
-Dio.netty.leakDetectionLevel=ADVANCED
ReferenceCountUtil.release(msg);释放资源
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { boolean release = true; try { if (this.acceptInboundMessage(msg)) { this.channelRead0(ctx, msg); } else { release = false; ctx.fireChannelRead(msg); } } finally { if (this.autoRelease && release) { ReferenceCountUtil.release(msg); } } }
ChannelPipeline 接口
ChannelHandlerContext 接口
ChannelHandlerContext 代表了 ChannelHandler 和 ChannelPipeline 之间的关联,每当有 ChannelHandler 添加到 ChannelPipeline 中时,都会创建 ChannelHandlerContext。 ChannelHandlerContext 的主要功能是管理它所关联的 ChannelHandler 和在 同一个 ChannelPipeline 中的其他 ChannelHandler 之间的交互。![]()