Netty是一个非阻塞的,时间驱动的网络框架。web
一个Netty程序开始于一个Bootstrap类,Bootstrap类是Netty提供的一个能够经过简单配置来设置或“引导”程序的重要的类。数据库
Netty中设计了Handlers来处理特定的“event”和设置Netty中的事件,从而来处理多个协议和数据。能够自定义Handler用来将Obeject转换成byte[],或者将byte[]转换成Object,或者定义handler处理异常。数组
ChannelInboundHandler用来接收消息,当程序须要返回信息时,在ChannelInboundHandler里write/flush数据。能够认为程序的业务逻辑都是在ChannelInboundHandler里处理的,业务逻辑的生命周期是在ChannelInboundHandler中。promise
链接客户端或者绑定服务器端须要知道如何发送或接收消息,能够经过不一样类型的Handler来实现。Netty经过ChannelInitializer来配置Handler,ChannelInitializer经过ChannelPipeline来添加Handler。ChannelInitializer自己也是一个ChannelHander,在添加完其余的Handler以后会本身从ChannelPipeline删除本身。安全
Netty中全部的I/O操做都是异步的,Netty使用Futrures或者ChannelFutures来达到注册监听异步操做是否成功的目的。Furture注册一个监听,当操做成功或失败时会通知,ChannelFutures封装一个操做的相关信息,操做被执行时,会马上返回ChannelFutures。服务器
Netty中的EventLoopGroup包含一个或多个EventLoop,而EventLoop就是一个Channel执行实际工做的线程。EventLoop老是绑定一个单一的线程,在其生命周期内不会改变。这保证了在Netty I/O操做中,程序不须要同步,由于一个指定通道的全部I/O始终由同一个线程来执行。不少Channel会共享同一个EventLoop。网络
Bootstrap用来链接远程主机,有1个EventLoopGroup(单例的)。框架
ServerBootstrap用来绑定本地端口,有2个EventLoopGroup(实际上使用的是相同的实例)。异步
ServerBootstrap在服务器端监听一个端口,轮询客户端的“Bootstrap”或DatagramChannel是否链接服务器,一般须要调用“Bootstrap”类的connect()方法,可是也能够先调用bind()再调用connect()进行链接,以后使用的Channel包含在bind()返回的ChannelFuture中。socket
一个ServerBootstrap能够认为有2个channel组,第一组包含一个单例的ServerChannel,表明持有了一个保存本地端口的socket;第二组包含全部的Channel,表明服务器已经接受了的连接。
上图中EventLoopGroup A的惟一目的就是接收全部链接请求,而后交给EventLoopGroup B。
EventLoopGroup将是程序性能的瓶颈,由于时间循环忙于处理连接请求,没有多余的资源来处理业务逻辑,如有两个EventLoops,即便是在高负载的状况下,全部的链接也会被接收,由于EventLoop接收连接不会和已链接并进行业务逻辑处理的EventLoop共享资源。
ChannelHandler是一段执行业务逻辑的代码,它们来来每每的经过ChannelPipeline.
Netty中有两个方向的数据流,入站(ChannelInboundHandler)和出站(ChannelOutboundHandler),若数据是从应用程序到远程主机,则是出站(outbound),若是是从远程主机到应用程序则是入站(Inbound)。
ChannelPipleline是一个管理ChannelHandler的容器,ChannelHandler在程序Bootstrap阶段被添加到ChannelPipleline中,被添加的顺序将决定处理数据的顺序,
访问非堆缓冲区ByteBuf的数组会致使UnsupportedOperationException,可使用ByteBuf.hasArray()来检查是否支持访问数组。ByteBuf.hasArray()的原理
Direct Buffer(直接缓冲区)在堆以外直接分配内存。直接缓冲区不会占用堆空间容量,使用时应该考虑到应用程序要使用的最大内存容量以及如何限制它。直接缓冲区在使用Socket传递数据时性能很好,由于若使用间接缓冲区,JVM会先将数据复制到直接缓冲区再进行传递。直接缓冲区不支持数组访问数据。
复合缓冲区,咱们能够建立多个不一样的ByteBuf,而后提供一个这些ByteBuf组合的视图。Netty提供了CompositeByteBuf类来处理复合缓冲区,CompositeByteBuf只是一个视图,CompositeByteBuf.hasArray()老是返回false。一条消息由header和body两部分组成,将header和body组装成一条消息发送出去,可能body相同,只是header不一样,使用
CompositeByteBuf就不用每次都从新分配一个新的缓冲区。
注意经过索引访问时不会推动读索引和写索引,咱们能够经过ByteBuf的readerIndex()或writerIndex()。ByteBuf提供两个指针变量支付读和写操做,读操做是使用readerIndex(),写操做时使用writerIndex()。这和JDK的ByteBuffer不一样,ByteBuffer只有一个方法来设置索引,因此须要使用flip()方法来切换读和写模式。
调用ByteBuf.clear()能够设置readerIndex和writerIndex为0,clear()不会清除缓冲区的内容,只是将两个索引值设置为0。请注意
ByteBuf.clear()与JDK的ByteBuffer.clear()的语义不一样。ByteBuf.discardReadBytes()能够用来清空ByteBuf中已读取的数据,从而使ByteBuf有多余的空间容纳新的数据,可是
discardReadBytes()可能会涉及内存复制,由于它须要移动ByteBuf中可读的字节到开始位置,这样的操做会影响性能,通常在须要立刻释放内存的时候使用收益会比较大。和discardReadBytes()相比,clear()是便宜的,由于clear()不会复制任何内存。
Netty使用reference-counting(引用计数)的时候知道安全释放Buf和其余资源,虽然知道Netty有效的使用引用计数,这都是自动完成的。
获取ByteBufAllocator对象很容易,能够从Channel的alloc()获取,也能够从ChannelHandlerContext的alloc()获取。
永远只有一个channelActive和channelInactive的状态,由于一个通道在其生命周期内只能链接一次,以后就会被回收;从新链接,则是建立一个新的通道。
ChannelHandlerAdapter实现了父类的全部方法,基本上就是传递事件到ChannelPipeline中的下一个ChannelHandler直到结束ChannelInboundHandlerAdapter的channelRead方法处理完消息后不会自动释放消息,若想自动释放收到的消息,可使用SimpleChannelInboundHandler。
若是消息被消费而且没有被传递到ChannelPipeline中的下一个ChannelOutboundHandler,那么就须要调用ReferenceCountUtil.release(message)来释放消息资源。一旦消息被传递到实际的通道,它会自动写入消息或在通道关闭时释放。重要的是要记得释放致远并通知ChannelPromise,若ChannelPromise没有被通知可能会致使其中一个ChannelFutureListener不被通知去处理一个消息。例如,应当使用以下的方式:
public class DiscardOutBoundler extends ChannelOutboundHandlerAdapter{ @override public void write(ChannelHandlerContext cxt , Object msg, ChannlePromise promise) throws Exception{ ReferenceCountUtil.release(msg); promise.setSuccess(); } }
消息被编码后解码后会自动经过ReferenceCountUtil.release(message)释放,若是不想释放消息可使用ReferenceCountUtil.retain(message)
有时候须要从另外一个Channel引导客户端,例如写一个代理或须要从其余系统检索数据。从其余系统获取数据时比较常见的,有很 多Netty应用程序必需要和企业现有的系统集成,如Netty程序与内部系统进行身份验证,查询数据库等。固然,你能够建立一个新的引导,这样作没有什么不妥,只是效率不高,由于要为新建立的客户端通道使用另外一个EventLoop,如 果须要在已接受的通道和客户端通道之间交换数据则须要切换上下文线程。Netty对这方面进行了优化,能够讲已接受的通道经过 eventLoop(...)传递到EventLoop,从而使客户端通道在相同的EventLoop里运行。这消除了额外的上下文切换工做,由于EventLoop继承 于EventLoopGroup。除了消除上下文切换,还能够在不须要建立多个线程的状况下使用引导。 一个EventLoop由一个线程执行,共享EventLoop能够肯定全部的Channel都分配给同一线程的 EventLoop,这样就避免了不一样线程之间切换上下文。
比较麻烦的是建立通道后不得不手动配置每一个通道,为了不这种状况,Netty提供了ChannelOption来帮助引导配置。这些选项会 自动应用到引导建立的全部通道,可用的各类选项能够配置底层链接的详细信息,如通道“keep-alive(保持活跃)”或“timeout(超时)”的特性。Netty提供了通道属性(channel attributes)。属性能够将数据和通道以一个安全的方式关联,这些属性只是做用于客户端和服务器的通道。例如,例如客户端请求web服务器应 用程序,为了跟踪通道属于哪一个用户,应用程序能够存储用的ID做为通道的一个属性。任何对象或数据均可以使用属性被关联到一个通道。使用ChannelOption和属性可让事情变得很简单,例如Netty WebSocket服务器根据用户自动路由消息,经过使用属性,应用程序 能在通道存储用户ID以肯定消息应该发送到哪里。应用程序能够经过使用一个通道选项进一步自动化,给定时间内没有收到消息将自动断开链接。