Pipeline Handler HandlerContext 创建源码剖析
ChannelPipeline 调度 handler 的源码剖析
源码剖析目的
Netty 中的 ChannelPipeline 、 ChannelHandler 和 ChannelHandlerContext 是非常核心的组件, 我们从源码来分析Netty 是如何设计这三个核心组件的,并分析是如何创建和协调工作的.
ChannelPipeline
| ChannelHandler
| ChannelHandlerContext
介绍每当 ServerSocket 创建一个新的连接,就会创建一个 Socket,对应的就是目标客户端。
每一个新创建的 Socket 都将会分配一个全新的 ChannelPipeline(以下简称 pipeline)
每一个 ChannelPipeline 内部都含有多个 ChannelHandlerContext(以下简称 Context)
他们一起组成了双向链表,这些 Context 用于包装我们调用 addLast 方法时添加的 ChannelHandler(以下简称 handler)
可以看到该接口继承了 inBound,outBound,Iterable 接口,表示他可以调用数据出站的方法和入站的方法,同时也能遍历内部的链表, 看看他的几个代表性的方法,基本上都是针对 handler 链表的插入,追加,删除,替换操作,类似是一个 LinkedList。同时,也能返回 channel(也就是 socket)
1)在 pipeline 的接口文档上,提供了一幅图
数据流向pipeline 是入栈,流出pipeline 是出栈
对上图的解释说明:
这是一个 handler 的 list,handler 用于处理或拦截入站事件和出站事件,pipeline 实现了过滤器的高级形式,以便用户控制事件如何处理以及 handler 在 pipeline 中如何交互。
上图描述了一个典型的 handler 在 pipeline 中处理 I/O 事件的方式,IO 事件由 inboundHandler 或者 outBoundHandler 处理,并通过调用ChannelHandlerContext.fireChannelRead
方法转发给其最近的handler 处理程序 。
入站事件由入站处理程序以自下而上的方向处理,如图所示。入站处理程序通常处理由图底部的I / O线程生成入站数据。入站数据通常从如SocketChannel.read(ByteBuffer) 获取。
通常一个 pipeline 有多个 handler,例如,一个典型的服务器在每个通道的管道中都会有以下处理程序
协议解码器 - 将二进制数据转换为Java对象。
协议编码器 - 将Java对象转换为二进制数据。
业务逻辑处理程序 - 执行实际业务逻辑(例如数据库访问)
你的业务程序不能将线程阻塞,会影响 IO 的速度,进而影响整个 Netty 程序的性能。如果你的业务程序很快,就可以放在 IO 线程中,反之,你需要异步执行。或者在添加 handler 的时候添加一个线程池,
例如:
// 下面这个任务执行的时候,将不会阻塞 IO 线程,执行的线程来自 group 线程池
pipeline.addLast(group,“handler”,new MyBusinessLogicHandler());
或者放taskQueue或者scheduleTaskQueue中中
public interface ChannelHandler {
//当把 ChannelHandler 添加到 pipeline 时被调用
void handlerAdded(ChannelHandlerContext ctx) throws Exception;
//当从 pipeline 中移除时调用
void handlerRemoved(ChannelHandlerContext ctx) throws Exception;
// 当处理过程中在 pipeline 发生异常时调用
@Deprecated
void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;
}
ChannelHandler 的作用就是处理 IO 事件或拦截 IO 事件,并将其转发给下一个处理程序 ChannelHandler。
Handler 处理事件时分入站和出站的,两个方向的操作都是不同的,因此,Netty 定义了两个子接口继承 ChannelHandler
2)ChannelInboundHandler
入站事件接口
channelActive 用于当 Channel 处于活动状态时被调用;
channelRead 当从Channel 读取数据时被调用等等方法。
程序员需要重写一些方法,当发生关注的事件,需要在方法中实现我们的业务逻辑,因为当事件发生时,Netty 会回调对应的方法。
3)ChannelOutboundHandler
出站事件接口
4)ChannelDuplexHandler
处理出站和入站事件
1)ChannelOutboundInvoker
和 ChannelInboundInvoker
部分源码
ChannelPipeline
| ChannelHandler
| ChannelHandlerContext
创建过程分为3个步骤来看创建的过程:
任何一个 ChannelSocket 创建的同时都会创建 一个 pipeline。
当用户或系统内部调用 pipeline 的 add*** 方法添加 handler 时,都会创建一个包装这 handler 的 Context。
这些 Context 在 pipeline 中组成了双向链表。
2.1 Socket 创建的时候创建 pipeline;在 SocketChannel 的抽象父类 AbstractChannel 的构造方法中
protected AbstractChannel(Channel parent) {
this.parent = parent; //断点测试
id = newId();
unsafe = newUnsafe();
pipeline = newChannelPipeline();
}
Debug 一下, 可以看到代码会执行到这里, 然后继续追踪到
protected DefaultChannelPipeline(Channel channel) {
this.channel = ObjectUtil.checkNotNull(channel, "channel");
succeededFuture = new SucceededChannelFuture(channel, null);
voidPromise = new VoidChannelPromise(channel, true);
tail = new TailContext(this);
head = new HeadContext(this);
head.next = tail;
tail.prev = head;
}
说明:
1) 将 channel 赋值给 channel 字段,用于 pipeline 操作 channel。
2) 创建一个 future 和 promise,用于异步回调使用。
3) 创建一个 inbound 的 tailContext
,创建一个既是 inbound 类型又是 outbound 类型的 headContext
.
4) 最后,将两个 Context 互相连接,形成双向链表。
5) tailContext 和 HeadContext 非常的重要,所有 pipeline 中的事件都会流经他们,
2.2 在 add** 添加 Handler 处理器的时候创建 Context** 看下 DefaultChannelPipeline 的 addLast 方法如何创建的 Context,代码如下
@Override
public final ChannelPipeline addLast(EventExecutorGroup executor, ChannelHandler... handlers) {
if (handlers == null) { //断点
throw new NullPointerException("handlers");
}
for (ChannelHandler h: handlers) {
if (h == null) {
break;
}
addLast(executor, null, h);
}
return this;
}
继续Debug
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
final AbstractChannelHandlerContext newCtx;
synchronized (this) {
checkMultiplicity(handler);
newCtx = newContext(group, filterName(name, handler), handler);//
addLast0(newCtx);
if (!registered) {
newCtx.setAddPending();
callHandlerCallbackLater(newCtx, true);
return this;
}
EventExecutor executor = newCtx.executor();
if (!executor.inEventLoop()) {
newCtx.setAddPending();
executor.execute(new Runnable() {
@Override
public void run() {
callHandlerAdded0(newCtx);
}
});
return this;
}
}
callHandlerAdded0(newCtx);
return this;
}
说明
newContext(group, filterName(name, handler), handler)
方法,创建一个 Context。从这里可以看出来了,每次添加一个 handler 都会创建一个关联 Context。ChannelPipeline
是如何调度 handler 的源码剖析
DefaultChannelPipeline 是如何实现这些 fire 方法的
1.1 DefaultChannelPipeline
源码
public class DefaultChannelPipeline implements ChannelPipeline {
@Override
public final ChannelPipeline fireChannelActive() {
AbstractChannelHandlerContext.invokeChannelActive(head);
return this;
}
@Override
public final ChannelPipeline fireChannelInactive() {
AbstractChannelHandlerContext.invokeChannelInactive(head);
return this;
}
@Override
public final ChannelPipeline fireExceptionCaught(Throwable cause) {
AbstractChannelHandlerContext.invokeExceptionCaught(head, cause);
return this;
}
@Override
public final ChannelPipeline fireUserEventTriggered(Object event) {
AbstractChannelHandlerContext.invokeUserEventTriggered(head, event);
return this;
}
@Override
public final ChannelPipeline fireChannelRead(Object msg) {
AbstractChannelHandlerContext.invokeChannelRead(head, msg);
return this;
}
@Override
public final ChannelPipeline fireChannelReadComplete() {
AbstractChannelHandlerContext.invokeChannelReadComplete(head);
return this;
}
@Override
public final ChannelPipeline fireChannelWritabilityChanged() {
AbstractChannelHandlerContext.invokeChannelWritabilityChanged(head);
return this;
}
}
说明:
可以看出来,这些方法都是 inbound 的方法,也就是入站事件,调用静态方法传入的也是 inbound 的类型 head handler。这些静态方法则会调用 head 的 ChannelInboundInvoker 接口的方法,再然后调用 handler 的真正方法
1.2 再看下piepline 的 outbound 的 fire 方法实现源码
public class DefaultChannelPipeline implements ChannelPipeline {
@Override
public final ChannelFuture bind(SocketAddress localAddress) {
return tail.bind(localAddress);
}
@Override
public final ChannelFuture connect(SocketAddress remoteAddress) {
return tail.connect(remoteAddress);
}
@Override
public final ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress) {
return tail.connect(remoteAddress, localAddress);
}
@Override
public final ChannelFuture disconnect() {
return tail.disconnect();
}
@Override
public final ChannelFuture close() {
return tail.close();
}
@Override
public final ChannelFuture deregister() {
return tail.deregister();
}
@Override
public final ChannelPipeline flush() {
tail.flush();
return this;
}
@Override
public final ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
return tail.bind(localAddress, promise);
}
@Override
public final ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) {
return tail.connect(remoteAddress, promise);
}
@Override
public final ChannelFuture connect(
SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
return tail.connect(remoteAddress, localAddress, promise);
}
@Override
public final ChannelFuture disconnect(ChannelPromise promise) {
return tail.disconnect(promise);
}
}
说明:
说明: