文章目录
前言
我们一般定义一个Handler,在channelRead0()
的入参中,有ChannelHandlerContext
对象ctx:
public class HttpServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
@Override
protected void channelRead0(ChannelHandlerContext ctx,
FullHttpRequest request) throws Exception {
if (!request.decoderResult().isSuccess()) {
sendError(ctx, BAD_REQUEST);
return;
}
System.out.println("Http Server receive the request : " + request);
ByteBuf body = request.content().copy();
FullHttpResponse response = new DefaultFullHttpResponse(
HTTP_1_1, HttpResponseStatus.OK, body);
response.headers().set(HttpHeaderNames.CONTENT_LENGTH, body.readableBytes());
ctx.writeAndFlush(response).sync();
System.out.println("Http Server send response succeed : " + response);
}
ChannelHandlerContext 是什么东西呢?
ChannelHandlerContext (子类类是AbstractChannelHandlerContext)其实是一个双向链表,对应一个ChannelPipeline,它是ChannelPipeline的工具,负责管理一组Handler。
1. ChannelHandler
ChannelHandler下主要是两个子接口:
-
ChannelInboundHandler(入站): 处理输入数据和Channel状态类型改变。
适配器: ChannelInboundHandlerAdapter(适配器设计模式) 常用的: SimpleChannelInboundHandler
-
ChannelOutboundHandler(出站): 处理输出数据
适配器: ChannelOutboundHandlerAdapter
作用是负责处理业务。
2. ChannelPipeline
ChannelPipeline类是ChannelHandler实例对象的链表,用于处理或截获通道的接收和发送数据。它提供了一种高级的截取过滤模式(类似serverlet中的filter功能),让用户可以在ChannelPipeline中完全控制一个事件以及如何处理ChannelHandler与ChannelPipeline的交互。
对于每个新的通道Channel(一个通道就是一个客户端发起的连接),都会创建一个新的ChannelPipeline,并将器pipeline附加到channel中。
下图描述ChannelHandler与pipeline中的关系,一个io操作可以由一个ChannelInboundHandler或ChannelOutboundHandle进行处理,并通过调用ChannelInboundHandler处理入站io或通过ChannelOutboundHandler处理出站IO。
3. ChannelHandlerContext
ChannelPipeline
并不是直接管理ChannelHandler
,而是通过ChannelHandlerContext
来间接管理,这一点通过ChannelPipeline的默认实现DefaultChannelPipeline可以看出来。
当然,存在继承链,ChannelHandlerContext、AbstractChannelHandlerContext、DefaultChannelPipeline
DefaultChannelHandlerContext和DefaultChannelPipeline是ChannelHandlerContext和ChannelPipeline的默认实现。
3.1 AbstractChannelHandlerContext
// 组成双向链表
volatile AbstractChannelHandlerContext next;
volatile AbstractChannelHandlerContext prev;
3.2 DefaultChannelHandlerContext
DefaultChannelHandlerContext
组成了一个双向链表。 我们看下DefaultChannelPipeline的构造函数:
/**
* 可以看到,DefaultChinnelPipeline 内部使用了两个特殊的Hander 来表示Handel链的头和尾。
*/
public DefaultChannelPipeline(AbstractChannel channel) {
if (channel == null) {
throw new NullPointerException("channel");
}
this.channel = channel;
//尾节点
TailHandler tailHandler = new TailHandler();
tail = new DefaultChannelHandlerContext(this, null, generateName(tailHandler), tailHandler);
//头节点
HeadHandler headHandler = new HeadHandler(channel.unsafe());
head = new DefaultChannelHandlerContext(this, null, generateName(headHandler), headHandler);
head.next = tail;
tail.prev = head;
}
所以对于DefaultChinnelPipeline它的Handel头部和尾部的Handel是固定的,我们所添加的Handel是添加在这个头和尾之前的Handel。(下面这个图更加清晰):
4. 几者关系
先大致说下什么是Channel
通常来说, 所有的 NIO 的 I/O 操作都是从 Channel 开始的. 一个 channel 类似于一个 stream。在Netty中,Channel是客户端和服务端建立的一个连接通道。
虽然java Stream 和 NIO Channel都是负责I/O操作,但他们还是有许多区别的:
1)我们可以在同一个 Channel 中执行读和写操作, 然而同一个 Stream 仅仅支持读或写。
2)Channel 可以异步地读写, 而 Stream 是阻塞的同步读写。
3)Channel 总是从 Buffer 中读取数据, 或将数据写入到 Buffer 中。
几者的关系图如下:
总结:
一个Channel包含一个ChannelPipeline,创建Channel时会自动创建一个ChannelPipeline,每个Channel都有一个管理它的pipeline,这关联是永久性的。
这点从源码中就可以看出,我之前写的博客里有说到:【Netty】5 源码 Bootstrap。每一个ChannelPipeline中可以包含多个ChannelHandler。所有ChannelHandler
都会顺序加入到ChannelPipeline中,ChannelHandler实例与ChannelPipeline之间的桥梁是ChannelHandlerContext实例。
参考
【Netty】(8)—理解ChannelPipeline
Netty源码_AbstractChannelHandlerContext详解