ChannelInitializer 是一种特殊的 ChannelHandler,它也是一种 ChannelInboundHandler,它提供了在通道注册到 EventLoop 后初始化通道的简单方法;其主要目的是在某个 Channel 注册到 EventLoop 后,对这个 Channel 执行一些初始化操作。在初始化完成之后,ChannelInitializer 会 将自己从 Pipeline 中移除,不会影响后续的操作。
作用:在某个Channel注册到EventLoop后,对这个Channel执行一些初始化操作,初始化操作完成后会将自身从Pipeline中移除。
/*** 一个特殊的ChannelInboundHandler,提供了一个便捷的方式在Channel注册到EventLoop的时候来初始化这个Channel* A special {@link ChannelInboundHandler} which offers an easy way to initialize a {@link Channel} once it was* registered to its {@link EventLoop}.** 很多时候是在 Bootstrap#handler/ServerBootstrap#handler或者ServerBootstrap#childHandler 方法中使用,* 用于初始化 Channel 的 ChannelPipeline* * Implementations are most often used in the context of {@link Bootstrap#handler(ChannelHandler)} ,* {@link ServerBootstrap#handler(ChannelHandler)} and {@link ServerBootstrap#childHandler(ChannelHandler)} to* setup the {@link ChannelPipeline} of a {@link Channel}.**
* 使用示例如下:* public class MyChannelInitializer extends {@link ChannelInitializer} {* public void initChannel({@link Channel} channel) {* channel.pipeline().addLast("myHandler", new MyHandler());* }* }** {@link ServerBootstrap} bootstrap = ...;* ...* bootstrap.childHandler(new MyChannelInitializer());* ...*
* 注意到该类是由 @Sharable 标注的,因此实现必须是线程安全的,能够复用* Be aware that this class is marked as {@link Sharable} and so the implementation must be safe to be re-used.*/
* Channel被注册到EventLoop的时候initChannel会被调用,ChannelInitializer实现类必须重写该方法。* 并且该方法调用返回之后,ChannelInitializer实例会从ChannelPipeline移除** @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}.*/ /*** 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}.*
@SuppressWarnings("unchecked")private boolean initChannel(ChannelHandlerContext ctx) throws Exception {// Guard against re-entrance. 解决并发问题,原来没有放进去的话会返回null,原来有就不会放进去,返回旧值if (initMap.putIfAbsent(ctx, Boolean.TRUE) == null) {try {//1.初始化通道,调用用户自己的实现添加 handlerinitChannel((C) ctx.channel());} catch (Throwable cause) {//2.发生异常时,执行异常处理,记录日志并关闭 ChannelHandlerContext// Explicitly call exceptionCaught(...) as we removed the handler before calling initChannel(...).// We do so to prevent multiple calls to initChannel(...).exceptionCaught(ctx, cause);} finally {//3.从 pipeline 移除 ChannelInitializer 自身(这行日志是我自己添加的)logger.info("initChannel移除: " + ctx+ ", 内部的handler是: " + ctx.handler()+ ", handler类型是: " + ctx.handler().getClass()+ ", ctx 类型是: " + ctx.getClass());remove(ctx);}//4.初始化成功return true;}//5.初始化失败return false;}
@Sharable
public abstract class ChannelInitializer<C extends Channel> extends ChannelInboundHandlerAdapter {private static final InternalLogger logger &#61; 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./*** 由于 ChannelInitializer 通常可以在所有的 Bootstrap/ServerBootstrap 通道中共享&#xff0c;因此我们用一个 ConcurrentMap* 这种方式相对于使用 Attributes 方式&#xff0c;可以减少内存的使用&#xff0c;相当于不同的通道&#xff0c;对应的 ChannelHandlerContext 不同&#xff0c;由* ConcurrentMap 来解决了并发问题*/private final ConcurrentMap<ChannelHandlerContext, Boolean> initMap &#61; PlatformDependent.newConcurrentHashMap();/*** Handle the {&#64;link Throwable} by logging and closing the {&#64;link Channel}. Sub-classes may override this.*/&#64;Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {if (logger.isWarnEnabled()) {logger.warn("Failed to initialize a channel. Closing: " &#43; ctx.channel(), cause);}ctx.close();}private void remove(ChannelHandlerContext ctx) {try {ChannelPipeline pipeline &#61; ctx.pipeline();//1.从 pipeline 找到 ChannelHandler 对应的 ChannelHandlerContext 节点//(pipeline 中的节点是ChannelHandlerContext&#xff0c;ChannelHandlerContext包装了ChannelHandler)if (pipeline.context(this) !&#61; null) {//2.移除对应的handler(ChannelInitializer也是一种handler&#xff0c;内部会把 handler 包// 装成的ChannelHandlerContext 节点&#xff0c;再删除节点&#xff0c;pipeline内部是链表结构&#xff0c;节点// 是ChannelHandlerContext类型)pipeline.remove(this);}} finally {//3.从initMap移除initMap.remove(ctx);}}
}
/*** {&#64;inheritDoc} If override this method ensure you call super!*/&#64;Overridepublic void handlerAdded(ChannelHandlerContext ctx) throws Exception {//1.已注册if (ctx.channel().isRegistered()) {//添加该行日志调试logger.info("ChannelInitializer 的 handlerAdded 方法执行... " );// This should always be true with our current DefaultChannelPipeline implementation.// The good thing about calling initChannel(...) in handlerAdded(...) is that there will be no ordering// surprises if a ChannelInitializer will add another ChannelInitializer. This is as all handlers// will be added in the expected order.initChannel(ctx);}}&#64;Override&#64;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.//添加该行日志调试logger.info("进入 ChannelInitializer 的 channelRegistered 方法... " );// 初始化 Channelif (initChannel(ctx)) {//添加该行日志调试logger.info("ChannelInitializer 的 channelRegistered 方法进入if逻辑执行... " );// we called initChannel(...) so we need to call now pipeline.fireChannelRegistered() to ensure we not// miss an event.// 重新触发 Channel Registered 事件ctx.pipeline().fireChannelRegistered();} else {// 继续向下一个节点传播Channel Registered 事件// Called initChannel(...) before which is the expected behavior, so just forward the event.ctx.fireChannelRegistered();}}
private void register0(ChannelPromise promise) {// ... 其他逻辑pipeline.invokeHandlerAddedIfNeeded();//7.回调通知promise执行成功safeSetSuccess(promise);//8.触发通知已注册事件pipeline.fireChannelRegistered();// ... 其他逻辑}
ChannelInitializer的 initChannel 抽象方法在 Channel 被注册到 EventLoop 的时候会被调用&#xff0c;ChannelInitializer 实现类必须重写该方法。
如下是使用代码
//启动器中设置 ChannelInitializer.childHandler(new MyChannelInitializer());//自定义的ChannelInitializerpublic class MyChannelInitializer extends ChannelInitializer {&#64;Overrideprotected void initChannel(Channel ch) throws Exception {System.out.println("MyChannelInitializer begin ... ");ch.pipeline().addLast("logging",new LoggingHandler(LogLevel.INFO));ch.pipeline().addLast(new EchoServerHandler());}
}
//server端日志Connected to the target VM, address: &#39;127.0.0.1:65446&#39;, transport: &#39;socket&#39;
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/C:/Users/intellif/.m2/repository/org/slf4j/slf4j-simple/1.7.25/slf4j-simple-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/C:/Users/intellif/.m2/repository/org/apache/logging/log4j/log4j-slf4j-impl/2.7/log4j-slf4j-impl-2.7.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.slf4j.impl.SimpleLoggerFactory]
Thread[nioEventLoopGroup-2-1,10,main]: register
Thread[nioEventLoopGroup-2-1,10,main]: user handler
[nioEventLoopGroup-2-1] INFO io.netty.channel.ChannelInitializer - ChannelInitializer 的 handlerAdded 方法执行...
[nioEventLoopGroup-2-1] INFO io.netty.channel.ChannelInitializer - initChannel移除: ChannelHandlerContext(ServerBootstrap$1#0, [id: 0x024ee434]), 内部的handler是: io.netty.bootstrap.ServerBootstrap$1&#64;5a308693, handler类型是: class io.netty.bootstrap.ServerBootstrap$1, ctx 类型是: class io.netty.channel.DefaultChannelHandlerContext
Thread[nioEventLoopGroup-2-1,10,main]: PendingRegistrationPromise
Thread[nioEventLoopGroup-2-1,10,main]: ServerBootstrapAcceptor
Thread[nioEventLoopGroup-2-1,10,main]: bind[nioEventLoopGroup-2-2] INFO io.netty.channel.ChannelInitializer - ChannelInitializer 的 handlerAdded 方法执行...
Thread[nioEventLoopGroup-2-2,10,main]: register //这里客户端连接成功
MyChannelInitializer begin ... // MyChannelInitializer 执行&#xff0c;后面会移除 MyChannelInitializer
EchoServerHandler 构造方法执行 ... //添加自定义的handler
[nioEventLoopGroup-2-2] INFO io.netty.channel.ChannelInitializer - initChannel移除: ChannelHandlerContext(MyChannelInitializer#0, [id: 0x38a1d03f, L:/127.0.0.1:12345 - R:/127.0.0.1:49196]), 内部的handler是: com.intellif.channelinitializer.MyChannelInitializer&#64;332f2ee9, handler类型是: class
com.intellif.channelinitializer.MyChannelInitializer, ctx 类型是: class io.netty.channel.DefaultChannelHandlerContext
[nioEventLoopGroup-2-2] INFO io.netty.handler.logging.LoggingHandler - [id: 0x38a1d03f, L:/127.0.0.1:12345 - R:/127.0.0.1:49196] REGISTERED
[nioEventLoopGroup-2-2] INFO io.netty.handler.logging.LoggingHandler - [id: 0x38a1d03f, L:/127.0.0.1:12345 - R:/127.0.0.1:49196] ACTIVE
[nioEventLoopGroup-2-2] INFO io.netty.handler.logging.LoggingHandler - [id: 0x38a1d03f, L:/127.0.0.1:12345 - R:/127.0.0.1:49196] READ: 12B&#43;-------------------------------------------------&#43;| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
&#43;--------&#43;-------------------------------------------------&#43;----------------&#43;
|00000000| 4e 65 74 74 79 20 72 6f 63 6b 73 21 |Netty rocks! |
&#43;--------&#43;-------------------------------------------------&#43;----------------&#43;
[nioEventLoopGroup-2-2] INFO io.netty.handler.logging.LoggingHandler - [id: 0x38a1d03f, L:/127.0.0.1:12345 - R:/127.0.0.1:49196] WRITE: 12B&#43;-------------------------------------------------&#43;| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
&#43;--------&#43;-------------------------------------------------&#43;----------------&#43;
|00000000| 4e 65 74 74 79 20 72 6f 63 6b 73 21 |Netty rocks! |
&#43;--------&#43;-------------------------------------------------&#43;----------------&#43;
[nioEventLoopGroup-2-2] INFO io.netty.handler.logging.LoggingHandler - [id: 0x38a1d03f, L:/127.0.0.1:12345 - R:/127.0.0.1:49196] READ COMPLETE
Server received: Netty rocks!
[nioEventLoopGroup-2-2] INFO io.netty.handler.logging.LoggingHandler - [id: 0x38a1d03f, L:/127.0.0.1:12345 - R:/127.0.0.1:49196] WRITE: 0B
[nioEventLoopGroup-2-2] INFO io.netty.handler.logging.LoggingHandler - [id: 0x38a1d03f, L:/127.0.0.1:12345 - R:/127.0.0.1:49196] FLUSH
[nioEventLoopGroup-2-2] INFO io.netty.handler.logging.LoggingHandler - [id: 0x38a1d03f, L:/127.0.0.1:12345 - R:/127.0.0.1:49196] CLOSE
[nioEventLoopGroup-2-2] INFO io.netty.handler.logging.LoggingHandler - [id: 0x38a1d03f, L:/127.0.0.1:12345 ! R:/127.0.0.1:49196] INACTIVE
[nioEventLoopGroup-2-2] INFO io.netty.handler.logging.LoggingHandler - [id: 0x38a1d03f, L:/127.0.0.1:12345 ! R:/127.0.0.1:49196] UNREGISTERED