浅谈高性能的Netty

1.什么是Netty?

笔者最近在业余时间正好在看netty的一个课程,感觉netty挺有意思的,和我们熟悉的spring mvc那一套有点不一样,而他也是挺主流的一个技术框架,就刚好借着写博客的机会小总结一下,因实践经验不多,了解不深,文章之中如有错漏,欢迎各位看官拍砖。
“Netty是一个异步事件驱动的网络应用框架,可用于快速开发可维护且高性能的协议服务器和客户端。”
上面这句话是官网的一个介绍,简单明了,下面来看看Netty的构成图:
在这里插入图片描述
从最小面的核心部分开始看,首先是Netty中的零拷贝,这里的零拷贝和OS的零拷贝又有所区别,后面会单独提到。
再上一层是Netty封装好的通用通信API,在JDK中使用OIO和NIO所用的API是不一致的,在netty中是统一的,只是方法参数不一样,大大提高了开发效率最后是可扩展的事件模型,netty是基于异步的事件模型驱动的。
最上面就是列出的netty对各种协议的支持,其中netty还提供了对Google Protobuf的支持,Google Protobuf是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化,它很适合做数据存储或 RPC 数据交换格式,它和Thrift是类似的,不过Thrift支持的语言更多,笔者所在的公司所研发的RPC框架,即是使用的Thrift作为通讯协议。上图还有一个RTSP实时串流协议(Real Time Streaming Protocol,RTSP),主要是用于游戏或视频等媒体传输。其余的协议大家应当都比较熟,此处不做赘述。

2.Netty的特点?

下面的表格,是《Netty in action》中列出的Netty几个比较突出的特点,现在对其中几个进行单独分析:
在这里插入图片描述

2.1零拷贝

2.1.1系统层面的零拷贝

在 OS 层面上的 Zero-copy 通常指避免在 用户态(User-space) 与 内核态(Kernel-space) 之间来回拷贝数据. 例如 Linux 提供的 mmap 系统调用, 它可以将一段用户空间内存映射到内核空间, 当映射成功后, 用户对这段内存区域的修改可以直接反映到内核空间; 同样地, 内核空间对这段区域的修改也直接反映用户空间. 正因为有这样的映射关系, 我们就不需要在 用户态(User-space) 与 内核态(Kernel-space) 之间拷贝数据, 提高了数据传输的效率.
而在netty中的零拷贝

2.1.2Netty中的零拷贝

Netty的 Zero-coyp 完全是在用户态(Java 层面)的, 它的 Zero-copy 的更多的是偏向于 优化数据操作 这样的概念.Netty零拷贝主要体现在一下三方面:

  1. Netty的接收和发送ByteBuffer采用DIRECT BUFFERS,使用堆外直接内存进行Socket读写,不需要进行字节缓冲区的二次拷贝。如果使用传统的堆内存(HEAP BUFFERS)进行Socket读写,JVM会将堆内存Buffer拷贝一份到直接内存中,然后才写入Socket中。相比于堆外直接内存,消息在发送过程中多了一次缓冲区的内存拷贝。
  2. Netty提供了组合Buffer对象,可以聚合多个ByteBuffer对象,用户可以像操作一个Buffer那样方便的对组合Buffer进行操作,避免了传统通过内存拷贝的方式将几个小Buffer合并成一个大的Buffer。
  3. Netty的文件传输采用了java NIO 的transferTo方法,它可以直接将文件缓冲区的数据发送到目标Channel,避免了传统需要先暂存再多拷贝一次的问题。
    JAVA NIO文件传输
    在这里插入图片描述

2.2 Netty的线程模型

下图是netty所使用的主从Reactor模型。
在这里插入图片描述
主从 Reactor 线程模型的特点是:服务端用于接收客户端连接的不再是个 1 个单独的 NIO 线程,而是一个独立的 NIO 线程池。Acceptor 接收到客户端 TCP 连接请求处理完成后(可能包含接入认证等),将新创建的 SocketChannel 注册到 IO 线程池(sub reactor 线程池)的某个 IO 线程上,由它负责 SocketChannel 的读写和编解码工作。Acceptor 线程池仅仅只用于客户端的登陆、握手和安全认证,一旦链路建立成功,就将链路注册到后端 subReactor 线程池的 IO 线程上,由 IO 线程负责后续的 IO 操作。
它的工作流程总结如下:
从主线程池中随机选择一个 Reactor 线程作为 Acceptor 线程,用于绑定监听端口,接收客户端连接;
Acceptor 线程接收客户端连接请求之后创建新的 SocketChannel,将其注册到主线程池的其它 Reactor 线程上,由其负责接入认证、IP 黑白名单过滤、握手等操作;
步骤 2 完成之后,业务层的链路正式建立,将 SocketChannel 从主线程池的 Reactor 线程的多路复用器上摘除,重新注册到 Sub 线程池的线程上,用于处理 I/O 的读写操作。

3.Netty能做什么?

  • Http的server和client
  • Websocket的server和client
  • RPC框架

下面是笔者去netty官网找到的netty相关项目,其中熟悉的面孔也很多,像 akkasparkgRPC 这么多成熟知名的项目都是基于netty来的,可见Netty早就已经是一个主流的网络应用框架了。

在这里插入图片描述

4.Echo Server实战

实战这部分,我觉得直接放一个简单的Echo demo代码吧,也就几十行,看注释基本能明白。如有使用需要,这部分只要看官网的doc就好了,笔者一直相信学习官网demo,是学习新技术上手最快也最少走弯路的。

服务端两个类:

EchoServer.java ,服务端入口类。
在这里插入图片描述
Netty是基于责任链模式封装的,这是服务端自定义的一个handler类:
在这里插入图片描述

客户端两个类:

client入口类,其实和服务端套路很像,事实上整个netty的套路都是类似,很好上手。
在这里插入图片描述
客户端的handler类:
在这里插入图片描述

5.结语

以上就是本篇博客的全部内容了,总结的粗浅,还望大家见谅。笔者也还在对netty的继续学习中,欢迎大家多多批评指教,期待向大家学习。

by eric02,li

发布了87 篇原创文章 · 获赞 42 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/vipshop_fin_dev/article/details/103553768