`
jishuaige
  • 浏览: 10303 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

netty探索之旅五

 
阅读更多
netty中的管道--ChannelPipeline机制

前面我们在提到ChannelPipeline的地方只是简单的描述了一下,这里我们再进一步深入到ChannelPipeline的内部中。

在netty中每一个channel都会只有一个ChannelPipeline与之对应.

在看看AbstractChannel类的构造函数吧,channel的初始化都会到这个父类的构造函数中。
protected AbstractChannel(Channel parent) {
        this.parent = parent;
        unsafe = newUnsafe();
        pipeline = newChannelPipeline();
    }

 protected DefaultChannelPipeline newChannelPipeline() {
        return new DefaultChannelPipeline(this);
    }

DefaultChannelPipeline的构造函数:
protected DefaultChannelPipeline(Channel channel) {
        this.channel = ObjectUtil.checkNotNull(channel, "channel");

        tail = new TailContext(this);
        head = new HeadContext(this);

        head.next = tail;
        tail.prev = head;
    }

保存与之关联的channel到DefaultChannelPipeline对象中。创建了tail和head字段,这个是双向链表的头和尾,在DefaultChannelPipeline维护了一个以AbstractChannelHandlerContext为节点的双向链表。TailContext继承了AbstractChannelHandlerContext实现了ChannelInboundHandler。
HeadContext继承了AbstractChannelHandlerContext实现了ChannelOutboundHandler和ChannelInboundHandler。

看看HeadContext的构造函数:
HeadContext(DefaultChannelPipeline pipeline) {
            super(pipeline, null, HEAD_NAME, false, true);
            unsafe = pipeline.channel().unsafe();
            setAddComplete();
        }

HeadContext调用了父类AbstractChannelHandlerContext的构造器,并传入参数inbound=false,outbound= true。TailContext的构造器与HeadContext的相反。传入参数inbound=true,outbound=false。

HeadContext是一个outboundHandler,TailContext是一个inboundHandler。HeadContext和TailContext即是一个ChannelHandler,又是一个ChannelHandlerContext。

以上就是一个通道的DefaultChannelPipeline自身的组成参数。那么我们在使用管道的时候,都会向里面添加自定义的ChannelHandler。这样才会发挥出DefaultChannelPipeline的魅力。
在EchoClient中:
EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            b.group(group)
             .channel(NioSocketChannel.class)
             .option(ChannelOption.TCP_NODELAY, true)
             .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 EchoClientHandler());
                 }
             });
            ChannelFuture f = b.connect(HOST, PORT).sync();
            f.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully();
        }

在调用.handler方法时候传入了ChannelInitializer对象,它最终实现了ChannelHandler接口,那么在ChannelInitializer对象是怎么添加到DefaultChannelPipeline的?

handler方法会调用到AbstractBootstrap实例的handler方法:
public B handler(ChannelHandler handler) {
        if (handler == null) {
            throw new NullPointerException("handler");
        }
        this.handler = handler;
        return (B) this;
    }

这个方法做了一件事件就是把传入的handler实例赋值给AbstractBootstrap的handler变量。

还记得我们在第三节讲到的initAndRegister方法不。在此方法中调用了channel = channelFactory().newChannel()方法创建了一个新的channel后,就会调用init方法:
这个方法在Bootstrap类中会重写:
@Override
    @SuppressWarnings("unchecked")
    void init(Channel channel) throws Exception {
        ChannelPipeline p = channel.pipeline();
        p.addLast(handler());

        final Map<ChannelOption<?>, Object> options = options();
        synchronized (options) {
            setChannelOptions(channel, options, logger);
        }

        final Map<AttributeKey<?>, Object> attrs = attrs();
        synchronized (attrs) {
            for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
                channel.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
            }
        }
    }

handler()获取我们在刚刚赋值的handler变量的值(ChannelInitializer),然后添加到管道的最后:调用的addLast方法。看看DefaultChannelPipeline的addLast方法:
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
        final AbstractChannelHandlerContext newCtx;
        synchronized (this) {
            checkMultiplicity(handler);
            newCtx = newContext(group, filterName(name, handler), handler);
            addLast0(newCtx);
            if (!registered) {
                newCtx.setAddPending();
                callHandlerCallbackLater(newCtx, true);
                return this;
            }
            EventExecutor executor = newCtx.executor();
            if (!executor.inEventLoop()) {
                newCtx.setAddPending();
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        callHandlerAdded0(newCtx);
                    }
                });
                return this;
            }
        }
        callHandlerAdded0(newCtx);
        return this;
    }

newContext(group, filterName(name, handler), handler);为这个handler创建一个与之关联的DefaultChannelHandlerContext实例。在DefaultChannelHandlerContext中有一个handler变量保存传入关联的handler实例(ChannelInitializer)。
看看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;
    }

看2个方法:
private static boolean isInbound(ChannelHandler handler) {
        return handler instanceof ChannelInboundHandler;
    }

    private static boolean isOutbound(ChannelHandler handler) {
        return handler instanceof ChannelOutboundHandler;
    }

这两个方法就是判断了Handler是不是实现了ChannelOutboundHandler或者是ChannelInboundHandler接口。这个2个方法返回的true或者是false都会传入到父类中:AbstractChannelHandlerContext。

ChannelInitializer实现了ChannelInboundHandler接口,因此这里实例化的 DefaultChannelHandlerContext的inbound = true,outbound = false。这两个字段关系到在pipeline中的事件的流向与分类。

在创建好DefaultChannelHandlerContext后(在pipeline的链表中只会加入ChannelHandlerContext对象),会将他插入到pipeline的双向链表中。
private void addLast0(AbstractChannelHandlerContext newCtx) {
        AbstractChannelHandlerContext prev = tail.prev;
        newCtx.prev = prev;
        newCtx.next = tail;
        prev.next = newCtx;
        tail.prev = newCtx;
    }

这个是典型的双向链表的插入操作。

还记得channel的注册过程不,在第三节的时候,我们讨论的channel的注册过程,在此过程中调用了AbstractUnsafe.register0方法中调用了pipeline.fireChannelRegistered() :
@Override
    public final ChannelPipeline fireChannelRegistered() {
        AbstractChannelHandlerContext.invokeChannelRegistered(head);
        return this;
    }

AbstractChannelHandlerContext中的invokeChannelRegistered:
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();
                }
            });
        }
    }

next变量的实例就是HeadContext,invokeChannelRegistered调用的AbstractChannelHandlerContext的:
private void invokeChannelRegistered() {
        if (invokeHandler()) {
            try {
                ((ChannelInboundHandler) handler()).channelRegistered(this);
            } catch (Throwable t) {
                notifyHandlerException(t);
            }
        } else {
            fireChannelRegistered();
        }
    }

这里handler()调用的是DefaultChannelHandlerContext中的handler()方法。返回的就是ChannelInitializer对象。这就是和前面关联起来了。
public final void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        if (initChannel(ctx)) {
            ctx.pipeline().fireChannelRegistered();
        } else {
            ctx.fireChannelRegistered();
        }
    }

initChannel方法:
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) {
                exceptionCaught(ctx, cause);
            } finally {
                remove(ctx);
            }
            return true;
        }
        return false;
    }

initChannel((C) ctx.channel());熟悉吧,这个就会调用我们在EchoClient类中覆盖的方法:
.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 EchoClientHandler());
                 }
             });

再调用remove(ctx)后,pipeline里面就是head---->EchoClientHandler---->tail。
分享到:
评论

相关推荐

    《Netty进阶之路 跟着案例学Netty》.rar

    Java进阶技术-netty进阶之路

    Netty进阶之路-跟着案例学Netty

    《Netty进阶之路-跟着案例学Netty》是由知名技术专家李林峰撰写的一本专为Java开发者深入理解Netty框架而准备的书籍。这本书旨在通过实例教学,帮助读者全面掌握Netty的核心特性和实战技巧,提升网络编程的能力。 ...

    《Netty进阶之路 跟着案例学Netty》_李林锋_

    Netty进阶之路 跟着案例学Netty 整本书无密码,Netty进阶之路 跟着案例学Netty

    Netty进阶之路:跟着案例学Netty 完整版.pdf

    《Netty进阶之路:跟着案例学Netty》中的案例涵盖了Netty的启动和停止、内存、并发多线程、性能、可靠性、安全等方面,囊括了Netty绝大多数常用的功能及容易让人犯错的地方。在案例的分析过程中,还穿插讲解了Netty...

    netty4-netty5.rar

    ChannelHandlerAdapter 4.X版本和5.X版本的差别很大。ChannelRead是属于5.X版本的4.X版本没有这个方法,所以如果要用ChannelRead。可以更换5.X版本的Netty。

    高清_书签_Netty进阶之路 跟着案例学Netty.zip

    精选自1000多个一线业务实际案例,从原理到实践全景式讲解Netty项目实践,快速领悟Netty专家花大量时间积累的经验,提高编程水平及分析解决问题的能力,《Netty木又威指南》作者力作,众专家力荐 Netty将Java NIO...

    netty学习之ServerChannel

    5. **NioServerSocketChannel**:在Netty中,ServerChannel的具体实现之一是`NioServerSocketChannel`,它使用Java NIO(非阻塞I/O)来处理连接。NioServerSocketChannel监听指定的端口,并在有新的连接请求时创建新...

    读书笔记:Netty权威指南学习之旅.zip

    读书笔记:Netty权威指南学习之旅

    Netty5.rar

    在本文中,我们将深入探讨 Netty 5 版本中的关键概念和特性,以及如何创建一个简单的客户端-服务器实例。 Netty 的核心设计理念是基于 NIO(非阻塞 I/O)模型,这使得它在处理大量并发连接时表现优秀。它提供了丰富...

    netty之hello world

    Netty 是一个高性能、异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。在本文中,我们将深入探讨Netty的基本概念,通过“Hello World”范例来理解其工作原理。 首先,让我们理解...

    NIO+Netty5视频教程与Netty源码剖析视频教程

    《NIO+Netty5视频教程与Netty源码剖析视频教程》是一份全面解析网络编程框架Netty的教育资源,旨在帮助学习者深入理解和应用NIO(非阻塞I/O)以及Netty5的核心技术。该教程分为两个主要部分,分别针对理论分析和实战...

    使用netty5进行udp网络通讯

    这个小程序使用netty5进行udp网络通讯,客户端有两种,1:用netty5类库发送DatagramPacket和接收 2:直接使用DatagramSocket发送接收DatagramPacket 先运行netty_server的QuoteOfTheMomentServer, 在运行netty_...

    Netty进阶之路--跟着案例学part2

    在《Netty进阶之路:跟着案例学Netty》中,作者将在过去几年实践中遇到的问题,以及Netty学习者咨询的相关问题,进行了归纳和总结,以问题案例做牵引,通过对案例进行剖析,讲解问题背后的原理,并结合Netty源码分析...

    Netty实战.epub_netty实战epub_netty实战epub_netty_

    《Netty实战》这本书是针对Java网络编程框架Netty的一本深入实践教程,旨在帮助读者掌握Netty的核心特性和实际应用。Netty是一款高性能、异步事件驱动的网络应用程序框架,广泛应用于各种分布式系统、微服务架构以及...

    Netty基础,用于学习Netty,参考黑马程序员的netty教程

    Netty基础,用于学习Netty,参考黑马程序员的netty教程

    Netty5用户指南

    5. 自顶向下的学习方法:Netty的用户指南建议,如果学习者偏好从应用层面开始理解Netty,可以从架构概述章节开始,然后再回归到入门指南来实现具体的客户端和服务端的构建。 6. 示例与入门:Netty指南中的例子包括...

    跟闪电侠学Netty:Netty即时聊天实战与底层原理-book-netty.zip

    5. **即时聊天实战**:书中会介绍如何使用Netty构建一个简单的即时聊天应用,涉及用户注册、登录、消息发送、接收等核心功能的实现。 6. **性能优化**:Netty的性能优化策略,如内存管理、线程模型调整、缓冲区复用...

    Netty 教程 Netty权威指南

    **Netty 深度解析** Netty 是一个高性能、异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。它广泛应用于各种领域,如分布式系统、云计算、游戏服务器、大数据传输等。Netty 的设计...

    Netty实战 电子版.pdf_java_netty_服务器_

    《Netty实战》是针对Java开发者的一本技术指南,它深入介绍了如何利用Netty这个高性能、异步事件驱动的网络应用程序框架来构建高效且可扩展的网络应用。Netty不仅简化了网络编程的复杂性,还提供了丰富的特性和组件...

    网络编程之Netty一站式精讲.rar

    5. 扩展性和灵活性:Netty允许自定义编码解码器,支持多种数据格式和协议。 6. 快速故障恢复:通过心跳检测和自动重连机制,提高了系统的稳定性和可靠性。 三、Netty核心组件 1. Channel:表示网络连接,负责数据...

Global site tag (gtag.js) - Google Analytics