背景
Netty提供了很多内置的处理器,高效地利用这些处理器,可以经过简单的配置就可以实现部分复杂功能,而不是自己花时间和精力去重复造“轮子”。
要使用,得先知道,本文主要依据《netty实战》一书进行的梳理,结合了实际应用经验进行了整理。
解码器
ByteToMessageDecoder:是最基础的解码器,将字节数组解析为消息。
ReplayingDecoder:继承自ByteToMessageDecoder,使得我们不必调用readableBytes()方法来判断是否有足够的数据可被读取。
LineBasedFrameDecoder:使用了行尾控制字符(\n 或者\r\n)来解析消息数据。
MessageToMessageDecoder:将一种消息的格式转换为另外一种。
HttpObjectAggregator:同样是解码器的一个子类,非常复杂,处理http请求。
注意:由于Netty 是一个异步框架,所以需要在字节可以解码之前在内存中缓冲它们。因此,不能让解码器缓冲大量的数据以至于耗尽可用的内存。为了解除这个常见的顾虑,Netty 提供了TooLongFrameException 类,其将由解码器在帧超出指定的大小限制时抛出。
编码器
MessageToByteEncoder:最基本的编码器,将消息编码为字节数组。
MessageToMessageEncoder:将一种消息的格式转换为另外一种。
编解码器
编解码器可以在同一个类中管理入站和出站数据和消息的转换。
ByteToMessageCodec:基类,同时实现了ChannelInboundHandler 和ChannelOutboundHandler 接口,但不是直接实现,而是间接实现。
public abstract class ByteToMessageCodec extends ChannelDuplexHandler
public class ChannelDuplexHandler extends ChannelInboundHandlerAdapter implements ChannelOutboundHandler
MessageToMessageCodec:消息间的编解码。
从重用的角度来说,将编码和解码放到一个类中管理会丧失灵活性,这里有一种途径来解决该问题,使用CombinedChannelDuplexHandler。这个类充当了ChannelInboundHandler 和ChannelOutboundHandler(该类的类型参数I 和O)的容器。
public class CombinedByteCharCodec extends CombinedChannelDuplexHandler<ByteToCharDecoder, CharToByteEncoder> {
public CombinedByteCharCodec() {
super(new ByteToCharDecoder(), new CharToByteEncoder());
}
}
安全传输
SslHandler:用于加密传输。
Http请求
Http编解码器
HttpServerCodec:服务端,进行http编码和解码,实际是HttpRequestDecoder和HttpResponseEncoder两个的组合。
HttpClientCodec:客户端,进行http编码和解码,实际是HttpRequestEncoder和HttpResponseDecoder两个的组合。
这里用到了我们上章提到的复合编解码器的CombinedChannelDuplexHandler
public final class HttpClientCodec
extends CombinedChannelDuplexHandler<HttpResponseDecoder, HttpRequestEncoder>
implements HttpClientUpgradeHandler.SourceCodec
Http聚合器
HttpObjectAggregator:http消息通常是多部分组成,以请求为例,通常由HttpHeader、HttpContent(1个或多个)、LastHttpContent,该处理器可以将tcp多次传输的结果组合为一个完整的http请求。
Http压缩解压器
压缩与解压是为了提高性能。
HttpContentCompressor,放在服务端。
HttpContentDecompressor,放在客户端。
WebSocket处理器
WebSocketServerProtocolHandle:负责处理服务端的握手和控制(Ping、Pong、Close),不负责TextWebSocketFrame、BinaryWebSocketFrame和ContinuationWebSocketFrame。
空闲的连接和超时
IdleStateHandler:空闲检测,通常用于心跳保持,可分别设置读空闲、写空闲和读写都空闲的时间。
pipeline.addLast(new IdleStateHandler(5,0,0, TimeUnit.SECONDS));
如超出后,可触发空闲事件,由下个处理器在userEventTriggered中处理:
public class HeartbeatTimeoutHandler extends ChannelInboundHandlerAdapter {
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if(evt instanceof IdleStateEvent){
IdleStateEvent event=(IdleStateEvent)evt;
if(event.state().equals(IdleState.READER_IDLE)){
log.info("读空闲");
}
}else{
//非空闲事件,传递到下个处理器
super.userEventTriggered(ctx,evt);
}
}
}
netty不仅内置了灵活性最大的IdleStateHandler,实际还内置了两个可以直接使用的读超时和写超时处理器,对于心跳机制,如果是客户端发心跳请求,服务端发心跳响应,那么我们完全可以直接使用ReadTimeoutHandler,而不需要使用IdleStateHandler+自定义处理器实现。
解码基于分隔符的协议和基于长度的协议
DelimiterBasedFrameDecoder:使用任何由用户提供的分隔符来提取帧的通用解码器。
LineBasedFrameDecoder:提取由行尾符(\n 或者\r\n)分隔的帧的解码器。
FixedLengthFrameDecoder:提取在调用构造函数时指定的定长帧。
LengthFieldBasedFrameDecoder:根据编码进帧头部中的长度值提取帧;该字段的偏移量以及长度在构造函数中指定。
写大型数据
如果是直接传输文件内容,不做处理,建议使用FileRegion来实现,充分利用零拷贝特性。
如还需要对文件内容处理,则netty内置了ChunkedWriteHandler,不会消耗大量内存,但是需要结合以下类使用: