Pipeline和ChannelHandler是Netty处理流程的重要组成部分,ChannelHandler对应一个个业务处理器,Pipeline则是负责将各个ChannelHandler串起来的“容器”,二者结合起来一起完成Netty的处理流程。

Pipeline

每个channel内部都会持有一个ChannelPipeline对象pipeline,pipeline默认实现DefaultChannelPipeline内部维护了一个DefaultChannelHandlerContext链表。

channel的读写操作都会走到DefaultChannelPipeline中,当channel完成register、active、read、readComplete等操作时,会触发pipeline的相应方法。

  1. 当channel注册到selector后,触发pipeline的fireChannelRegistered方法;
  2. 当channel是可用时,触发pipeline的fireChannelActive方法。(fireChannelActive触发一般是在fireChannelRegistered之后触发的);
  3. 当客户端发送数据时,触发pipeline的fireChannelRead方法;
  4. 触发pipeline的fireChannelRead方法之后会触发pipeline的fireChannelReadComplete方法。

DefaultChannelPipeline是Netty默认pipeline实现,对应代码如下:

public class DefaultChannelPipeline implements ChannelPipeline {
// head和tail是handler的处理链/上下文
final AbstractChannelHandlerContext head;
final AbstractChannelHandlerContext tail;
private final Channel channel; protected DefaultChannelPipeline(Channel channel) {
this.channel = ObjectUtil.checkNotNull(channel, "channel");
succeededFuture = new SucceededChannelFuture(channel, null);
voidPromise = new VoidChannelPromise(channel, true); tail = new TailContext(this);
head = new HeadContext(this);
head.next = tail;
tail.prev = head;
}
}

TailContext实现了ChannelOutboundHandler接口,HeadContext实现了ChannelInboundHandler和ChannelOutboundHandler接口,head和tail构成了一个链表。

对于Inbound操作,从head开始进行处理,向后遍历;对于OutBound操作,从tail开始处理,向前遍历。那么哪些操作是Inbound哪些是OutBound操作呢?

  • InBound:channelRegistered、channelActive、channelRead、channelReadComplete;
  • OutBound:bind、connect、close、flush等。

注意,HeadContext实现了ChannelInboundHandlerChannelOutboundHandler接口,对于OutBound操作,最后也是会走到HeadContext来处理的,其实TailContext只是一个浅封装,实际逻辑并不多。HeadContext 中包含了一个netty底层的socket操作类,对于bind/connect/disconnect/close/deregister/beginRead/read/wirte/flush操作都是由unsafe对象来完成的。

final class HeadContext extends AbstractChannelHandlerContext
implements ChannelOutboundHandler, ChannelInboundHandler { // netty的底层socket操作类
private final Unsafe unsafe; HeadContext(DefaultChannelPipeline pipeline) {
super(pipeline, null, HEAD_NAME, false, true);
unsafe = pipeline.channel().unsafe();
setAddComplete();
}
// ...
}

channelPipeline的channelHandlerContext链表是“责任链”模式的体现,一个请求的处理可能会涉及到多个channelHandler,比如decodeHandler、自定义的业务channelHandler和encodeHandler。业务channelHandler示例如下:

public class EchoHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ByteBuf in = (ByteBuf) msg;
System.out.println(in.toString(CharsetUtil.UTF_8));
ctx.write(msg);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}

在channelReadComplete方法中调用flush,其实会走到head.flush方法,最后调用unsafe.flush将数据发送出去。netty pipeline就是责任链(或者说是流水线)模式的体现,通过pipeline机制,使netty处理数据机制具有强大的扩展性和灵活性。

ChannelHandler

netty的channelHandler是channel处理器,基于netty的业务处理,不管多么复杂,都是由channelHandler来做的,可能涉及到多个channelHandler,channelHandler分为多种类型:encoder、decoder、业务处理等。

decoderHandler

decoderHandler大都是接收到数据之后进行转换或者处理的,基本都是ByteToMessageDecoder的子类,其类图如下:

ByteToMessageDecoder中会有一个数据暂存缓冲区,如果接收到数据不完整,可以先暂存下等到下次接收到数据时再处理。

encoderHandler

encoderHandler大都是将message转换成bytebuf数据,基本都是MessageToByteEncoder的子类,其类图如下:

业务channelHandler

业务处理channelHanler就是用户自定义的业务逻辑了,一般是在最后才addLast到channel.pipeline的,比如http处理逻辑如下:

ServerBootstrap boot = new ServerBootstrap();
boot.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.localAddress(8080)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline()
.addLast("decoder", new HttpRequestDecoder())
.addLast("encoder", new HttpResponseEncoder())
.addLast("aggregator", new HttpObjectAggregator(512 * 1024))
.addLast("handler", new HttpHandler());
}
});

DefaultChannelPipeline中的headContext(实现了ChannelOutboundHandler和ChannelInboundHandler)、tailContext(实现了ChannelOutboundHandler)和自定义的channelHandler(decoderHandler、ecoderHandler、channelHandler等,一般实现ChannelInboundHandler),通过ChannelHandlerContext的链接,组成了一个请求处理链。

注意,ChannelOutboundHandler和ChannelInboundHandler的顺序如何添加的,其实只要记住一条:ChannelOutboundHandler之间要保证顺序,ChannelInboundHandler之间要保证顺序,二者之间无需保证顺序。

channelHandler的运行流程图:

TailContesxt本身代码不多并且挺多方法都是"空实现",不过它的channelRead方法内部会执行ReferenceCountUtil.release(msg)释放msg占用的内存空间,也就是说在未定义用户ChannelHandler或者用户ChannelHandler的channelRead继续传递后续ChannelHandler的channelRead时,到TailContext的channelRead时会自动释放msg所占用内存。

推荐阅读

欢迎小伙伴关注【TopCoder】阅读更多精彩好文。

Netty Pipeline与ChannelHandler那些事的更多相关文章

  1. netty中Pipeline的ChannelHandler执行顺序案例详解

    一.netty的Pipeline模型 netty的Pipeline模型用的是责任链设计模式,当boss线程监控到绑定端口上有accept事件,此时会为该socket连接实例化Pipeline,并将In ...

  2. netty中的ChannelHandler和ChannelPipeline

    netty中的ChannelHandler和ChannelPipeline ChannelHandler 家族 https://www.w3cschool.cn/essential_netty_in_ ...

  3. Netty 源码 ChannelHandler(四)编解码技术

    Netty 源码 ChannelHandler(四)编解码技术 Netty 系列目录(https://www.cnblogs.com/binarylei/p/10117436.html) 一.拆包与粘 ...

  4. Netty 源码 ChannelHandler(三)概述

    Netty 源码 ChannelHandler(三)概述 Netty 系列目录(https://www.cnblogs.com/binarylei/p/10117436.html) 一.Channel ...

  5. Netty学习:ChannelHandler执行顺序详解,附源码分析

    近日学习Netty,在看书和实践的时候对于书上只言片语的那些话不是十分懂,导致尝试写例子的时候遭遇各种不顺,比如decoder和encoder还有HttpObjectAggregator的添加顺序,研 ...

  6. Netty 框架学习 —— ChannelHandler 与 ChannelPipeline

    ChannelHandler 1. Channel 生命周期 Channel 的生命周期状态如下: 状态 描述 ChannelUnregistered Channel 已经被创建,但还未注册到 Eve ...

  7. 【Netty学习】 ChannelHandler 改动及影响

    channelHandler 在Netty 4.x版本有了相当大的改动 http://netty.io/wiki/new-and-noteworthy.html   官网的更新改进说明. 以下节选官网 ...

  8. Netty入门(3) - ChannelHandler

    ChannelPipeline ChannelHandler实例的列表,用于处理或者截获通道的接收和发送数据,让用户可以在ChannelPipeline中完全控制一个事件以及处理ChannelHand ...

  9. netty pipeline.addLast

    pipeline有一个主要的实现类 DefaultChannelPipeline ,addLast顾名思义,就是在处理器链的最后添加一个channelHandler. 代码如下:@Override  ...

随机推荐

  1. Nginx 热部署和日志切割,你学会了吗?

    上篇文章,我们已经安装好 Nginx,并且配置好 Nginx 文件后,这个时候我就需要操作 Nginx 的命令行了,这篇文章主要讲解 Nginx 命令行相关知识,并通过日常遇到的热部署.切割日志文件场 ...

  2. docker入门级详解

    Docker 1 docker安装 yum install docker [root@topcheer ~]# systemctl start docker [root@topcheer ~]# mk ...

  3. 牛客练习赛51 **E-数列** (二分,贪心,构造)

    牛客练习赛51 E-数列 链接:https://ac.nowcoder.com/acm/contest/1083/E来源:牛客网 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 327 ...

  4. Https 与 iOS 信息安全

    转载自:swift-cafe 什么是 Https 咱们从最直观的说起. 我们平时在用电脑访问网页的时候,有时候会在地址栏的左边多出一个小锁的图标,就像这样: 这是大多数主流浏览器的一个通用做法,当我们 ...

  5. 谁说搞Java的不能玩机器学习?

    简介 机器学习在全球范围内越来越受欢迎和使用. 它已经彻底改变了某些应用程序的构建方式,并且可能会继续成为我们日常生活中一个巨大的(并且正在增加的)部分. 没有什么包装且机器学习并不简单. 它对许多人 ...

  6. 爬虫学习--Urllib库基本使用 Day1

    一.Urllib库详解 1.什么是Urllib Python内置的HTTP请求库 urllib.request    请求模块(模拟实现传入网址访问) urllib.error             ...

  7. 开发板,pc,虚拟机三者如何互相ping通

    1 安装虚拟机时,主机和虚拟机必须是桥接网卡,保证了ip 同一:192,168,1,xx 2 打开虚拟机之前,先把pc机的无线网卡禁用掉只能使用本地连接,pc通过网线连接上网,打开虚拟机,命令行输入: ...

  8. Python面向对象 | 静态方法 staticmethod

    静态方法是类中的函数,不需要实例.静态方法主要是用来存放逻辑性的代码,逻辑上属于类,但是和类本身没有关系,也就是说在静态方法中,不会涉及到类中的属性和方法的操作.可以理解为,静态方法是个独立的.单纯的 ...

  9. 物联网安全himqtt防火墙数据结构之红黑树源码分析

    物联网安全himqtt防火墙数据结构之红黑树源码分析 随着5G的发展,物联网安全显得特别重要,himqtt是首款完整源码的高性能MQTT物联网防火墙 - MQTT Application FireWa ...

  10. Secure CRT注册码

    secure CRT 把记忆的东西放在这就行了,:)   SecureCRT 5.2.2的注册码 Name:          Apollo InteractiveCompany:    Apollo ...