源码分析 -Netty:开篇
一 开篇,再谈架构
这里提出关于架构的几个问题,但不会给出答案,毕竟本人也是在不断探索中,并非权威。旨在引起思考,自己也会每日自省。
1、架构,是创造,还是选择?
2、架构的目的,到底是什么?
3、有哪些制约条件?
4、影响、决定架构的因素?
二 关于Netty
2.1 一些基础知识
IO相关:
Java NIO浅析
Linux IO模式及 select、poll、epoll详解
网络 IO 演变过程
一文读懂高性能网络编程中的I/O模型
这里只列一张图,用于简要说明常用的I/O方法和对比:
2.2 Netty定义
Netty官网,醒目位置描述:
Netty is an asynchronous event-driven network application framework
for rapid development of maintainable high performance protocol servers & clients.
Netty is a NIO client server framework which enables quick and easy development of network applications such as protocol servers and clients. It greatly simplifies and streamlines network programming such as TCP and UDP socket server.
翻译过来:
Netty是一个异步事件驱动网络应用框架,用于快速开发可维护的高性能协议服务器和客户端。
总结起来关键包括两点:
NIO异步编程模式,事件驱动的高性能客户端/服务端网络开发框架
显著简化了诸如TCP和UDP socket server的网络编程
2.3 特性
为不同的传输类型提供了统一的API——阻塞和非阻塞的Socket
基于灵活和可扩展的事件模型,可以清晰地分离关注点
高度可定制的线程模型——单线程、一个或多个线程池
真正的无连接数据报套接字支持(自 3.1版本 起)
除此之外,大家更了解的是Netty:
1、易用:详细记录的 Javadoc,用户指南和示例;只依赖JDK,没有其他依赖。
2.高性能:吞吐量更高、延迟更低、减少资源消耗、最小化不必要的内存复制。
3、安全性:完整的SSL/TLS和Start TLS支持;
4、以及较高的社区活跃度。
2.4 架构
下图是Netty官网的最新架构图:
从架构图可见,Netty可以分为Core、Transport Services、Protocol Support三大模块。
2.4.1 Core
核心模块,支持零拷贝的富字节缓冲区、通用的通信API、可扩展事件模型。
零拷贝(Zero-Copy)是一个比较重要的概念,解决了网络传输时多次数据拷贝的问题。Wiki中对零拷贝的定义如下:
"Zero-copy" describes computer operations in which the CPU does not perform the task of copying data from one memory area to another. This is frequently used to save CPU cycles and memory bandwidth when transmitting a file over a network.
翻译过来:
零拷贝技术是指计算机执行操作时,CPU不需要先将数据从某处内存复制到另一个特定区域。这种技术通常用于通过网络传输文件时节省 CPU 周期和内存带宽。
零拷贝解决什么问题:
减少甚至完全避免操作系统内核缓冲区之间进行数据拷贝操作。
2、帮助用户进程绕开操作系统内核空间直接访问硬件存储接口操作数据。
3、利用 DMA 而非 CPU 来完成硬件接口和内核缓冲区之间的数据拷贝,从而4、解放 CPU,使之能去执行其他的任务,提升系统性能。
想要更详细地了解Linux I/O和零拷贝相关内容,可以看这边腾讯的文章:Linux I/O 原理和 Zero-copy 技术全面揭秘。
2.4.2 Transport Service
传输服务,包括:
Socket & DatagramSocket
Http Tunnel(http隧道)
In-VM pipe(虚拟机内的管道)
这一块稍有些难以理解,对最上层的Socket大家都有使用,但对Http隧道 和 虚拟机内管道了解不多。简单来说,HTTP Tunnel是HTTP1.1引入的一个功能,用以解决明文的HTTP Proxy无法代理跑在TLS中的流量(即https)的问题,同时提供了作为任意流量的TCP通道的能力。
定义可查看:rfc2817 和 rfc7540。应用实例:HTTP Tunnel的应用实例
关于管道,目前没有查到特别明确的资料。不过初步分析是JVM的管道,用于处理进程间通讯。进程间常用的通讯方式可以初步划分为管道、共享内存、Socket三种。
2.4.3 协议支持
Http&WebSocket
SSL&StartTLS:加密协议
Google Protobuf:google开源的序列化协议
zlib/gzip Compression:zlib 和 gzip 压缩
LargeFile Transfer:支持大文件传输
RTSP:位于应用层的多媒体实时流传输协议
Legacy Text.Binary Protocols with Unit Testability:传统的文本。具有单元可测试性的二进制协议
2.4.4 Netty的逻辑架构
2.5 版本说明及代码结构
2.5.1 版本选择
目前从官网可下载的最新版本是4.1.60(发布于2021-03-09)。不过其实netty5在2016年就已经发布,但被官方舍弃,相关说明可以看作者的这个issue: Remove master branch #4466,原因如下:
netty5 中使用了 ForkJoinPool,增加了代码的复杂度,但是对性能的改善却不明显
多个分支的代码同步工作量很大
作者觉得当下还不到发布一个新版本的时候
在发布版本之前,还有更多问题需要调查一下,比如是否应该废弃 exceptionCaught, 是否暴露EventExecutorChooser等等。
而3.x 和 4.x中,4.x是官方推荐版本,也可以看到一直在维护更新,所以以4.x版本作为分析目标。
2.5.2 工程结构
目前项目中使用的实际上是4.1.25版本,但这里会选择相对新一点的4.1.50版本进行分析。Maven引入方式如下:
我们对比一下两个版本之间的结构差异:
从结构上看,增加了native-image.io.netty包,并且在native下增加了libnetty_resolver_dns_native_macos_x86_64.jnilib 和 libnetty_transport_native_epoll_aarch_64.so两个库文件。可以看到是对macos 和 aarch_64架构的支持。
2.5.3 源码结构
从上面也可以看到,源码结构并没有发生变化。按照包结构:
bootstrap
启动类所在包,Netty中服务端和客户端的启动类不同,这点切记!bootstrap中主要包括两类:bootstrap和config。channelFactory接口已标记为废弃,乱入了一个FailedChannel。
buffer
涉及底层的缓冲处理。上面Netty的特性和零拷贝概念,以及buffer包下的代码量都可以看到,这是很重的一块内容。
channel
管道,用于连接字节缓冲区Buf和另一端的实体,这个实例可以是Socket,也可以是File, 在Nio网络编程模型中, 服务端和客户端进行IO数据交互(得到彼此推送的信息)的媒介。
Netty对Jdk原生的ServerSocketChannel
进行了封装和增强,成了NioXXXChannel
, 相对于原生的JdkChannel, Netty的Channel增加了如下的组件:
id 标识唯一身份信息
可能存在的parent Channel
管道 pepiline
用于数据读写的unsafe内部类
NioEventLoop
handler
处理器,是Reactor模型中的重要角色。再引用一下这张图:
在Reactor经典模型中,Reactor查询到NIO就绪的事件后,分发到Handler,由Handler完成NIO操作和计算的操作。
Handler主要的操作为Channel缓存读、数据解码、业务处理、写Channel缓存,然后由Channel(代表client)发送到最终的连接终端。
resolver
分解器,针对Host、Addres、dns进行处理。
三 总结
本篇介绍Netty的架构和代码结构,下一篇将通过一个demo来分析Netty运行的主流程,并结合reactor模型进行解析。