目录
一、为什么使用Netty
1. Netty编程相比NIO编程的优势
2. Netty 相比其它网络应用框架的优势
二、让我们走进Netty
1. 简介
2. 设计目标
3. 主要特点
4. Netty的作者
5. Netty 的地位
6. Netty 的优势
五、Netty版本说明
六、Netty架构设计
1. 线程模型基本介绍
2. 传统模型
3. Reactor模式
3.1. 基于I/O复用模型
3.2. 基于线程池复用线程资源
4. 原理解析
5. Reactor模式中核心组成
5.1. Reactor
5.2. Handlers
6. Reactor分类
6.1. 单Reactor单线程
解析
实例
优缺点
使用场景
6.2. 单Reactor多线程
解析
优缺点
6.3. 主从Reactor多线程
解析
实例
优缺点
7. 知识小结
一、为什么使用Netty
1. Netty编程相比NIO编程的优势
还有使用 NIO 编程工作量大,bug 多,这样就会导致许多问题要自己避免及解决:
- 需要自己构建协议
- 解决 TCP 传输问题,如粘包、半包
- epoll 空轮询导致 CPU 100%
- 对 API 进行增强,使之更易用,如 FastThreadLocal => ThreadLocal,ByteBuf => ByteBuffer
说到bug,例如:臭名昭著的Epoll Bug,它会导致Selector空轮询,最终导致CPU 100%。
直到JDK 1.7版本该问题仍旧存在,没有被根本解决。
除此之外,NIO编程所花费的成本更高,可以通过本人总结的以下几点可以看出来!
1、NIO的类库和API繁杂,使用起来比较麻烦
- 需要熟练掌握Selector、ServerSocketChannel、SocketChannel、ByteBuffer等。
2、需要具备其他的额外技能
- 要熟悉Java多线程编程,因为NIO编程涉及到Reactor模式,你必须对多线程和网络编程非常熟悉,才
能编写出高质量的NIO程序。
3、开发工作量和难度都非常大
- 例如:客户端面临断连重连、网络闪断、半包读写、失败缓存、网络拥塞和异常流的处理等等。
2. Netty 相比其它网络应用框架的优势
这里就拿 Mina 网络应用框架做一个介绍,我们可以通过调查发现Mina 由 apache 维护,将来 3.x 版本可
能会有较大重构,破坏 API 向下兼容性,Netty 的开发迭代更迅速,API 更简洁、文档更优秀
久经考验,16年,Netty 版本
- 2.x 2004
- 3.x 2008
- 4.x 2013
- 5.x 已废弃(没有明显的性能提升,维护成本高)
二、让我们走进Netty
1. 简介
Netty是一个开源的、异步的、基于事件驱动的Java网络应用框架,用于快速开发高性能、可维护、
可扩展的网络服务器和客户端程序。
它为我们提供了一套易于使用的抽象组件,使得开发者可以方便地构建各种不同类型的网络应用。
官网:https://netty.io/
2. 设计目标
Netty设计的目标是提供一个简单、高效、稳定的网络编程框架。
它建立在Java NIO(New I/O)的基础上,并在其之上进行了封装和增强。
相比于传统的Java网络编程,Netty提供了更高级别、更易用的API,同时具备更好的性能和可伸缩性。
3. 主要特点
Netty的主要特点包括:
- 异步和事件驱动:利用了Java NIO提供的非阻塞I/O,通过事件驱动的方式处理网络请求和响
应,实现了高并发和低延迟。
- 高度可定制:提供了丰富的可定制选项和灵活的扩展机制,使得开发者可以根据自己的需求来定
制和优化网络应用。
- 支持多种协议:支持多种常见的网络协议,如TCP、UDP、HTTP等,开发者可以根据需要选择
合适的协议进行开发。
- 安全性和稳定性:提供了一些安全性和容错机制,如SSL/TLS支持和优雅的错误处理,以确保网络应用
在面对复杂环境时能够保持稳定和安全。
由于其强大的功能和出色的性能,在众多互联网公司和开发者中得到了广泛的应用和认可。
无论是构建高性能的服务器程序,还是编写复杂的网络应用,Netty都是一个优秀的选择。
4. Netty的作者
他还是另一个著名网络应用框架 Mina 的重要贡献者
5. Netty 的地位
Netty 在 Java 网络应用框架中的地位就好比:Spring 框架在 JavaEE 开发中的地位
以下的框架都使用了 Netty,因为它们有网络通信需求!
- Cassandra - nosql 数据库
- Spark - 大数据分布式计算框架
- Hadoop - 大数据分布式存储框架
- RocketMQ - ali 开源的消息队列
- ElasticSearch - 搜索引擎
- gRPC - rpc 框架
- Dubbo - rpc 框架
- Spring 5.x - flux api 完全抛弃了 tomcat ,使用 netty 作为服务器端
- Zookeeper - 分布式协调框架
6. Netty 的优势
Netty对JDK自带的NIO的API进行了封装,解决了上述问题。
1、设计优雅
适用于各种传输类型的统一API阻塞和非阻塞Socket;基于灵活且可扩展的事件模型,可以清晰地分离关注
点;高度可定制的线程模型-单线程,一个或多个线程池.
2、使用方便
详细记录的Javadoc,用户指南和示例;
没有其他依赖项,JDK 5(Netty 3.x)或6(Netty 4.x)就足够了。
3、高性能、吞吐量更高
延迟更低;减少资源消耗;最小化不必要的内存复制。
4、安全
完整的SSL/TLS和StartTLS支持。
5、社区活跃、不断更新
社区活跃,版本迭代周期短,发现的Bug可以被及时修复,同时,更多的新功能会被加入
五、Netty版本说明
- netty版本分为netty3.x和netty4.x、netty5.x
- 因为Netty5出现重大bug,已经被官网废弃了,目前推荐使用的是Netty4.x的稳定版本
- 目前在官网可下载的版本netty3.x netty4.0.x 和netty4.1.x
这里,我主要讲解Netty4.1.x版本。
netty下载地址: Service End for Bintray, JCenter, GoCenter, and ChartCenter | JFrog
六、Netty架构设计
1. 线程模型基本介绍
- 不同的线程模式,对程序的性能有很大影响,为了搞清Netty线程模式,我将要来系统的讲解下各个线
程模式,最后看看Netty线程模型有什么优越性.
- 目前存在的线程模型有:传统阻塞、I/O、服务模型、Reactor模式
- 根据Reactor的数量和处理资源池线程的数量不同,有3种典型的实现
-
- 单Reactor单线程;
- 单Reactor多线程;
- 主从Reactor多线程
- Netty线程模式(Netty主要基于主从Reactor多线程模型做了一定的改进,其中主从Reactor多线程模
型有多个Reactor)
2. 传统模型
- 采用阻塞IO模式获取输入的数据
- 每个连接都需要独立的线程完成数据的输入,业务处理,数据返回
- 当并发数很大,就会创建大量的线程,占用很大系统资源
- 连接创建后,如果当前线程暂时没有数据可读,该线程会阻塞在read操作,造成线程资源浪费
3. Reactor模式
针对传统阻塞I/O服务模型的2个缺点,解决方案:
3.1. 基于I/O复用模型
多个连接共用一个阻塞对象,应用程序只需要在一个阻塞对象等待,无需阻塞等待所有连接。
当某个连接有新的数据可以处理时,操作系统通知应用程序,线程从阻塞状态返回,开始进行业务处理
Reactor 对应的叫
- 反应器模式
- 分发者模式(Dispatcher)
- 通知者模式(notifier)
3.2. 基于线程池复用线程资源
不必再为每个连接创建线程,将连接完成后的业务处理任务分配给线程进行处理,一个线程可以处理多个
连接的业务。
4. 原理解析
- Reactor模式,通过一个或多个输入同时传递给服务处理器的模式(基于事件驱动)
- 服务器端程序处理传入的多个请求,并将它们同步分派到相应的处理线程,因此Reactor模式也叫
Dispatcher模式
- Reactor模式使用IO复用监听事件, 收到事件后,分发给某个线程(进程), 这点就是网络服务器高并发
处理关键
5. Reactor模式中核心组成
5.1. Reactor
Reactor在一个单独的线程中运行,负责监听和分发事件,分发给适当的处理程序来对IO事件做出反应。
它就像公司的电话接线员,它接听来自客户的电话并将线路转移到适当的联系人;
5.2. Handlers
处理程序执行I/O事件要完成的实际事件,类似于客户想要与之交谈的公司中的实际官员。
Reactor通过调度适当的处理程序来响应I/O事件,处理程序执行非阻塞操作。
6. Reactor分类
根据Reactor的数量和处理资源池线程的数量不同,有3种典型的实现
- 单Reactor单线程
- 单Reactor多线程
- 主从Reactor多线程
6.1. 单Reactor单线程
解析
- Select是前面I/O复用模型介绍的标准网络编程API,可以实现应用程序通过一个阻塞对象监听多路连
接请求
- Reactor对象通过Select监控客户端请求事件,收到事件后通过Dispatch进行分发
- 如果是建立连接请求事件,则由Acceptor通过Accept处理连接请求,然后创建一个Handler对象处理
连接完成后的后续业务处理
- 如果不是建立连接事件,则Reactor会分发调用连接对应的Handler来响应
- Handler会完成Read→业务处理→Send的完整业务流程
实例
服务器端用一个线程通过多路复用搞定所有的IO操作(包括连接,读、写等),编码简单,清晰明了,
但是如果客户端连接数量较多,将无法支撑,前面的NIO案例就属于这种模型。
优缺点
优点:模型简单,没有多线程、进程通信、竞争的问题,全部都在一个线程中完成
缺点:
- 性能问题:只有一个线程,无法完全发挥多核CPU的性能。Handler在处理某个连接上的业务时,整
个进程无法处理其他连接事件,很容易导致性能瓶颈
- 可靠性问题:线程意外终止,或者进入死循环,会导致整个系统通信模块不可用,不能接收和处理外部
消息,造成节点故障。
使用场景
客户端的数量有限,业务处理非常快速,比如Redis在业务处理的时间复杂度O(1)的情况
6.2. 单Reactor多线程
解析
- Reactor对象通过select监控客户端请求事件,收到事件后,通过dispatch进行分发
- 如果建立连接请求,则右Acceptor通过accept处理连接请求,然后创建一个Handler对象处理完成连接后
的各种事件
- 如果不是连接请求,则由reactor分发调用连接对应的handler来处理
- handler只负责响应事件,不做具体的业务处理,通过read读取数据后,会分发给后面的worker线程池
的某个线程处理业务
- worker线程池会分配独立线程完成真正的业务,并将结果返回给handler
- handler收到响应后,通过send将结果返回给client
优缺点
优点:可以充分的利用多核cpu的处理能力
缺点:多线程数据共享和访问比较复杂,reactor处理所有的事件的监听和响应,在单线程运行,在高并发
场景容易出现性能瓶颈。
6.3. 主从Reactor多线程
针对单Reactor多线程模型中,Reactor在单线程中运行,高并发场景下容易成为性能瓶颈,可以让
Reactor在多线程中运行。
解析
- Reactor主线程MainReactor对象通过select监听连接事件,收到事件后,通过Acceptor处理连接事件
- 当Acceptor处理连接事件后,MainReactor将连接分配给SubReactor
- subreactor将连接加入到连接队列进行监听,并创建handler进行各种事件处理
- 当有新事件发生时,subreactor就会调用对应的handler处理
- handler通过read读取数据,分发给后面的worker线程处理
- worker线程池分配独立的worker线程进行业务处理,并返回结果
- handler收到响应的结果后,再通过send将结果返回给client
- Reactor主线程可以对应多个Reactor子线程,即MainRecator可以关联多个SubReactor。
实例
这种模型在许多项目中广泛使用,包括Nginx主从Reactor多进程模型,Memcached主从多线程,Netty
主从多线程模型的支持。
优缺点
优点:
- 父线程与子线程的数据交互简单职责明确,父线程只需要接收新连接,子线程完成后续的业务处理。
- 父线程与子线程的数据交互简单,Reactor主线程只需要把新连接传给子线程,子线程无需返回数据。
缺点:编程复杂度较高
7. 知识小结
现在知道Netty对主从Reactor多线程模型做改进的好处了吧
- 响应快,不必为单个同步时间所阻塞,虽然Reactor本身依然是同步的
- 可以最大程度的避免复杂的多线程及同步问题,并且避免了多线程/进程的切换开销
- 扩展性好,可以方便的通过增加Reactor实例个数来充分利用CPU资源
- 复用性好,Reactor模型本身与具体事件处理逻辑无关,具有很高的复用性