导读
原创文章,转载请注明出处。
本文源码地址:netty-source-code-analysis
两篇开胃小菜过后,我的公众号已经有一些粉丝了,还有一些粉丝加了我的好友,有粉丝通过微信对我的文章表示了肯定,在此表示感谢。
哈喽,大佬好,您的公众号文章写的很细致,质量很好,希望以后能向您学习,沟通交流
我也是从一个netty小白开始学习的,在学习中也遇到过很多困惑和难点。我尽量用比较通俗易懂的语言来写文章(话说比较高档的词汇我也不会),咱们像讲故事一样,一点点地讲netty。曾经我遇到过的困惑,后来怎么理解的,这种地方我会详细展开来讲,把我怎样从困惑到理解的过程给大家讲出来。
本篇文章开始我们真正进入了netty的世界,今天先瞥一眼netty这个美人的优雅姿态,稍后咱们再对她的眼睛、鼻子仔细展开欣赏。
1 整体架构
这是非常重要的一张图,非常全面,它不仅仅包含了netty的组件,这张示意图是完整的一个用netty开发的网络应用。今天我们并不准备把这张图讲得明明白白,因为后边我们还会讲它,我会一点一点地带领大家分析这张图中的每一个细节。
Channel:一条连接。
PipeLine:一组ChannelHandler组成的双向链表。
ChannelHandler:ChannelHandler未在图中标出,PipeLine中每一个竖着的矩形就代表一个ChannelHandler,在添加到PipeLine中时会被封装成ChannelHandlerContext,所以PipeLine中的竖状矩形实际上是ChannelHandlerContext。ChannelHandler是我们注册的io事件和io命令处理器。
ByteBuf:字节数据容器。
ByteBufAllocator:分配ByteBuf的类。
EventLoop:就是一个线程。
EventLoopGroup:一组EventLoop。
2 Channel
常用的Channel实现有NioServerSocketChannel和NioSocketChannel,分别对应jdk的ServerSocketChannel和SocketChannel。
NioServerSocketChannel代表一个正在监听的端口,并没有真正建立一条连接。SocketChannel是一条真正的连接。
3 PipeLine
每一个Channel会被分配一个PipeLine实例,在PipeLine中有一条由多个ChannelHandlderContext组成的双向链向表,而一个ChannelHandlerContext中有一个ChannelHandler。其中的Head和Tail是PipeLine中内置的两个特殊的ChannelHandlerContext。其他的ChannelHandlerContext例如Decoder、BizHandler和Encoder均由用户添加。
PipeLine控制着Channel有关的事件和命令在各个ChannelHandlerContext中传播,这里我为什么说是事件和命令呢,读者也许看过其他的netty书籍,没出现过命令这个词汇,在其他的netty书籍中叫作入站事件和出站事件,也许我叫得不准确不专业,但是你继续往下看,看完我的连载,一定会明白这么叫的好处,关注我不要错过。
如果你看过其他netty书籍中叫作入站事件和出站事件,并且没看明白,搞得稀里糊涂,请联系我,一定让你弄明白。
4 ChannelHandler
处理Channel上有关的事件和命令的处理器,每一个ChannelHandler都会被封装成ChannleHandlerContext加入Pipeline。netty中的ChannelHandler分为ChannelInBoundHandler和ChannelOutBoundHandler,其中ChannelInBoundHandler处理Channel有关的事件,而ChannelOutBoundHandler处理Channel有关的命令。
当然一个ChannelHandler也可以同时是ChannelInBoundHandler和ChannelOutBoundHandler。
5 ByteBuf
ByteBuf对应着jdk中的ByteBuffer,是字节数据容器。netty的ByteBuf比jdk ByteBuffer更好用。
6 ByteBufferAllocator
上面我们提到了ByteBuf,那么ByteBuf从哪里生成呢,就是从ByteBufferAllocator生成的。
netty中的ByteBufferApplocator有两种,分别为PooledByteBufAllocator和UnPooledByteBufApplocator。顾名思义就知道一个是池化的,一个是非池化的,如果我们使用PooledByteBufAllocator,可以减少内存垃圾的产生,减少分配内存时的cpu开销。
7 EventLoop和EventLoopGroup
7.1 EventLoop
EventLoop实现了jdk ScheduledExecutorService接口,它其实是个线程池,而实际上该线程池中只有一个线程,所以可以直接认为这就是一个线程。netty中为每一个Channel绑定一个EventLoop,所有关于该Channel的端口绑定、建立连接、读写操作等全部由该EventLoop完成,如果调用线程不是EventLoop线程,那么该调用将被封装成异步任务交给EventLoop完成。这也就是为什么netty中到处都在返回Future的原因,我们在编程中调用的netty的大部分方法都会返回一个Future。
形象一点形容,假如汽车是一个Channel,司机是一个线程,但是并非绑定在Channel上的线程,发动机就是绑定在该Channel上的EventLoop。司机期望汽车行走,并不能直接推动汽车,司机挂档加油会被封装成行走指令(异步任务)交给发动机来完成。开空调、听歌曲被封装成发电指令(异步任务)需要发动机提供电力来完成。虽然司机在开车,但是汽车的动力来源是发动机,而不是司机。
7.2 EventLoopGroup
EventLoopGroup是一组EventLoop的集合,在创建Channel时netty会轮询地从EventLoopGroup中选择一个EventLoop绑定到Channel上,一旦绑定完成,这个EventLoop就要为这个Channel劳作一生。当然一个EventLoop上是可以绑定多个Channel的。
8 总结
本文我们已经对netty中的组件有了大致了解,包含Channel、PipeLine、ChannelHandler、ByteBuf、ByteBufAllocator、EventLoop等关键组件,接下来咱们对这些组件一一展分析。
关于作者
王建新,转转架构部资深Java工程师,主要负责服务治理、RPC框架、分布式调用跟踪、监控系统等。爱技术、爱学习,欢迎联系交流。
原创文章,码字不易,别忘了点赞、分享。