第一部分handler
pipeline是一根管道,数据通过这根管道,首先流向编解码的handler,然后在流向我们自定义的handler中
1.handler是基于pipeline的
例如我们需要处理 HTTP 数据, 那么就可以在 pipeline 前添加一个 Http 的编解码的 Handler,
然后接着添加我们自己的业务逻辑的 handler,
2.也就是你要处理什么样的请求,tcp,http,或者udp的时候,
需要在pipeline前添加一个编解码的Handler,
然后在添加我们自己的handler
这样网络上的数据流就向通过一个管道一样, 从不同的 handler 中流过并进行编解码, 最终在到达我们自定义的 handler 中.
.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
if (sslCtx != null) {
p.addLast(sslCtx.newHandler(ch.alloc(), HOST, PORT));
}
//p.addLast(new LoggingHandler(LogLevel.INFO));
p.addLast(new EchoClientHandler());
}
});
handler方法,参数是ChannelHandler,我们传入进去的是ChannelInitializer
ChannelInitializer是 ChannelHandlerAdapter的子类
public abstract class ChannelHandlerAdapter implements ChannelHandler
public B handler(ChannelHandler handler) {
if (handler == null) {
throw new NullPointerException("handler");
}
this.handler = handler;
return (B) this;
}
2.ChannelInitializer是一个抽象类,
里面有一个initChannel方法,接收Channel的参数
public abstract class ChannelInitializer<C extends Channel> extends ChannelInboundHandlerAdapter {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(ChannelInitializer.class);
// We use a ConcurrentMap as a ChannelInitializer is usually shared between all Channels in a Bootstrap /
// ServerBootstrap. This way we can reduce the memory usage compared to use Attributes.
private final ConcurrentMap<ChannelHandlerContext, Boolean> initMap = PlatformDependent.newConcurrentHashMap();
/**
* This method will be called once the {@link Channel} was registered. After the method returns this instance
* will be removed from the {@link ChannelPipeline} of the {@link Channel}.
*
* @param ch the {@link Channel} which was registered.
* @throws Exception is thrown if an error occurs. In that case it will be handled by
* {@link #exceptionCaught(ChannelHandlerContext, Throwable)} which will by default close
* the {@link Channel}.
*/
protected abstract void initChannel(C ch) throws Exception;
@Override
@SuppressWarnings("unchecked")
public final void channelRegistered(ChannelHandlerContext ctx) throws Exception {
// Normally this method will never be called as handlerAdded(...) should call initChannel(...) and remove
// the handler.
if (initChannel(ctx)) {
// we called initChannel(...) so we need to call now pipeline.fireChannelRegistered() to ensure we not
// miss an event.
ctx.pipeline().fireChannelRegistered();
} else {
// Called initChannel(...) before which is the expected behavior, so just forward the event.
ctx.fireChannelRegistered();
}
}
}
ChannelInitializer 是一个抽象类, 它有一个抽象的方法 initChannel,
我们正是实现了这个方法, 并在这个方法中添加的自定义的 handler 的.
那么 initChannel 是哪里被调用的呢? 答案是 ChannelInitializer.channelRegistered 方法中.
3.我们来关注一下 channelRegistered 方法. 从上面的源码中, 我们可以看到, 在 channelRegistered 方法中,
会调用 initChannel 方法, 将自定义的 handler 添加到 ChannelPipeline 中,
然后调用 ctx.pipeline().remove(this) 将自己从 ChannelPipeline 中删除.
上面的分析过程, 可以用如下图片展示:
一开始, ChannelPipeline 中只有三个 handler, head, tail 和我们添加的 ChannelInitializer.
在 channelRegistered 方法会调用 initChannel方法, 参数是 ChannelHandlerContext
private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
if (initMap.putIfAbsent(ctx, Boolean.TRUE) == null) { // Guard against re-entrance.
try {
initChannel((C) ctx.channel());
} catch (Throwable cause) {
// Explicitly call exceptionCaught(...) as we removed the handler before calling initChannel(...).
// We do so to prevent multiple calls to initChannel(...).
exceptionCaught(ctx, cause);
} finally {
remove(ctx);
}
return true;
}
return false;
}
initChannel((C) ctx.channel())这个方法就是上面那个抽象的方法,需要我们自己去实现
romove(ctx) 调用 ctx.pipeline().remove(this) 将自己从 ChannelPipeline 中删除.
private void remove(ChannelHandlerContext ctx) {
try {
ChannelPipeline pipeline = ctx.pipeline();
if (pipeline.context(this) != null) {
pipeline.remove(this);
}
} finally {
initMap.remove(ctx);
}
}
4.channelPipeline有哪些handler
一开始, ChannelPipeline 中只有三个 handler, head, tail 和我们添加的 ChannelInitializer.
head---》ChannelInitializer---》tail
接着 initChannel 方法调用后, 添加了自定义的 handler:
p.addLast(new EchoClientHandler());
head---》ChannelInitializer---》 EchoClientHandler ----》tail
最后将 ChannelInitializer 删除:
pipeline.remove(this);
head---》 EchoClientHandler ----》tail
分析到这里, 我们已经简单了解了自定义的 handler 是如何添加到 ChannelPipeline 中的,
不过限于主题与篇幅的原因, 我没有在这里详细展开 ChannelPipeline 的底层机制,
我打算在下一篇 Netty 源码分析之 二 贯穿Netty 的大动脉 ── ChannelPipeline 中对这个问题进行深入的探讨.