共计 3036 个字符,预计需要花费 8 分钟才能阅读完成。
简介
Netty 为什么这么优良,它在 JDK 自身的 NIO 根底上又做了什么改良呢?它的架构和工作流程如何呢?请走进明天的 netty 系列文章之:netty 架构概述。
netty 架构图
netty 的次要作用就是提供一个简略的 NIO 框架能够和下层的各种协定相结合,最终实现高性能的服务器。上面是 netty 官网提供的架构图:
从上图能够看到 netty 的外围次要分成三局部,别离是可扩大的 event model、对立的 API、和弱小的 Byte Buffer。这三个个性是 netty 的制胜法宝。
上面会从这几个方面对 netty 的个性进行具体阐明,务必让读者理解到 netty 的优良之处。
丰盛的 Buffer 数据机构
首先从最底层的 Buffer 数据结构开始,netty 提供了一个 io.netty.buffer 的包,该包外面定义了各种类型的 ByteBuf 和其衍生的类型。
netty Buffer 的根底是 ByteBuf 类,这是一个抽象类,其余的 Buffer 类基本上都是由该类衍生而得的,这个类也定义了 netty 整体 Buffer 的基调。
netty 重写 ByteBuf 其目标是让最底层的 ByteBuf 比 JDK 自带的更快更适宜扩大。具体而言,netty 的 ByteBuf 要比 JDK 中的 ByteBuffer 要快,同时,扩大也更加容易,大家能够依据须要 Buf 进行自定义。另外 netty 有一些内置的复合缓冲区类型,所以能够实现通明的零拷贝。对于动静缓冲区类型来说,能够和 StringBuffer 一样按需扩大,十分好用。
零拷贝
什么是零拷贝呢?零拷贝的意思是在须要拷贝的时候不做拷贝。咱们晓得数据在应用底层协定进行传输的过程中是会被封装成为一个个的包进行传输。当传输的数据过大,一个包放不下的时候,还须要对数据进行拆分,目标方在接管到数据之后,须要对收到的数据进行组装,个别状况下这个组装的操作是对数据的拷贝,将拆分过后的对象拷贝到一个长的数据空间中。
比方上面的例子所示,将底层的 TCP 包组合称为顶层的 HTTP 包,然而并没有进行拷贝:
具体怎么拷贝呢?在上一篇文章中,咱们晓得 netty 提供了一个工具类办法 Unpooled, 这个工具类中有很多 wrapped 结尾的办法,咱们举几个例子:
public static ByteBuf wrappedBuffer(byte[]... arrays) {return wrappedBuffer(arrays.length, arrays);
}
public static ByteBuf wrappedBuffer(ByteBuf... buffers) {return wrappedBuffer(buffers.length, buffers);
}
public static ByteBuf wrappedBuffer(ByteBuffer... buffers) {return wrappedBuffer(buffers.length, buffers);
}
下面三个办法别离是封装 byte 数组、封装 ByteBuf 和封装 ByteBuffer,这些办法都是零拷贝。大家能够在理论的我的项目中依据理论状况,自行选用。
对立的 API
一般来说,在传统的 JDK 的 IO API 中,依据传输类型或者协定的不同,应用的 API 也是不同的。咱们须要对不同的传输方式开发不同的应用程序,不能做到对立。这样的后果就是无奈平滑的迁徙,并且在程序扩大的时候须要进行额定的解决。
什么是传输方式呢?这里是指以什么样的形式来实现 IO,比方传统的阻塞型 IO,咱们能够称之为 OIO,java 的 new IO 能够称之为 NIO,异步 IO 能够称之为 AIO 等等。
并且 JDK 中的传统 IO 和 NIO 是割裂的,如果在最开始你应用的是传统 IO,那么当你的客户数目增长到肯定的水平筹备切换到 NIO 的时候,就会发现切换起来异样简单,因为他们是割裂的。
为了解决这个问题,netty 提供了一个对立的类 Channel 来提供对立的 API。
先看下 Channel 中定义的办法:
从上图咱们能够看到应用 Channel 能够判断 channel 以后的状态,能够对其进行参数配置,能够对其进行 I / O 操作,还有和 channel 相干的 ChannelPipeline 用来解决 channel 关联的 IO 申请和事件。
应用 Channel 就能够对 NIO 的 TCP/IP,OIO 的 TCP/IP,OIO 的 UDP/IP 和本地传输都能提供很好的反对。
传输方式的切换,只须要进行很小的老本替换。
当然,如果你对现有的实现都不称心的话,还能够对外围 API 进行自定义扩大。
事件驱动
netty 是一个事件驱动的框架,事件驱动框架的根底就是事件模型,Netty 专门为 IO 定义了一个十分无效的事件模型。能够在不毁坏现有代码的状况下实现本人的事件类型。netty 中自定义的事件类型通过严格的类型层次结构跟其余事件类型辨别开来,所以可扩展性很强。
netty 中的事件驱动是由 ChannelEvent、ChannelHandler 和 ChannelPipeline 独特作用的后果。其中 ChannelEvent 示意产生的事件,ChannelHandler 定义了如何对事件进行解决,而 ChannelPipeline 相似一个拦截器,能够让用户自行对定义好的 ChannelHandler 进行管制,从而达到管制事件处理的后果。
咱们看一个简略的自定义 Handler:
public class MyHandler extends SimpleChannelInboundHandler<Object> {
@Override
public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
// 对音讯进行解决
ByteBuf in = (ByteBuf) msg;
try {log.info("收到音讯:{}",in.toString(io.netty.util.CharsetUtil.US_ASCII));
}finally {ReferenceCountUtil.release(msg);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
// 异样解决
cause.printStackTrace();
ctx.close();}
}
下面的例子中,咱们定义了如何对接管到的音讯和异样进行解决。在后续的文章中咱们会具体对 ChannelEvent、ChannelHandler 和 ChannelPipeline 之间的交互应用进行介绍。
其余优良的个性
除了下面提到的三大外围个性之外,netty 还有其余几个长处,不便程序员的开发工作。
比方对 SSL / TLS 的反对,对 HTTP 协定的实现,对 WebSockets 实现和 Google Protocol Buffers 的实现等等,表明 netty 在各个方面多个场景都有很强的利用能力。
总结
netty 是由三个外围组件形成:缓冲区、通道和事件模型,通过了解这三个外围组件是如何互相工作的,那么再去了解建设在 netty 之上的高级性能就不难了。
本文的例子能够参考:learn-netty4
本文已收录于 http://www.flydean.com/03-netty-architecture/
最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!
欢送关注我的公众号:「程序那些事」, 懂技术,更懂你!