Netty版本4.1.22
pipeline可以看作是一个拦截流经Channel的入站和出站事件的ChannelHandler实例链。
图片来自博客:简书闪电侠
前面分析的NioServerSocketChannel
与NioSocketChannel
在创建时都会创建自己的pipeline,在AbstractChannel
中。
《Netty权威指南》里的一张图:
protected AbstractChannel(Channel parent) {
this.parent = parent;
id = newId();
unsafe = newUnsafe();
pipeline = newChannelPipeline();
}
protected DefaultChannelPipeline newChannelPipeline() {
return new DefaultChannelPipeline(this);
}
来看看DefaultChannelPipeline
final AbstractChannelHandlerContext head;
final AbstractChannelHandlerContext tail;
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;
}
保存chnnel引用,创建了两个AbstractChannelHandlerContext
对象,关于head
与tail
final class HeadContext extends AbstractChannelHandlerContext
implements ChannelOutboundHandler, ChannelInboundHandler {
final class TailContext extends AbstractChannelHandlerContext implements
ChannelInboundHandler {
head既是inbound
也是outbound
,tail是inbound
此时整个pipeline是这样。
pipeline中的每个节点是一个ChannelHandlerContext
对象。ChannelHandlerContext
代表ChannelHandler和channelPipeline之间的关联,每当有ChannelHandler添加到ChannelPipeline中时,都会创建ChannelHandlerContext。其主要功能是管理它所关联的ChannelHandler与同在一个ChannelPipeline中的其它ChannelHandler之间的交互。
pipeline添加节点
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline()
.addLast("IdleStateHandler", new IdleStateHandler(10, 0, 0))
.addLast("LengthFieldPrepender", new LengthFieldPrepender(4, 0))
.addLast("RpcEncoder", new RpcEncoder())
.addLast("LengthFieldBasedFrameDecoder",
new LengthFieldBasedFrameDecoder(1024 * 1024, 0,4,0,4))
.addLast("RpcDecoder", new RpcDecoder())
.addLast("RpcServerHandler", new RpcServerHandler(handlerMap));
}
})
来跟踪addLast方法
DefaultChannelPipeline:
public final ChannelPipeline addLast(String name, ChannelHandler handler) {
return addLast(null, name, handler);
}
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
final AbstractChannelHandlerContext newCtx;
synchronized (this) {
checkMultiplicity(handler); // 检测handler是否已添加
// 创建节点
newCtx = newContext(group, filterName(name, handler), handler);
// 添加节点
addLast0(newCtx);
// registered 为false表明channel还未注册,
// 那么就将newCtx封装成一个任务加到任务链表,当channel注册完成后
// 再触发callHandlerAdded0,他会触发handler的handlerAdded
if (!registered) {
newCtx.setAddPending();
callHandlerCallbackLater(newCtx, true);
return this;
}
// 若你调用addLast时传入group,则上面newContext方法在构建headlerContext
// 的时候,会从group种next一个NioEventLoop赋给
// AbstractChannelHandlerContext.executor字段,
// 作为一个child executor,即子线程。
// 否则方法返回 channel 的 EventLoop
EventExecutor executor = newCtx.executor();
// callHandlerAdded0 方法的执行交给特定的线程
if (!executor.inEventLoop()) {
newCtx.setAddPending();
executor.execute(new Runnable() {
@Override
public void run() {
callHandlerAdded0(newCtx);
}
});
return this;
}
}
callHandlerAdded0(newCtx);
return this;
}
对pipeline中context节点操作由synchronized保护,即对handler链的更改要确保安全性。
newCtx.executor()
定位到AbstractChannelHandlerContext
public EventExecutor executor() {
if (executor == null) {
return channel().eventLoop();
} else {
return executor;
}
}
这里addLast
时没传入group,则AbstractChannelHandlerContext.executor
为null,返回与pipeline关联的channnel的EventLoop
1,checkMultiplicity检测该handler是否已添加
private static void checkMultiplicity(ChannelHandler handler) {
if (handler instanceof ChannelHandlerAdapter) {
ChannelHandlerAdapter h = (ChannelHandlerAdapter) handler;
if (!h.isSharable() && h.added) {
throw new ChannelPipelineException(
h.getClass().getName() +
" is not a @Sharable handler, so can't be added or removed multiple times.");
}
h.added = true;
}
}
如果该handler是非共享的且已经添加过(hanler的added
标识一个handler是否已添加)则抛异常。否则h.added = true;
标识该handler为已添加。
关于共享handler
@Sharable
public class BusinessHandler {
}
isSharable() 检测的就是该注解
public boolean isSharable() {
Class<?> clazz = getClass();
Map<Class<?>, Boolean> cache = InternalThreadLocalMap.get().handlerSharableCache();
Boolean sharable = cache.get(clazz);
if (sharable == null) {
sharable = clazz.isAnnotationPresent(Sharable.class);
cache.put(clazz, sharable);
}
return sharable;
}
2,newContext创建节点
newCtx = newContext(group, filterName(name, handler), handler);
filterName给handler创建一个唯一性的名字。
private AbstractChannelHandlerContext newContext(EventExecutorGroup group, String name, ChannelHandler handler) {
return new DefaultChannelHandlerContext(this, childExecutor(group), name, handler);
}
先来看看childExecutor(group),group为null则返回null
private EventExecutor childExecutor(EventExecutorGroup group) {
if (group == null) {
return null;
}
// 检查SINGLE_EVENTEXECUTOR_PER_GROUP属性,默认为true,代表
// 整个pipeline由一个线程来执行
Boolean pinEventExecutor = channel.config().getOption(ChannelOption.SINGLE_EVENTEXECUTOR_PER_GROUP);
if (pinEventExecutor != null && !pinEventExecutor) {
return group.next(); // 若为false,则从group中选择一个线程来执行,不推荐
}
// 下面的操作就是从group中选择一个NioEventLoop,再将group与该eventLoop关联并存储
// 下次同一group用同一eventLoop
Map<EventExecutorGroup, EventExecutor> childExecutors = this.childExecutors;
if (childExecutors == null) {
// Use size of 4 as most people only use one extra EventExecutor.
childExecutors = this.childExecutors = new IdentityHashMap<EventExecutorGroup, EventExecutor>(4);
}
// Pin one of the child executors once and remember it so that the same child executor
// is used to fire events for the same channel.
EventExecutor childExecutor = childExecutors.get(group);
if (childExecutor == null) {
childExecutor = group.next();
childExecutors.put(group, childExecutor);
}
return childExecutor;
}
SINGLE_EVENTEXECUTOR_PER_GROUP
Netty参数,单线程执行ChannelPipeline中的事件,默认值为True。该值控制ChannelPipeline中执行ChannelHandler的线程。如果为True,整个pipeline由一个线程执行,这样不需要进行线程切换以及线程同步,是Netty4的推荐做法;如果为False,ChannelHandler中的处理过程会由Group中的不同线程执行。
继续,DefaultChannelHandlerContext
DefaultChannelHandlerContext(
DefaultChannelPipeline pipeline, EventExecutor executor, String name, ChannelHandler handler) {
super(pipeline, executor, name, isInbound(handler), isOutbound(handler));
if (handler == null) {
throw new NullPointerException("handler");
}
this.handler = handler;
}
private static boolean isInbound(ChannelHandler handler) {
return handler instanceof ChannelInboundHandler;
}
private static boolean isOutbound(ChannelHandler handler) {
return handler instanceof ChannelOutboundHandler;
}
保存handler引用
AbstractChannelHandlerContext(DefaultChannelPipeline pipeline, EventExecutor executor, String name,
boolean inbound, boolean outbound) {
this.name = ObjectUtil.checkNotNull(name, "name");
this.pipeline = pipeline;
this.executor = executor;
this.inbound = inbound;
this.outbound = outbound;
// Its ordered if its driven by the EventLoop or the given Executor is an instanceof OrderedEventExecutor.
ordered = executor == null || executor instanceof OrderedEventExecutor;
}
3,addLast0(newCtx)添加节点
private void addLast0(AbstractChannelHandlerContext newCtx) {
AbstractChannelHandlerContext prev = tail.prev;
newCtx.prev = prev;
newCtx.next = tail;
prev.next = newCtx;
tail.prev = newCtx;
}
典型的双向链表插入过程,插入到tail
节点之前。
4, callHandlerAdded0回掉handler的handlerAdded()
之前连接一篇很长一大篇来分析ChannelInitializer
的initChannel
什么时候被调用,就是在这里 callHandlerAdded0
private void callHandlerAdded0(final AbstractChannelHandlerContext ctx) {
try {
// We must call setAddComplete before calling handlerAdded.
// Otherwise if the handlerAdded method generates
// any pipeline events ctx.handler() will miss them because
// the state will not allow it.
ctx.setAddComplete();
ctx.handler().handlerAdded(ctx);
通常节点状态为ADD_PENDING
,CAS更改为ADD_COMPLETE
,如果是REMOVE_COMPLETE
则不应该更改它。
ctx.handler().handlerAdded(ctx);
回调该handler的handlerAdded()
方法。以ChannelInitializer
为例:
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
if (ctx.channel().isRegistered()) {
initChannel(ctx);
}
}
运行到里ctx.channel().isRegistered()
一定为true,即保证channel已注册,指的是AbstractChannel.registered
为true
,它只在regoster0
里被改为true,如何保证callHandlerAdded0
的调用满足这一条件,callHandlerAdded0在DefaultChannelPipeline的addFirst,addLast,addBefore,addAfter里被调用,在调用前会检测DefaultChannelPipeline.registered
变量值,为false则将该callHandlerAdded0
的调用延迟(延迟方法看连接一篇),该变量只在callHandlerAddedForAllHandlers
被更改,channel注册在register0里会调用pipeline.invokeHandlerAddedIfNeeded();
,如果第一次注册调用callHandlerAddedForAllHandlers,更改DefaultChannelPipeline.registered,调用callHandlerAdded0,这样保证了ctx.channel().isRegistered()
一定返回true。
对之前分析中出现的pipeline操作进行分析
- register0中出现的invokeHandlerAddedIfNeeded,fireChannelRegistered,fireChannelActive
- 连接阶段出现的fireChannelRead,fireChannelReadComplete
invokeHandlerAddedIfNeeded
在register0
中invokeHandlerAddedIfNeeded:对pipeline中链表的添加删除等改动操作,最后都会调用callHandlerRemoved0,callHandlerAdded0,去改变节点handlerContext的状态,并触发该handler的handlerRemoved,handlerAdded。不过上述有个前提是该channel已注册,所以在未注册情况下,这两个方法的调用被封装起来,等待注册后执行,invokeHandlerAddedIfNeeded 就是在注册后触发它们的方法。
fireChannelRegistered
选取一个点分析:服务启动阶段register0,invokeHandlerAddedIfNeeded执行后,NioServerSocketChannel的pipeline变为head->ServerBootstrapAcceptor->tail,假设用户没有设定bootstrap.handler(),所以就只有三个handler。
DefaultChannelPipeline:
public final ChannelPipeline fireChannelRegistered() {
AbstractChannelHandlerContext.invokeChannelRegistered(head);
return this;
}
参数是 head 头节点
static void invokeChannelRegistered(final AbstractChannelHandlerContext next) {
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeChannelRegistered();
} else {
executor.execute(new Runnable() {
@Override
public void run() {
next.invokeChannelRegistered();
}
});
}
}
既然是register0方法,那么这里(executor.inEventLoop()
返回true
private void invokeChannelRegistered() {
if (invokeHandler()) {
try {
((ChannelInboundHandler) handler()).channelRegistered(this);
} catch (Throwable t) {
notifyHandlerException(t);
}
} else {
fireChannelRegistered();
}
}
handler 在加入 pipeline 后其 handlerAdded 被调用,在该方法被调用前会先将 handler 所属节点的状态改为 ADD_COMPLETE
private boolean invokeHandler() {
// Store in local variable to reduce volatile reads.
int handlerState = this.handlerState;
return handlerState == ADD_COMPLETE || (!ordered && handlerState == ADD_PENDING);
}
该方法检测ChannelHandler#handlerAdded(ChannelHandlerContext)
是否已被调用,是返回true。
关于 ordered :在节点初始化过程中在超类 AbstractChannelHandlerContext 中进行判断,一般情况下我们在调用如addLast 时不会传一个 NioEventLoopGroup 对象,也就是我们希望一个 channel 的操作都有唯一一个NioEventLoop 的线程来处理,不交给其它的线程,所以 ordered 一般为 true。
ordered = executor == null || executor instanceof OrderedEventExecutor;
回到invokeChannelRegistered,handler() 返回 head,来看它的channelRegistered(head),
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
invokeHandlerAddedIfNeeded();
ctx.fireChannelRegistered();
}
ctx指的是head,ctx.fireChannelRegistered();
将channelRegistered事件传递下去
AbstractChannelHandlerContext:
public ChannelHandlerContext fireChannelRegistered() {
invokeChannelRegistered(findContextInbound());
return this;
}
private AbstractChannelHandlerContext findContextInbound() {
AbstractChannelHandlerContext ctx = this;
do {
ctx = ctx.next;
} while (!ctx.inbound);
return ctx;
}
找寻handler链上下一个inbound,调用其channelRegistered
,本例子中ServerBootstrapAcceptor extends ChannelInboundHandlerAdapter
,ServerBootstrapAcceptor 就是下一个inbound。来看看它的实现,在其父类ChannelInboundHandlerAdapter
中
ChannelInboundHandlerAdapter:
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
ctx.fireChannelRegistered();
}
不做任何反应,直接传递下去,下一个inbound是tail,TailContext extends AbstractChannelHandlerContext implements ChannelInboundHandler
public void channelRegistered(ChannelHandlerContext ctx) throws Exception { }
作为尾节点对channelRegistered事件不做任何处理,事件到此结束。
fireChannelActive
对于服务端启动阶段,fireChannelActive并不会在注册阶段register0方法中被调用,因为NioServerSocketChannel.isActive
(该方法有多种实现,由于选的是服务端启动,这里定位到NioServerSocketChannel)只有在成功绑定地址后才会返回true。在服务端启动的绑定阶段,AbstractUnsafe的bind方法里,当doBind实际绑定操作结束后
if (!wasActive && isActive()) {
invokeLater(new Runnable() {
@Override
public void run() {
pipeline.fireChannelActive();
}
});
}
invokeLater就是将其封装策成Runnable加入到taskQueue
中。这里是哪个线程在执行,服务端启动的主线程,还是在register
方法中启动的reactor线程?回到AbstractBootstrap.doBind0
,发现其将之后绑定操作交给了reactor线程来执行。这里的reactor线程指的是NioServerSocketChannel所属的NioEventLoop的线程
所以这里我们分析的阶段定为服务端启动的绑定阶段,AbstractUnsafe的bind方法里,当doBind实际绑定操作结束后,上述代码的执行
public final ChannelPipeline fireChannelActive() {
AbstractChannelHandlerContext.invokeChannelActive(head);
return this;
}
static void invokeChannelActive(final AbstractChannelHandlerContext next) {
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeChannelActive();
} else {
executor.execute(new Runnable() {
@Override
public void run() {
next.invokeChannelActive();
}
});
}
}
AbstractChannelHandlerContext
AbstractChannelHandlerContext:
private void invokeChannelActive() {
if (invokeHandler()) {
try {
((ChannelInboundHandler) handler()).channelActive(this);
} catch (Throwable t) {
notifyHandlerException(t);
}
} else {
fireChannelActive();
}
}
这里handler()指的是head。
HeadContext的channelActive实现
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.fireChannelActive(); // 传递channelActive事件
readIfIsAutoRead();
}
private void readIfIsAutoRead() {
if (channel.config().isAutoRead()) { // true
channel.read();
}
}
HeadContext的处理是首先将channelActive事件传递给下一个inbound,随后调用readIfIsAutoRead
:channel.config().isAutoRead()
为true,跟踪read
AbstractChannel
public Channel read() {
pipeline.read();
return this;
}
DefaultChannelPipeline
public final ChannelPipeline read() {
tail.read();
return this;
}
调用了tail的read
AbstractChannelHandlerContext
public ChannelHandlerContext read() {
// 从tail往前找第一个outBound
final AbstractChannelHandlerContext next = findContextOutbound();
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeRead();
} else {
Runnable task = next.invokeReadTask;
if (task == null) {
next.invokeReadTask = task = new Runnable() {
@Override
public void run() {
next.invokeRead();
}
};
}
executor.execute(task);
}
return this;
}
private AbstractChannelHandlerContext findContextOutbound() {
AbstractChannelHandlerContext ctx = this; // 指tail
do {
ctx = ctx.prev;
} while (!ctx.outbound);
return ctx;
}
从tail往前找第一个outBound,调用其invokeRead
,以上面选取的阶段为例,head->ServerBootstrapAcceptor->tail
,tail前的第一个outBound是head implements ChannelOutboundHandler, ChannelInboundHandler
,它的invokeRead()
实现在AbstractChannelHandlerContext
AbstractChannelHandlerContext:
private void invokeRead() {
if (invokeHandler()) {
try {
((ChannelOutboundHandler) handler()).read(this);
} catch (Throwable t) {
notifyHandlerException(t);
}
} else {
read();
}
}
调用其本身的read(ChannelHandlerContext ctx)
HeadContext:
public void read(ChannelHandlerContext ctx) {
unsafe.beginRead();
}
对于NioServerSocketChannel
它的unsafe指的是NioMessageUnsafe
,beginRead方法实现在AbstractUnsafe
AbstractUnsafe:
public final void beginRead() {
assertEventLoop();
if (!isActive()) {
return;
}
try {
doBeginRead();
} catch (final Exception e) {
invokeLater(new Runnable() {
@Override
public void run() {
pipeline.fireExceptionCaught(e);
}
});
close(voidPromise());
}
}
首先isActive()
由于选取的阶段是服务端启动,定位到NioServerSocketChannel里的实现
public boolean isActive() {
return javaChannel().socket().isBound();
}
java.net.ServerSocket:
/**
* Returns the binding state of the ServerSocket.
*
* @return true 成功绑定地址返回true
* @since 1.4
*/
public boolean isBound() {
// Before 1.3 ServerSockets were always bound during creation
return bound || oldImpl;
}
成功绑定地址isActive
才会返回true。而由于我们选取的阶段时绑定成功后,所以代码执行到这里返回true,doBeginRead()
调用。
AbstractNioMessageChannel:
protected void doBeginRead() throws Exception {
if (inputShutdown) {
return;
}
super.doBeginRead();
}
AbstractNioChannel:
protected void doBeginRead() throws Exception {
// Channel.read() or ChannelHandlerContext.read() was called
final SelectionKey selectionKey = this.selectionKey;
if (!selectionKey.isValid()) {
return;
}
readPending = true;
final int interestOps = selectionKey.interestOps();
if ((interestOps & readInterestOp) == 0) {
selectionKey.interestOps(interestOps | readInterestOp);
}
}
对于一个SelectionKey它的isValid()
为true是从其被创建开始一直到它被cancelled或者channel关闭或者Selector关闭位置。
readInterestOp对于NioServerSocketChannel
是OP_ACCEPT,对于NioSocketChannel
是OP_READ。上述代码做的是当相应channel关心的事件被删除则恢复它。
总结一下:我们选取的阶段是绑定完成后的fireChannelActive
的调用,一路分析到这,发现该方法在此阶段的调用目的就是设置NioServerSocketChannel
的关心事件为ACCEPT
在上面HeadContext的channelActive方法中,首先是将channelActive事件传递给下一个inbound,此时pipeline里情况是head->ServerBootstrapAcceptor->tail,下个inbound是ServerBootstrapAcceptor,跟踪发现其并不关心channelActive事件,直接将其向下传递,下一个inbound是tail
TailContext:
public void channelActive(ChannelHandlerContext ctx) throws Exception {
onUnhandledInboundChannelActive();
}
protected void onUnhandledInboundChannelActive() {
}
没做任何处理,channelActive传到尾部就吞掉该事件。
前面分析了服务端启动阶段 invokeHandlerAddedIfNeeded,fireChannelRegistered,fireChannelActive所起的作用。接下来分析它们在连接阶段的作用(连接指客户端连接到来,NioSocketChannel创建过程,具体看我连接一篇的分析)。
对于连接阶段这三个方法全在register0
中被调用。
invokeHandlerAddedIfNeeded:调用前NioSocketChannel的pipeline里的情况是head->ChannelInitialize(指childHandler)->tail,调用后变为head->xxx->xxx->xxx->…(指代码中我们设置的那些childHandler)->tail。
fireChannelRegistered:调用各个handler的channelRegistered
方法。
fireChannelActive:不同于服务端启动,连接阶段执行到这会调用fireChannelActive,因为isActive返回true,具体实现定位到NioSocketChannel
public boolean isActive() {
SocketChannel ch = javaChannel();
return ch.isOpen() && ch.isConnected();
}
channel创建既是open的,关于isConnected()连接一篇中分析过,ServerSocketChannel.accept
返回的SocketChannel
,其在创建过程中状态被赋值为2,即CONNECTED,所以isActive()返回true。
一开始过程与上面的分析一样,直到从tail往前找第一个outBound,调用其invokeRead
,多数outbound的read实现只是将事件往前传,如LengthFieldPrepender,MessageToByteEncoder,IdleStateHandler等,所以如服务端一样最终传到head,head的实现调用unsafe.beginRead();
,对于NioSocketChannel
它的unsafe指的是NioSocketChannelUnsafe
,跟踪发现其实现与上面服务端的一样,只是这里恢复的是NioSocketChannel的OP_READ事件。
连接阶段出现的fireChannelRead,fireChannelReadComplete,以及其它一些将在下一篇介绍。