zoukankan      html  css  js  c++  java
  • 02handler

    第一部分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 中对这个问题进行深入的探讨.

  • 相关阅读:
    Android应用开发提高系列(1)——《Practical Java 中文版》读书笔记(上)
    Android开发指南(35) —— Toast Notifications
    Android开发指南(37) —— Data Backup
    Android开发指南(36) —— Search
    [转]闲话操作系统1
    [从架构到设计]第二回:对象的旅行对象和人,两个世界,一样情怀
    [Thinking]从赢在中国,思考博客园的商业化
    [你必须知道的.NET]目录导航
    070809
    [CLR团队公告]CLR基础研究团队纲领
  • 原文地址:https://www.cnblogs.com/handsome1013/p/10002839.html
Copyright © 2011-2022 走看看