第一章 异步+事件驱动:
优势:可以以任意顺序响应在任意时间点产生的事件
第二章 NIO入门
2.1,BIO
同步阻塞,BIO的主要问题是每当有一个新的客户端请求接入时,服务端必须创建一个
新的线程处理,一个线程只能处理一个客户端链接,当有成千上万个客户端的并发连接,
这种模型显然无法满足高性能、高并发接入的场景
2.2 伪异步
优点:将客户端的Socket包装成一个Task(该任务实现了Runnable接口)投递到后端的线程池中进行处理,由于线程池可以设置消息队列的大小和最大线程数,因此资源占用是可控的,无论多少个客户端并发访问,都不会导致资源的耗尽和宕机
缺点:
通过查看输入(InputStream)和输出(OutputStream)分析,他们都是同步阻塞的,阻塞的时间取决于对方IO线程的处理速度和网络IO的传输速度
2.3 NIO(no-block-IO)
2.3.1 缓冲区Buffer
在NIO中,所有数据都是在缓冲区处理的,缓冲区包含了要写入和读取的数据,实质是一个数组,除了内容以外,还包含capacity, limit, and position(具体看BUffer接口解释)
2.3.1 通道Channel
channel是一个通道,网络数据通过channel读取和写入,channel是全双工的,流只是在一个方向上移动(必须是InputStream和OutputStream的子类)
channel可分为两大类,用于网络读写的SelectableChannel和用于文件读写的FileChannel
2.3.2 多路复用器 Seletor
多路复用器提供了选择已经就绪任务的能力,它会不断轮训注册在上面的channel,如果某个channel发生了读或写事件,这个channel就会处于就绪状态,通过SelectionKey就可以获取就绪channel的状态,便于后续操作
JDK使用epoll代替传统的select实现,没有最大句柄的限制,所以一个线程负责selector轮训,就可以处理成千上万的客户端连接
2.3.3 NIO编程的优点
1. 客户端发起的连接操作都是异步的,通过向多路复用器注册OP_CONNECT等待后续结果,不需要像之前那样同步阻塞
2. SocketChannel的读写操作都是异步的,如果没有可读写的数据它不会同步等待,直接返回,不占用IO通信线程的资源
3.线程模型的优化,由于JDK的Selector在Linux等主流操作系统上通过epoll实现,他没有连接句柄数的限制,一个Selector线程可以同时处理成千上万个客户端连接,而且性能不会随着客户端的增加而线性下降
几种IO的对比
补充:
2.4 netty的核心组件
2.4.1 channel
2.4.2 回调
netty在内部使用了回调来处理事件,当一个回调被触发时,相关的事件可以被interface-ChannelHandler的实现处理
2.4.3 Future
netty提供了ChannelFuture,能够注册一个或者多个ChannelFuture实例,监听器的回调方法 operationComplete(),将会在对应的操作完成时被调用,每个netty的出站I/O操作都将返回一个ChannelFuture,也就是说他将不会堵塞
回调和Future是相互补充的机制,他们相互补充,构成了netty本身的关键构件之一
2.4.4事件和ChannelHandler
netty使用不同的事件来通知我们状态的改变或者是操作的状态,每个事件都被某一个ChannelHandler链处理
流经ChannelHandler链的出站和入站事件
2.4.5 选择器 事件 和EventLoop
Netty通过触发事件将Selector从应用程序中抽象出来,消除了所有本来将需要手动编写的
派发代码,在内部,将会为每一个Channel分配一个EventLoop,用于处理所有的事件EventLoop本身只由一个线程驱动,其处理了一个Channel上的所有I/O事件
第三章 ;Netty的组件和设计
3.1 channel接口
基本的IO操作(bind connect read write)依赖于底层网络传输提供的原语(原语:不可中断的机器指令)
在java网络编程中,基本的构造就是class socket
3.2 EventLoop接口
用于处理连接的生命周期中所发生的事件
这些关系是:
- 一个EventLoop在它的生命周期内只和一个Thread绑定
- 所有由EventLoop处理的I/O事件都将在它专有的Thread上被处理
- 一个channel在它的生命周期内只注册于一个EventLoop
- 一个EventLoop可能会被分配给一个或多个Channel
- 一个EventLoopGroup包含一个或者多个EventLoop
注意⚠️:一个给定channel上所有的IO操作都是由相同的Thread执行的,实际上消除了对于同步的需要
3.3 ChannelFuture接口
Netty所有的IO操作都是异步的,Netty提供了ChannelFuture接口,addLIstener()方法注册一个ChannelFutureListener,以便在某个操作完成时得到通知
3.4 ChannelHandler接口
1.充当了所有处理入站和出站数据的应用程序逻辑的容器,ChannelHandler的方法是网络事件触发的
2.Netty以适配器的形式提供了大量默认的chnnelHandler实现,其作用是简化应用程序处理逻辑的开发过程
例如:
ChannelHandlerAdapter
ChannelInboundHandlerAdapter
ChannelOutboundHandlerAdapter
三个ChannelHandler的子类型:
编码器
解码器
抽象类:SimpleChannelInboundHandler(显示的处理特定类型的消息,在
这种类型的ChannelHandler中,最重要的方法是protected abstract void channelRead0(ChannelHandlerContext ctx, I msg) throws Exception;
)
3.5 channelPinline 接口
1. 处理或拦截通道的入站事件和出站操作的ChannelHandlers列表
2. netty使用不同的事件来通知我们状态的改变或者是操作的状态,每个事件都被某一个ChannelHandler链处理
3. 当ChannelHandler被添加到ChannelPipeline时,它将会被分配一个ChannelHandlerContext,其代表了Channelhandler和ChannelPineline之间的约定
3.6 引导
Netty的引导类为应用程序的网络层配置提供了容器
服务器需要两组不同的Channel,第一组只包含一个ServerChannel,代表服务器本身已绑定到本地端口正在监听到套接字,第二组将包含所有已创建的用来处理传入客户端的连接的Channel
第四章 传输
4.2 传输API
如图,每一个Channel都会被分配一个ChannelPipeline和ChannelConfig
Netty的channel是线程安全的,一个EventLoop处理一个Channel
4.3 内置的传输
Netty内置了开箱即用的传输
名称 | 包 | 描述 |
NIO | io.netty.channel.socket.nio | 使用java.nio.channels包作为基础,基于选择器的方式 |
Epoll | io.netty.channel.epoll | https://github.com/netty/netty/wiki/Native-transports 这个传输支持只有在Linux系统上可用的多种特性 |
OIO | io.netty.channel.socket.oio | 使用java.net包作为基础—使用阻塞流 |
Local | io.netty.channel.local | 可以在VM内部使用管道进行通信的本地传输 |
Embedded | io.netty.channel.embedded | Embedded传输,允许使用ChannelHandler而不需要一个真正的基于网络的传输,这在测试ChannelHandler实现时非常有用 |
NIO
零拷贝
零拷贝是一种只有在使用NIO和Epoll传输时才可使用的特性。它可以快速高效地将数据从文件系统移动到网络接口,而不需要将其从内核空间复制到用户空间
Epoll-用户Linux的本地非阻塞传输
epoll—一个高度可拓展的I/O事件通知特性。
Native transports · netty/netty Wiki · GitHub
OIO-旧的阻塞IO
套接字:是应用程序与网络协议栈进行交互的接口
用于JVM内部通信的Local传输
Netty提供了Local传输,用于在同一个JVM中运行的客户端和服务端程序之间的异步通信
第五章
1. ByteBuf-Netty的数据容器
一 . 使用模式
1. 堆缓冲区
将数据存储在JVM的堆空间中,这种模式被称为支撑数组,它能在没有使用池化的情况下提供快速的分配和释放
2.直接缓冲区
NIO在JDK1.4中引入的ByteBuffer类允许JVM实现通过本地调用来分配内存,为了避免缓冲区与直接缓冲区的的复制
3. 复合缓冲期
Netty的独特性能,通过一个ByteBuf的子类- CompositeByteBuf实现了这一模式,它提供了将多个缓冲区和表示为单个合并缓冲区的虚拟表示
二 . 字节级操作
1. 随机访问索引
getByte(int i) 不会改变readerIndex和writeIndex
2. 顺序访问索引
ByteBuf同时具有读索引和写索引,但是在JDK的ByteBuffer却只有一个索引,这就是为什么需要调用flip()来在读模式和写模式切换的原因
3. 可丢弃字节
可以调用discardReadBytes()方法,可以丢弃它们并回收空间
注意:频繁的调用discardReadBytes()方法,可能导致内存复制,因为可读字节必须被移动到缓冲区的开始位置,建议只有在真正需要的时候才这样做
4. 索引管理
可以调用markReaderIndex()、markWriteIndex()、resetWriterIndex()和resertReaderIndex()来标记和重置ByteBuf的readIndex和writeIndex
可以调用clear()方法将readIndex和writeIndex都设置为0
5. 查找
public abstract int forEachByte(ByteProcessor processor);
6. 派生缓冲区
特点:
1. 每个方法返回一个新的ByteBuf实例,它具有自己的读索引、写索引和标记索引
2. 修改了它的内容,也同时修改了对应的源实例
方法:
duplicate();
slice();
slice(int,int);
order(ByteOrder);
readSlice(int);
io.netty.buffer.Unpooled.unmodifiableBuffer(io.netty.buffer.ByteBuf)
二. 真实复制:
copy()或者copy(int,int)方法,这个调用返回的ByteBuf拥有独 立的数据副本
三 . ByteBufHolder接口
除了实际的数据负载之外,我们还需要存储各种属性值,比如HTTP中的 cookle、状态码等,为了处理这种常见的用例,netty提供了ByteBufHolder
四. ByteBuf分配
1. 按需分配 ByteBufAllocator接口
Netty提供了两种实现,PooledByteBufAllocator(默认)和 UnpooledByteBufAllocator,前者池化了ByteBuf的实例,以提高系统性能最大限度的减少内存碎片,后者的实现不池化ByteBuf实例,每次调用都返回一个新的实例
2. Unpooled缓冲区
Netty提供一个Unpooled工具类,提供了静态的辅助方法 来创建非池化的ByteBuf实例
五. ByteBufUtil工具类
hexDump()方法,以十六进制的表示形式打印ByteBuf内容
boolean equals(ByteBuf,ByteBuf) 用来判断两个ByteBuf实例的相 等性
六. 引用计数
引用计数是一种通过在某个对象所持有的资源不再被其他对象引用时释放该对象所持有的资源来优化内存使用和性能的技术
ByteBuf和ByteBufHolder引入了引用计数技术,他们都实现了ReferenceCounted接口
一个ReferenceCounted接口实现的实例将通常以引用计数为1开始,retain()增加,release()减少,当为0时,该实例就会被释放
第六章 ChannelHandler和ChannelPipline
6.1 channelHandler家族
6.1.1 Channel的生命周期
状态 | 描述 |
ChanelUnregistered | Channel已经被创建,但还未注册到EventLoop |
ChannelRegistered | Channel已经被注册到EventLoop |
ChannelActive | Channel是活跃状态(已经连接到它的远程节点),现在它可以接收和发送数据了 |
ChannelInactive | Channel没有连接到远程节点 |
Chanel状态模型
6.1.2 ChannelHandler的生命周期
类型 | 描述 |
handlerAdded(ChannelHandlerContext ctx) | 当把ChannelHandler添加ChannePieline中时被调用 |
handlerRemoved(ChannelHandlerContext ctx) | 当从ChannelPipeline中移除ChanneHandler时被调用 |
6.1.3 ChannelInboundHandler接口
ChannelInboundHandler接口定义了生命周期方法,这些方法会在数据被接收时或者起对应的Channel状态改变时被调用
注意:当某个ChannelInboundHandler的实现重写channelRead()方法时,它将显示的释放ByteBuf实例相关的内存,Netty为此提供了一个实用方法 ReferenceCountUtil.release()
Netty 实用Warn级别的日志记录未释放的资源,一个简单的方法是直接使用SimpleChannel InboundHandler,会自动的释放资源
6.1.4 ChannelOutboundHandler接口
出站操作和数据ChannelOutboundHandler处理,它的方法将被Channel、ChannelPipeline以及ChannelHandlerContext调用
6.1.5 ChannelHandler适配器
- channelInboundhandlerAdapter和channelOutboundHandlerAdapter提供了基本实现,他们都拓展了抽象类channelHandlerAdapter,这个类提供了一个实用的方法isSharable(),其对应的实现被标注为Sharable,这个方法返回true,表示可以被添加到多个ChannePineLine中
- 这两个类所提供的方法中调用了其关联的ChannelHandlerContext方法,从而将事件转发到ChannelPipeline中的下一个Channel Handler中
6.1.5 资源管理
为了帮助诊断潜在的(资源泄漏)问题,Netty提供了class ResourceLeakDetector 默认它将对你的应用程序的缓冲区分配做大约1%的采样来检测内存泄漏
Netty定义了4种泄漏检测级别
原则:如果一个消息被消费或者丢弃了,并且没有传递给channelPipeline中的下一个ChannelHandler,则用户有责任调用ReferenceCountUtil.release()方法
6.2 ChannelPipeline接口
根据事件的起源,事件将会被ChannelInboundHandler或者ChannelOutboundHandler处理。随后,通过调用ChannelHandlerContext实现,它将会被转发给同一超类型的下一个ChannelHandler
- 修改ChannelPipeline
ChanelHandler可以通过添加、删除或者替换其他的ChannelHandler来实时修改ChannelPipeline的布局
ChannelHandler的执行与阻塞:
- 通常ChannelPipeline中每一个ChannelHandler都是通过它的EventLoop(I/O线程)来处理它的事件的,所以至关重要的是不要阻塞这个线程,因为这会对整体的I/O处理产生负面的影响
- 但有时需要与那些阻塞API的遗留代码进行交互,对于这种情况,ChannelPipeline有一些接受了EventExecutorGroup的add()方法,如果一个事件被传递给一个自定义的EventExecutorGroup,它将会被包含在这个EventExcutorGroup中的某个EventExcutor处理,从而在Channel本身的EventLoop中移除。对于这种用例,Netty提供了一个叫DefaultEventExcutorGroup的默认实现
如:
ChannelPipeline addFirst(EventExecutorGroup group, ChannelHandler... handlers);
总结:
- ChannelPipeline保存了与Channel相关联的ChannelHandler
- Channel Pipeline可以根据需要,通过添加或删除ChannelHandler来动态的修改
- Channel Pipeline有丰富的API用以被调用,以相应入站和出站事件
6.3 ChannelHandlerContext接口
- 这个接口代表了ChannelHandler和ChannelPipeline之间的关联,每当有ChannelHandler加入到ChannelPipeline中时,都会创建ChannelHandlerContext
- ChanelhandlerContext有很多方法,其中一些方法也存在于Channel和ChannelPipeline身上,不同点是:调用Channel或者ChannelPipeline上点这些方法,会沿着整个Pipeline传播,但是如果调用ChannelHandlerContext上相同的方法,则将从当前关联的ChannelHandler开始,只传播给能够处理该事件的ChannelHandler
- ChannelHandlerContext和ChannelHandler之间的关联是永远不会改变的,所以缓存对它的作用是安全的
注意:只有在确定了你的ChannelHandler是线程安全时才使用@Sharable注解
6.4 异常处理
处理入站异常
如果入站事件的过程中有异常被抛出,会沿着ChannelPIPeline传播,要想处理这种入站异常,需要在你的ChannelInboundHandler中重写下面的方法
void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;
通常实现这个逻辑的handler在ChannelPipeline的最后,这确保了所有的入站异常都会被处理
处理出站异常
- 每一个出站操作都会返回一个ChanneFuture,注册到ChannelFuture的ChannelListener都将会在操作完成时被通知该操作是成功了还是出错了
- 几乎所有的ChannelOutboundHandler上的方法都会传入一个ChannelPromise的实例,作为channelFuture的子类,ChannelPromise也可以被分配用于异步通知的监听器,但是,ChannelPromise还具有提供立即通知的可写方法:
Promise<V> setSuccess(V result);
Promise<V> setFailure(Throwable cause);
第7章 EventLoop和线程模型
———————————————————————————————————
前言:
虽然池化和重用线程相对于简单地为每一个任务都创建和销毁线程是一种进步,但是并不能消除上下文切换所带来的开销,并且随着线程数量的增加变得明显
7.2 EventLoop接口
运行任务来处理
运行任务来处理连接的生命周期内发生的事件,Netty使用了 EventLoop来适配
特点:
- 并发API,继承了EventExecutor,构件在java.util.concurrent包下,用来执行线程执行器
- 网络编程API,继承EventLoopGroup,这个类为了与Channel的事件进行交互,拓展了这些接口/类
- 一个EventLoop永远由一个不会改变的Thread驱动,同时任务(Runnable或者Callable)可以直接提交给EventLoop实现,以立即执行或者调度执行
7.3 任务调度
7.3.1使用EventLoop调度任务
- ScheduledExecutorService的实现具有局限性,调度任务时会有额外的线程创建,如果有大量的任务被紧凑的调度,那么将成为一个瓶颈
- Netty线程模型的卓越性能取决于对于当前执行Thread的身份的确定,也就是确定是否是分配给当前Channel和它的EventLoop的那一个线程
- 如果是,则会被立刻执行,都则会放到该EventLoop的内部队列,以便稍后执行
7.3.2使用EventLoop线程的分配
1.异步传输
使用少量的EventLoop,他们可能被多个Channel所共享,少量的Thread就可以支撑大量的channel,而不是每个Channel分配一个Thread ,EventLoopGroup负责为每一个新创建的Channel分配一个EventLoop
使用非阻塞传输(NIO和AIO)的EventLoop的分配方式
2.阻塞传输
- 特点:每一个channel的IO事件都会被一个Thread处理
阻塞传输(如OIO)的EventLoop分配方式
第八章:引导
定义:引导一个应用程序是指对它进行配置,并使它运行起来的过程
服务器致力于使用一个父channel来接受客户端的连接,并创建子channel来用于他们之间的通信,而客户端只需要一个channel来用于网络交互
1. AbstractBootstrap:通用的引导步骤,而特定于客户端或服务端的引导步骤分别由Bootstrap或ServerBootstrap处理
Bootstrap:
- Bootstrap类被用于客户端和无连接协议的应用程序
- 引导客户端
- channel和EventLoopGroup的兼容性
在io.netty.channel包下,对与NIO和OIO来说,都有相关的EventLoopGroup和Channel实现
相互混用会发生IllegalStateException
注:
在引导的过程中,在调用bind()和connect()方法之前,必须调用以下的方法来设置所需的组件
- group()
- channel()
- handler()
ServerBootStrap类
.编写netty程序的准则:
尽可能的重用EventLoop,以减少线程创建所带来的开销
假设服务器正在处理一个客户端连接,并且这个请求需要充当第三方系统的客户端
两个channel之间共享EventLoop
8.5在引导过程中添加多个ChannelHandler
- Netty提供了一个特殊的ChanelnboundHandlerAdapter的子类
ChannelInitializer
它定义了下面的方法
protected abstract void initChannel(Channel ch) throws Exception;
一旦channel注册到它的EventLoop之后,它将会调用你的initChannel版本
8.6使用netty的ChannelOption和属性
作用:对channel属性进行配置,并应用到引导
- public <T> B option(ChannelOption<T> option, T value) ;
- 当某些属性和数据不可用时,Netty使用了AttributeMap抽象以及AttributeKey
- 使用这些工具,便可以安全的将任何类型的数据项与客户端和服务器Channel相关联了
8.7 引导DatagramChannel(A UDP/IP Channel)
- BootStrap类也可用于无连接的协议,Netty提供了DatagramChannel的实现,唯一区别是不在调用connect方法,而是调用bind()方法
8.8 优雅关闭
- 最重要的是:需要关闭EventLoopGroup,它将处理任何挂起的时间和任务,并且随后释放所有活动的线程
- 调用 Future<?> shutdownGracefully();是一个异步操作,所以需要阻塞等待它完成,或者向所返回的Future注册一个监听器以在关闭完成时获得通知
第九章 单元测试
EmbeddedChannel: Netty专门为改进针对ChannelHandler的单元测试而提供的,同时可以try/catch(如果未重写exceptionCaught方法对前提下),方便对异常测试
EmbeddedChannel的数据流
第十章 编解码器
背景:编码器作出站数据,解码器处理入站数据
10.2 解码器
- 解码器分为两种
- 将字节解码为消息—> ByteToMessageDecoder和ReplayingDecoder
- 将消息类型解码为另一 —>MessageToMessageDecoder
- 使用时机:当需要为ChannelPipeline中的下一个ChannelInboundHandler转换数据时会用到
ByteToMessageDecoder
注意:引用计数需要特别注意,对于编码器和解码器来说,一旦消息被编码或者解码,它就会被ReferenceCountUtil.release(message)调用自动释放,如果需要保留引用便于稍后使用,可以调用ReferenceCountUtil.retain(message)方法。这将会增加引用计数,防止该消息被释放
ReplayingDecoder
- public abstract class ReplayingDecoder<S> extends ByteToMessageDecoder;
- 参数S代表了状态管理的类型,Void代表不需要状态管理
- 它通过一个自定义的ByteBuf实现—RePlayingDecoderByteBuf,包装传入的ByteBuf实现了这一点,将在内部执行时被调用
- 使用这个类我们不需要调用readableBytes()方法,因为在没有足够的字节可用时,将会抛出一个Error(Signal类),其将会在基类中捕获并处理
- LineBasedFrameDecoder:使用行尾控制字符(\n或者\r\n)来解析数据
HttpObjectDecoder:一个HTTP数据的解码器
MessageToMessageDecoder<T>
有关更加复杂的例子,研究io.netty.handler.codec.http.HttpObjectAggregator
TooLongFrameException类
- Netty是一个异步框架,需要在字节解码之前在内存中缓冲它们,为防止大量数据耗尽可用的内存,Netty提供了TooLongFrameException类,由解码器在帧超出指定的大小限制时抛出
10.3 编码器
MessageToByteEncoder
MessageToMessageEncoder
package io.netty.handler.codec.protobuf.ProtobufEncoder,它处理了Google的Protocol Buffers规范所定义的数据格式;
10.4 编解码器
ByteToMessageCodec
MessageToMessageCodec
CombinedChannelDuplexHandler:编解码器结合
public class CombinedChannelDuplexHandler<I extends ChannelInboundHandler, O extends ChannelOutboundHandler>;
第十一章 预置的ChannelHandler和编解码器
11.2 构建基于Netty的Http/Https应用程序
11.2.1 HTTP编码器、解码器和编解码器
名称 | 描述 |
HttpRequestEncoder | 将HttpRequest、HttpContent和LastHttpContent消息编码为字节 |
HttpResponseEncoder | 将HttpResponse、HttpContent和LastHttpContent消息编码为字节 |
HttpRequestDecoder | 将字节解码为HttpRequest、HttpContent和LastHttpContent消息 |
HttpResponseDecoder | 将字节解码为HttpResponse、HttpContent和LastHttpContent消息 |
11.2.2 聚合HTTP消息
背景:因为HTTP的请求和响应由许多部分组成,你需要聚合它们形成完整的消息,将多个消息部分合并为FullHttpRequest和FullHttpResponse消息
解决方法:引入一个ChannelInboundHandler:
HttpObjectAggregator
Client:HttpClientCodec+HttpObjectAggregator
Server:HttpServerCodec+HttpObjectAggregator
11.2.3 压缩
客户端指定服务端它所支持的压缩格式:
Accept-Encoding: gzip, deflate
加密:HttpContentCompressor
解密:HttpContentDeCompressor
11.2.4 HTTPS
启用HTTPS只需要将SslHandler添加到ChannelPipeline中
11.2.5 WebSocket
11.3 空闲的连接和超时
名称 | 描述 |
IdleStateHandler | 当空闲连接太长时,将会触发一个IdelStateEvent事件,然后在下一个ChannelInboundHandler中重写userEventTriggered()方法来处理该IdleStateEvent事件 |
ReadTimeoutHandler | 如果在指定的时间间隔内没有收到任何的入站数据,则抛出一个ReadTimeoutException并关闭对应的Channel。可以通过重写Channelhandler中的exceptionCaught()方法来检测该ReadTimeoutException |
WriteTimeoutHandler | 如果为指定的时间间隔内没有任何出站数据写入,则抛出WriteTimeoutException并关闭对应的Channel |
注:如果连接没有在指定的时间内有读写数据,那么IdleStateHandler将会使用IdleStateEvent事件调用fireUserEventTriggered()方法
11.4 解码基于分隔符与长度的协议
基于分隔符
名称 | 描述 |
DelimiterBasedFrameDecoder | 使用任何用户提供的分隔符来提取帧的通用解码器 |
LineBasedFrameDecoder | 提取由行尾符(\n或\r\n)分割的帧的解码器,这个解码器比DelimiterBasedFrameDecoder更快 |
基于长度的协议
名称 | 描述 |
FixedLengthFrameDecoder | 提取在调用构造函数时指定的定长帧 |
LengthFieldBasedFrameDecoder | 根据编码进帧头部中的长度值提取帧;该字段的偏移量以及长度在构造函数中指定 |
11.5 写大型数据
- 直接复制:FileRegion接口,适用于文件的直接复制
- 需要将数据从文件系统复制到用户内存:使用ChunkedWriteHandler,它支持异步写大型数据流,而不会有大量的内存消耗 关键是Interface ChunkedInput<B> ,Netty预制了该接口的4个实现,每个都代表将由ChunkedWriteHandler处理的不定长度的数据流
11.6 序列化数据
11.6.1. JDK序列化 ,在netty 3.1时已废弃
11.6.2 使用JBoss Marshalling进行序列化
优势:比JDK序列化最多快3倍,而且更加紧凑
官网:
JBoss Marshalling - JBoss Community
名称 | 描述 |
CompatibleMarshallingDecoder CompatibleMarshallingEncoder | 与只使用JDK序列化的远程节点兼容 |
MarshallingDecoder MarshallingEncoder | 适用于使用JBoss Marshalling的节点,这些类必须一起使用 |
11.6.3 使用Protocol Buffers进行序列化
介绍:一个由Google公司开发的、现在已经开源的数据交换形式,它以一种紧凑而高效的方式对结构化的数据进行编码和解码,它有很多语言绑定,使得它很适合跨语言的项目
GitHub - protocolbuffers/protobuf: Protocol Buffers - Google's data interchange format
(171条消息) 22 - 高并发- 序列化Protobuf协议通讯_良之才-小良的博客-CSDN博客
名称 | 描述 |
ProtobufDecoder | 使用protobuf对消息进行解码 |
ProtobufEncoder | 使用protobuf对消息编码 |
ProtobufVarint32FrameDecoder | 根据消息中的Google Protocol Buffers的“Base 128 Varints”整形长度字段值动态的分割所接受到的ByteBuf |
ProtobufVarint32LengthFieldPrepender | 在ByteBuf前追加一个Google Protocal Buffers的“Base 128 Varints” 整型的长度字段值 |
第十二章 WebSocket(TODO)
实时web 利用技术和时间,使用户在信息的作者发布信息之后就能够立即收到信息,而不需要他们或者他们的软件周期性的检查信息源以获取更新
第十三章 使用UDP广播事件
一. UDP除了支持单播传播模式,还支持多播—传输到一个预定义的主机组,广播—传输到网络(或者子网)上的所有主机,可以使用受限网络地址255.255.255.255,发送到这个地址的消息都会被定向给本地网络(0.0.0.0)上的所有主机,而不会被路由器转发给其他的网络
名称 | 描述 |
interface AddressedEnvelope<M,A> | 定义一个消息,其包装了另一个消息并带有发送者和接受者地址 M:消息类型 A:消息地址 |
DefaultAddressedEnvelope | 提供了interface AddressedEnvelope的默认实现 |
DatagramPacket extends DefaultAddressedEnvelope<ByteBuf, InetSocketAddress> implements ByteBufHolder | 拓展了DefaultAddressedEnvelope以使用ByteBuf作为消息数据容器 |
interface DatagramChannel extends Channel | 拓展了Netty的Channel抽象以支持UDP多播组管理 |
NioDatagramChannel extends AbstractNioMessageChannel implements io.netty.channel.socket.DatagramChannel | 定义一个能够发送和接收AddressedEnvelope消息的Channel类型 |