`
Donald_Draper
  • 浏览: 981593 次
社区版块
存档分类
最新评论

netty 通道初始化器ChannelInitializer

阅读更多
netty 消息解码器-ByteToMessageDecoder:http://donald-draper.iteye.com/blog/2388088
netty 默认Channel管道线-Inbound和Outbound事件处理:http://donald-draper.iteye.com/blog/2389148
netty 通道处理器上下文定义:http://donald-draper.iteye.com/blog/2389214
netty 通道处理器上下文:http://donald-draper.iteye.com/blog/2389299
引言:
在前面的文章中,我们看了通道处理器,Channel管道线,和通道上下文,梳理了他们之间的关系,那么通道处理器如何添加通道的管道中呢?这个不得不提通道初始化器ChannelInitializer。
在netty的相关demo实例中,有这么一段:
 EventLoopGroup workerGroup = new NioEventLoopGroup();
  try {
  	//Bootstrap,用于配置客户端,一般为Socket通道
      Bootstrap bootstrap = new Bootstrap();
      bootstrap.group(workerGroup)
       .channel(NioSocketChannel.class)
       .handler(new ChannelInitializer<SocketChannel>() {
           @Override
           protected void initChannel(SocketChannel ch) throws Exception {
          	 //添加安全套接字处理器和通道处理器到
               ChannelPipeline pipeline = ch.pipeline();
               if (sslCtx != null) {
              	 pipeline.addLast(sslCtx.newHandler(ch.alloc(), ip, port));
               }
               pipeline.addLast(new LoggingHandler(LogLevel.INFO));
               pipeline.addLast(new EchoClientHandler());
           }
       });
      InetSocketAddress inetSocketAddress = new InetSocketAddress(ip,port);
      //连接socket地址
      ChannelFuture f = bootstrap.connect(inetSocketAddress).sync();
      log.info("=========Client is start=========");
      //等待,直到连接关闭
      f.channel().closeFuture().sync();
  } finally {
  	workerGroup.shutdownGracefully();
}

今天我们关注的是通道处理器的初始化:
.handler(new ChannelInitializer<SocketChannel>() {
           @Override
           protected void initChannel(SocketChannel ch) throws Exception {
          	 //添加安全套接字处理器和通道处理器到
               ChannelPipeline pipeline = ch.pipeline();
               if (sslCtx != null) {
              	 pipeline.addLast(sslCtx.newHandler(ch.alloc(), ip, port));
               }
               pipeline.addLast(new LoggingHandler(LogLevel.INFO));
               pipeline.addLast(new EchoClientHandler());
           }
});

上面一段代码,有一个通道处理器初始化类ChannelInitializer,
这个类就是今天我们要看的:
package io.netty.channel;

import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;

import java.util.concurrent.ConcurrentMap;

/**
 * A special {@link ChannelInboundHandler} which offers an easy way to initialize a {@link Channel} once it was
 * registered to its {@link EventLoop}.
 *ChannelInitializer是一个特殊的inboun通道处理器,一旦通道注册到事件循环中时,提供一个便捷的初始化通道的方式。
 * 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}.
 *具体的实现一般在Bootstrap#handler(ChannelHandler) ,ServerBootstrap#handler(ChannelHandler)和
 ServerBootstrap#childHandler(ChannelHandler)中,设置Channel的管道。
 * <pre>
 *
 * 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());
 * ...
 * </pre>
 * Be aware that this class is marked as {@link Sharable} and so the implementation must be safe to be re-used.
 *注意此类为共享类型,具体的实现必须是线程安全的。
 * @param <C>   A sub-type of {@link Channel} 参数类型C为通道的子类
 */
@Sharable
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.
    //由于通道初始器经常在Bootstrap /ServerBootstrap的所有通道中共享,所以我们用一个ConcurrentMap作为初始化器。
    //这种方式,相对于使用属性方式,减少了内存的使用。
    private final ConcurrentMap<ChannelHandlerContext, Boolean> initMap = PlatformDependent.newConcurrentHashMap();
    //注册通道处理器动事件循环
     @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.
	//正常情况下这个方法不为被调用,因为handlerAdded可以用于初始化通道
        if (initChannel(ctx)) {
            // we called initChannel(...) so we need to call now pipeline.fireChannelRegistered() to ensure we not
            // miss an event.
	    //调用上下文关联通道的fireChannelRegistered,确保不会调试事件
            ctx.pipeline().fireChannelRegistered();
        } else {
            // Called initChannel(...) before which is the expected behavior, so just forward the event.
	    //转发事件
            ctx.fireChannelRegistered();
        }
    }
    /**
     * {@inheritDoc} If override this method ensure you call super!
     如果重写,必须保证能够调用super
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        //当通道注册到事件循环中
        if (ctx.channel().isRegistered()) {
            // 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
            //在当前默认Channel管道下的实现下,总是返回true。在handlerAdded方法中调用initChannel方法好处是,如果一个初始化器
	    //添加到另外一个初始化器,不会从排序。所有的通道处理器将会以期望的顺序添加Channel管道中
            initChannel(ctx);
        }
    }
   //初始化通道处理器上下文
    @SuppressWarnings("unchecked")
    private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
        //将上下文放入通道初始化器的上下文Map中
        if (initMap.putIfAbsent(ctx, Boolean.TRUE) == null) { // Guard against re-entrance.
            try {
	        //添加成功,则调用initChannel(C ch),初始化通道
                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(...).
		//异常发生则抛出log异常,并关闭上下文
                exceptionCaught(ctx, cause);
            } finally {
	        //最后从通道初始化器的上下文Map移除当前上下文
                remove(ctx);
            }
            return true;
        }
        return false;
    }
     /**
     * 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;
    /**
     * Handle the {@link Throwable} by logging and closing the {@link Channel}. Sub-classes may override this.
     处理异常,并关闭上下文
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        logger.warn("Failed to initialize a channel. Closing: " + ctx.channel(), cause);
        ctx.close();
    }
    //移除通道处理器上下文
    private void remove(ChannelHandlerContext ctx) {
        try {
	    //从当前通道的管道中移除上下文
            ChannelPipeline pipeline = ctx.pipeline();
            if (pipeline.context(this) != null) {
                pipeline.remove(this);
            }
        } finally {
	    //从通道初始化器上下文Map移除上下文
            initMap.remove(ctx);
        }
    }
}


总结:
通道初始化器ChannelInitializer实际上为Inbound通道处理器,当通道注册到事件循环中后,添加通道初始化器到通道,触发handlerAdded事件,然后将初始化器的上下文放入通道初始化器的上下文Map中,如果放入成功且先前不存在,initChannel(C ch),初始化通道,其中C为当前通道,我们可以获取C的管道,添加通道处理器到管道,这就是通道初始化器的作用。
添加完后,从通道的管道中移除初始化器上下文,并从通道初始化器的上下文Map中移除通道初始化器上下文。
0
0
分享到:
评论

相关推荐

    Netty8583.zip

    7. **频道初始化器(ChannelInitializer)**:在 Netty 中,我们使用 ChannelInitializer 配置新建立的通道,添加必要的处理器到处理器管道,以便处理进来的连接和消息。 8. **线程模型**:理解 Netty 的 Boss-...

    后端netty网络编程

    2. **通道初始化(ChannelInitializer)**:在初始化器中,我们添加所需的处理器到通道管道,如解码器、编码器和业务处理器。 3. **连接处理**:当客户端连接到服务器时,会触发ChannelActive事件,我们可以在这个...

    Netty重点内容总结及实战.pdf

    - 使用ChannelInitializer为通道初始化处理器。 - 实现具体的业务逻辑处理器SimpleServerHandler。 - 将处理器添加到Pipeline中。 - 启动并绑定监听端口。 ***ty客户端编程细节 - 创建客户端启动组件Bootstrap...

    springboot整合netty的demo

    在Server端,我们可以使用`ServerBootstrap.bind()`方法绑定监听端口,并设置`ChannelInitializer`来初始化每个新建立的连接,这里可以注册自定义的`ChannelInboundHandlerAdapter`来处理接收到的数据。而在Client端...

    基于netty编写的socket服务端

    Netty的`Bootstrap`类是用来初始化和配置服务器的启动参数,如绑定的IP地址和端口号、事件循环组、处理器链等。例如,我们可以通过以下代码创建一个简单的Netty服务器: ```java EventLoopGroup bossGroup = new ...

    netty客户端和服务端通讯

    2. 添加自定义的 ChannelInitializer,用于在 Channel 初始化时设置 ChannelPipeline 中的 Handler。 3. 绑定客户端的 Handler,通常包含编码(将业务对象转换为 ByteBuf)和解码(将 ByteBuf 转换回业务对象)逻辑...

    Netty服务端和客户端调用demo

    `TimeServer`类就是这样一个启动点,它会初始化Netty服务器并监听指定的端口。在`TimeServer`的`main`函数中,会设置一个`ServerBootstrap`,配置一个`childHandler`,该处理器会处理连接到服务器的所有新通道。`...

    Netty架构源码剖析_netty_

    7. **ChannelInitializer**:用于初始化新创建的Channel,设置其Pipeline中的处理器。 Netty的事件驱动模型是它高效性能的关键。当接收到网络事件时,如连接建立、数据到达、连接关闭等,Netty会将这些事件封装成...

    使用jboss netty 创建高性能webservice客户端及服务端

    Netty的核心组件包括Bootstrap(启动器)、Channel(通道)、Handler(处理器)和EventLoop(事件循环),它们协同工作以实现高效的数据传输。 创建Web Service服务端,首先我们需要配置一个ServerBootstrap,它是...

    NettyDemo包含客户端服务端

    1. **通道初始化**: 在 Netty 中,客户端首先创建一个 `Bootstrap` 实例,用于配置客户端连接。通过设置 `ChannelInitializer`,我们可以为每个新创建的 `Channel` 添加处理器(`ChannelHandler`)链,处理进来的...

    netty框架,服务端、客户端代码示例

    2. **指定通道处理器**: 服务端需要定义一个ChannelInitializer,这个初始化器会在建立连接时被调用,用于配置新创建的Channel。在这里,我们可能会注册一个服务端接收消息的处理器。 3. **绑定端口并启动**: 通过...

    Netty源码依赖包

    - `ChannelInitializer`:用于初始化Channel时注册各种处理器。 - `ChannelInboundHandlerAdapter` 和 `ChannelOutboundHandlerAdapter`:为入站和出站事件提供默认的空实现。 ### 总结 通过对Netty源码依赖包的...

    netty_echo.zip

    在服务器端,首先需要创建一个`ServerBootstrap`实例,然后配置通道初始化器`ChannelInitializer`,用于在通道激活时设置处理器pipeline。在这个pipeline中,我们通常会添加`EchoServerHandler`,负责接收并回显...

    Android与Netty服务器连接

    3. 发送请求到服务器:在`channelActive`方法中可以发送初始化请求,或者在其他地方根据业务逻辑发送请求。 4. 错误处理:在`exceptionCaught`方法中捕获并处理异常。 最后,别忘了在不使用时关闭连接和资源,避免...

    learning-netty

    Bootstrap是Netty中的一个启动类,用于配置并初始化Netty服务。它有两个主要实现:`Bootstrap`和`ServerBootstrap`,分别用于客户端和服务端的初始化。 #### 类AbstractBootstrap 这是一个抽象基类,为`Bootstrap`...

    Netty4详解二:开发第一个Netty应用程序

    .handler(new ChannelInitializer() { // 设置初始化处理器 @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new MyServerHandler()); // 添加自定义的...

    netty实现websocket server

    接着,定义 `ChannelInitializer`,在这个初始化器中,我们设置服务器的通道处理链,添加自定义的处理器,如 `WebSocketServerHandler`。最后,启动服务器并绑定指定的端口。 2. **WebSocketServerHandler**(对应...

    基于netty4 的udp字节数据接收服务

    1. **初始化 Bootstrap**:首先,我们需要创建一个 `Bootstrap` 实例,这是 Netty 中启动服务器的基本配置类。通过调用 `new Bootstrap()` 来创建。 2. **配置 Channel**:接着,我们指定通道类型为 `Nio...

    netty之UDP协议开发

    在Netty中,服务器的初始化通常涉及以下几个步骤: 1. 创建一个`EventLoopGroup`,这是Netty中的工作线程组,负责处理I/O事件。 2. 定义`Bootstrap`实例,它是服务器端的启动配置类。 3. 配置`Bootstrap`,包括绑定...

    netty搭建tcp服务,粘拆包解决

    然后,我们在通道初始化器中添加了一个LengthFieldBasedFrameDecoder来处理粘包和拆包问题,并添加了自己的业务处理器MyBusinessHandler来处理解码后的数据。 总的来说,Netty通过提供一系列的工具和组件,使得在...

Global site tag (gtag.js) - Google Analytics