看了ChannelHandler我们就来看ChannelPipeline,这个类实现了责任链模式,我们就直接来看这个类的实现吧,看完后我们再看看javadoc的,这个写的很详细。
static final InternalLogger logger = InternalLoggerFactory.getInstance(DefaultChannelPipeline.class); static final ChannelSink discardingSink = new DiscardingChannelSink(); private volatile Channel channel; private volatile ChannelSink sink; private volatile DefaultChannelHandlerContext head; private volatile DefaultChannelHandlerContext tail; private final Map<String, DefaultChannelHandlerContext> name2ctx = new HashMap<String, DefaultChannelHandlerContext>(4);
这个里面的很好理解,channel就是和这个pipeline相关联的,sink也是和这个chanel相关联的对象,在pipeline里面会调度channelhandler的运行,在运行之后交给sink去处理传输等比较底层的操作。head和tail和name2ctx是这个pipe里面的一个ChanelHandlerContext的一个链式结构。
public ChannelSink getSink() { ChannelSink sink = this.sink; if (sink == null) { return discardingSink; } return sink; }
在这个里面可以看到单不传递sink的时候,默认使用discardingSink。
public void attach(Channel channel, ChannelSink sink) { if (channel == null) { throw new NullPointerException("channel"); } if (sink == null) { throw new NullPointerException("sink"); } if (this.channel != null || this.sink != null) { throw new IllegalStateException("attached already"); } this.channel = channel; this.sink = sink; }
这个里面实现channel和sink与这个pipeline的绑定,可以看出来,一个pipe会和一个channel和sink相绑定。
下来来看一下链表channelhandler的操作:
public synchronized void addFirst(String name, ChannelHandler handler) { if (name2ctx.isEmpty()) { init(name, handler); } else { checkDuplicateName(name); DefaultChannelHandlerContext oldHead = head; DefaultChannelHandlerContext newHead = new DefaultChannelHandlerContext(null, oldHead, name, handler); callBeforeAdd(newHead); oldHead.prev = newHead; head = newHead; name2ctx.put(name, newHead); callAfterAdd(newHead); } }
如果链表是空的时候实现初始化的一些操作,初始化head、tail变量,然后如果这个handler是实现LifeCycleChannelHandler的,就触发相应的动作。如果不是初始化的时候,就修改一下head指针,这个是比较好理解的。
同理addLast要修改相应的tail指针,addBefore修改prev,addAfter修改next指针,这些都比较好理解,就不多解释了。需要注意的是这些方法都是一些同步方法,都加了synchronized关键字,不知道这个地方是否可以使用ConcurrentHashMap。
中间的一些链表操作我们就不多讲了,但是写的感觉比较好,都比较精彩,作者的功底很高,我们接下来看一下调度:
public void sendUpstream(ChannelEvent e) { DefaultChannelHandlerContext head = getActualUpstreamContext(this.head); if (head == null) { logger.warn( "The pipeline contains no upstream handlers; discarding: " + e); return; } sendUpstream(head, e); } void sendUpstream(DefaultChannelHandlerContext ctx, ChannelEvent e) { try { ((ChannelUpstreamHandler) ctx.getHandler()).handleUpstream(ctx, e); } catch (Throwable t) { notifyHandlerException(e, t); } }
sendUpstream可以看到是从head开始走,然后找到下一个可以处理upstream的handler进行处理,然后让HandlerContext找到当前的下一个到下一个,慢慢的处理upstream事件,最后让交给ChannelSink组件。
public void sendDownstream(ChannelEvent e) { DefaultChannelHandlerContext tail = getActualDownstreamContext(this.tail); if (tail == null) { try { getSink().eventSunk(this, e); return; } catch (Throwable t) { notifyHandlerException(e, t); return; } } sendDownstream(tail, e); } void sendDownstream(DefaultChannelHandlerContext ctx, ChannelEvent e) { if (e instanceof UpstreamMessageEvent) { throw new IllegalArgumentException("cannot send an upstream event to downstream"); } try { ((ChannelDownstreamHandler) ctx.getHandler()).handleDownstream(ctx, e); } catch (Throwable t) { // Unlike an upstream event, a downstream event usually has an // incomplete future which is supposed to be updated by ChannelSink. // However, if an exception is raised before the event reaches at // ChannelSink, the future is not going to be updated, so we update // here. e.getFuture().setFailure(t); notifyHandlerException(e, t); } }
sendDownstream我们可以看到是找到最后一个Handler,然后找到前一个可以处理downstream事件的handler进行处理。
最后我们来看一下javadoc里面给的例子程序:
ChannelPipeline p = Channels.pipeline(); p.addLast("1", new UpstreamHandlerA()); p.addLast("2", new UpstreamHandlerB()); p.addLast("3", new DownstreamHandlerA()); p.addLast("4", new DownstreamHandlerB()); p.addLast("5", new UpstreamHandlerX());
我们可以看到,upstream事件的执行顺序是从头部找到第一个可以处理upstream事件的handler开始执行,所以执行顺序应该是1 --> 2 --> 5.
downstream事件的执行顺序是从尾部找到第一个可以处理downstream事件的handler开始执行,所以执行顺序是 4 --> 3.
在javadoc里面给我们指出了handler很灵活可以在运行的过程中自动的增加和删除,但是同时指出了下面的实现是错误的:
public class FirstHandler extends SimpleChannelUpstreamHandler { @Override public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) { // Remove this handler from the pipeline, ctx.getPipeline().remove(this); // And let SecondHandler handle the current event. ctx.getPipeline().addLast("2nd", new SecondHandler()); ctx.sendUpstream(e); } }
我们看出这个是错误的,remove方法会破坏这个链,所有应该在remove之前add,这个相信大家都不会犯这样的错误的。
相关推荐
《Netty源码深入分析》是由美团基础架构部的闪电侠老师所分享的一系列关于Netty源码解析的视频教程。以下将根据标题、描述、标签以及部分内容等信息,对Netty及其源码进行深入剖析。 ### Netty简介 Netty是基于...
#### 五、Netty源码分析实战案例 1. **ChannelHandlerContext与ChannelHandlerAdaptor详解**: - 分析`ChannelHandlerContext`的生命周期及其与`ChannelHandler`之间的交互方式。 - 深入理解`...
在深入探讨 Netty 源码之前,我们先了解一下 Netty 的核心概念和架构。 Netty 的主要特点包括: 1. **异步非阻塞I/O**:Netty 基于 NIO(Non-blocking I/O)库,利用了 Java 的 Channel 和 Selector,使得网络操作...
通过对Netty源码的深入分析,我们不仅能够了解到其内部工作原理,还能学习到许多优秀的设计思想和技术实践,这对于提高个人的技术水平和解决实际问题有着非常重要的意义。希望本文能够帮助读者更好地理解和掌握Netty...
### Netty源码分析 #### 1. Netty简介 Netty是一个高性能、异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器与客户端。其核心设计思想是简化网络编程的复杂性,并提供高度可定制化的API,...
《Netty源码深入剖析》一书旨在帮助读者深入了解Netty框架的工作原理和技术细节,从基础知识入手,逐步过渡到高级优化技巧,使开发者能够更好地掌握并应用Netty于实际项目中。 ### 一、Netty简介与核心特性 Netty...
以上内容便是Netty源码阅读笔记中提到的主要知识点。这些知识点构成了Netty框架的核心,涵盖了从数据容器到事件处理,再到粘包半包问题处理的完整流程。了解和掌握这些知识点对于深入使用Netty进行网络编程具有重要...
Netty的源码分析中,我们会关注以下几个关键点: 1. `NioEventLoop`:这是Netty针对Java NIO实现的EventLoop类,其中包含了对选择器(Selector)的管理和事件的分发。 2. `ChannelHandlerContext`:它是Netty的上...
Netty是一个高性能、异步事件驱动的网络应用框架...总的来说,这个压缩包提供了一个深入学习Netty源码的机会,通过阅读和实践,开发者能够掌握Netty的设计理念,提升网络编程的能力,并能根据需求定制自己的网络框架。
Java Netty 是一个高性能、异步...通过分析这些示例源码,我们可以深入理解 Netty 的工作原理,掌握如何在实际项目中构建高性能的网络应用。同时,这也将有助于我们了解如何处理异常、实现安全性以及优化网络通信性能。
1. **Netty源码分析系列文章**:这部分内容可能涵盖了Netty的整体架构、核心组件以及关键算法的解析,包括线程模型、缓冲区管理、事件驱动模型等方面。作者可能从整体上介绍了Netty的设计原则和实现方式。 2. **...
此外,源码分析有助于我们了解 Netty 如何处理网络事件、协议解析、线程调度等核心问题,对于提高开发水平和解决实际问题大有裨益。 书中可能涵盖了以下主题: - Netty 的设计理念和架构 - 异步事件驱动模型的实现...
由于Netty源码较庞大,所以文章建议通过分析几个关键组件来构建对整个框架的理解,从而避免在庞大的源码海洋中迷失方向。通过理解Netty的关键点,包括NIO的使用、事件处理机制、ChannelPipeline的运作以及Handler的...
JavaVIP课程深入分析了Netty源码,由Tom老师主讲,主要针对咕泡学院的高级Java学员。本文档详细探讨了Netty框架的核心组件及其使用,特别关注了BootStrap类在客户端和服务器端的角色。 BootStrap是Netty提供的一种...
源码分析中,你可以关注以下几个关键部分: - **Channel**:这是Netty中的基本抽象,代表一个网络连接,可以是服务器端的监听套接字,也可以是客户端的连接。 - **EventLoop**:每个Channel都会绑定到一个EventLoop...
这个“Netty 实战源码”压缩包很可能是为了帮助开发者深入理解 Netty 的工作原理和实现机制,通过源码分析来提升对网络编程的理解。 Netty 的核心特性包括: 1. **异步事件驱动**:Netty 使用非阻塞 I/O(NIO)...
通过分析ChannelPipeline,我们可以理解事件在Channel之间的传递机制。 依赖包的源码同样重要,因为Netty依赖于一些第三方库来完成特定功能。例如,依赖的Apache Commons Logging用于日志记录,Google的Guava库提供...
源码分析可以帮助理解这两者之间的交互,以及如何优雅地处理并发和多线程问题。 3. **ByteBuf**:Netty的自定义缓冲区类,提供了高效的数据读写和内存管理。通过源码,可以学习到如何优化数据传输和避免不必要的...
### Netty源码解析——服务启动过程 #### 一、Netty概述 Netty是一个高性能、异步事件驱动的网络应用框架,它被广泛应用于快速开发高性能协议服务器和客户端。Netty通过高度优化的设计和实现提供了低延迟和高吞吐...