Netty入门学习和技术实践
- Netty
- 1.Netty简介
- 2.IO模型
- 3.Netty框架介绍
- 4. Netty实战项目学习
- 5. Netty实际应用场景
- 6.扩展
Netty
1.Netty简介
Netty是由JBOSS提供的一个java开源框架,现为 Github上的独立项目。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。
也就是说,Netty 是一个基于NIO的客户、服务器端的编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户、服务端应用。Netty相当于简化和流线化了网络应用的编程开发过程,例如:基于TCP和UDP的socket服务开发。
“快速”和“简单”并不用产生维护性或性能上的问题。Netty 是一个吸收了多种协议(包括FTP、SMTP、HTTP等各种二进制文本协议)的实现经验,并经过相当精心设计的项目。最终,Netty 成功的找到了一种方式,在保证易于开发的同时还保证了其应用的性能,稳定性和伸缩性。
git地址链接:
来源:百度百科
2.IO模型
Unix中定义了五种I/O模型:
阻塞I/O
非阻塞I/O
I/O复用(select、poll、linux 2.6种改进的epoll)
信号驱动IO(SIGIO)
异步I/O(POSIX的aio_系列函数)
Java共支持3种网络编程模型/IO模式:BIO、NIO、AIO
BIO:
同步阻塞模式,进行IO操作时,程序会处于阻塞状态,直到IO操作完成。这意味着当程序进行网络操作或者文件操作时,如果操作耗时很长,程序会一直等待,无法进行其他任务。虽然BIO的编程模型比较简单,但是它的并发处理能力相对较弱。因为当有多个IO请求时,线程会被阻塞,CPU无法充分利用。对于高并发场景下的服务器应用来说,BIO的性能表现并不理想。
BIO原理图:
AIO:
Java 7 中引入了 NIO 的改进版 NIO 2,它是异步 IO 模型,异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。
AIO原理图:
NIO:
Java 7 中引入了 NIO 的改进版 NIO 2,它是异步 IO 模型,异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。
非阻塞IO虽然相对于阻塞IO大幅提升了性能,其依然存在性能问题,频繁的轮询导致频繁的系统调用,会耗费大量的CPU资源。当并发很高时,假设有1000个并发,那么单位时间循环内将会有1000次系统调用去轮询执行结果,而实际上可能只有2个请求结果执行完毕,这就会有998次无效的系统调用,造成严重的性能浪费。NIO问题的本质就是频繁轮询导致的无效系统调用
NIO原理图:
IO多路复用
NIO的升级解决方案,减少系统无效的调用
线程首先发起 select调用,询问内核数据是否准备就绪,等内核把数据准备好了,用户线程再发起read调用。read调用的过程(数据从内核空间->用户空间)还是阻塞的。
目前支持 IO 多路复用的系统调用,有 select,epoll 等。select 系统调用,是目前几乎在所有的操作系统上都有支持
select调用,内核提供的系统调用,它支持一次查询多个系统调用的可用状态。几乎所有的操作系统都支持。
epoll调用:linux 2.6 内核,属于 select调用的增强版本,优化了 IO 的执行效率。
Netty 的非阻塞 I/O 的实现关键是基于 I/O 复用模型
IO多路复用原理图
总结:
为什么会分为这五种情况,是因为应用程序和操作系统分为用户态和内核态,应用程序对操作系统的内核发起IO调用(系统调用),操作系统负责的内核执行具体的IO操作。也就是说,我们的应用程序实际上只是发起了IO操作的调用而已,具体 IO 的执行是由操作系统的内核来完成的。
应用程序发起的一次IO操作实际包含两个阶段:
IO调用阶段:应用程序进程向内核发起系统调用
IO执行阶段:内核执行IO操作并返回
2.1. 准备数据阶段:内核等待I/O设备准备好数据
2.2. 拷贝数据阶段:将数据从内核缓冲区拷贝到用户空间缓冲区
增进理解:
- 同步阻塞:你到饭馆点餐,然后在那等着,还要一边喊:好了没啊!
- 同步非阻塞:在饭馆点完餐,就去遛狗了。不过溜一会儿,就回饭馆喊一声:好了没啊!
- 异步阻塞:遛狗的时候,接到饭馆电话,说饭做好了,让您亲自去拿。
- 异步非阻塞:饭馆打电话说,我们知道您的位置,一会给你送过来,安心遛狗就可以了。
3.Netty框架介绍
Netty 的工作架构图
Server端包含 1 个 Boss NioEventLoopGroup 和 1 个 Worker NioEventLoopGroup
Boss 专门负责接收客户端的连接, Worker专门负责网络的读写;
NioEventLoopGroup:相当于 1 个事件循环组,这个组里包含多个事件循环 NioEventLoop,每个 NioEventLoop 包含 1 个 Selector 和 1 个事件循环线程。
Boss NioEventLoop 循环执行的任务包含 3 步:
1.轮询 Accept 事件。
2.处理 Accept I/O 事件,与 Client 建立连接,生成 NioSocketChannel,并将 NioSocketChannel 注册到某个 Worker NioEventLoop 的 Selector 上。
3.处理任务队列中的任务,runAllTasks。任务队列中的任务包括用户调用 eventloop.execute 或 schedule 执行的任务,或者其他线程提交到该 eventloop 的任务。
Worker NioEventLoop 循环执行的任务包含 3 步:
1.轮询 Read、Write 事件。
2.处理 I/O 事件,即 Read、Write 事件,在 NioSocketChannel 可读、可写事件发生时进行处理。
3.处理任务队列中的任务,runAllTasks。
Selector 选择器原理
netty几大核心概念:
拆包/粘包:
应用A 通过网络发送数据向应用B 发送消息,大概会经过如下阶段:
阶段一:应用A 把流数据发送到 TCP发送缓冲区。
阶段二:TCP发送缓冲区把数据发送到达 B服务器 TCP接收缓冲区。
阶段三:应用B 从 TCP接收缓冲区读取流数据。
假设客户端向服务端连续发送了两个数据包,分别用ABC和DEF来表示,那么服务端收到的数据可以分为以下三种情况:
情况1:接收端正常收到两个数据包,即没有发生拆包和粘包的现象。
情况2:接收端只收到一个数据包,这一个数据包中包含了发送端发送的两个数据包的信息,这种现象即为粘包。
情况3:接收端收到了两个数据包,但是这两个数据包要么是不完整的,要么就是多出来一块,这种情况即发生了拆包和粘包。
TCP发送数据原由:
1.因为TCP本身传输的数据包大小就有限制,所以应用发出的消息包过大,TCP会把应用消息包拆分为多个TCP数据包发送出去。
2.Negal算法的优化,当应用发送数据包太小,TCP为了减少网络请求次数的开销,它会等待多个消息包一起,打成一个TCP数据包一次发送出去。
TCP接收方的原由:
1.TCP缓冲区里的数据都是字符流的形式,没有明确的边界,因为数据没边界,所以应用从TCP缓冲区中读取数据时就没办法指定一个或几个消息一起读,而只能选择一次读取多大的数据流,而这个数据流中就可能包含着某个消息包的一部分数据
1)粘包原因:
发送的数据大小小于发送缓冲区,tcp就会把发送的数据多次写入缓冲区,此时发生粘包;
接收数据方的应用层没有及时从 接收缓冲区读取数据,也会发生粘包;
2)拆包原因:
发送的数据大小 大于 tcp发送缓冲区,就会发生拆包;
发送的数据大小 大于 报文最大长度,也会拆包;
原因总结:
1.一个TCP报文最大能传输65536个字节,也就是16Kb。
2.TCP是流式协议,数据无边界。
解决粘包拆包的关键在于为每一个数据包添加界限标识,常用方法如下:
方法1)发送方以固定长度封装数据包。如果不足,则补0填充。
方法2)自定义设置数据包的界限标识,如添加特别标识(如======)。接收方通过标识可以识别不同的数据包;
方法3)发送方为每一个数据包添加报文头部。头部至少包含数据包长度(类似http协议的头部length)。 通过这种方式,接收方通过读取头部的长度知道当前数据包的界限,并在界限处停止读取。
netty处理 拆包/粘包 的方式:
零拷贝:
零拷贝概念:在操作数据时, 不需要将数据buffer从一个内存区域拷贝到另一个内存区域。 少了一次内存的拷贝,CPU的效率就得到的提升。在系统层面上的零拷贝通常指避免在用户态(User-space)与内核态(Kernel-space)之间来回拷贝数据。Netty的Zero-copy完全是在用户态(Java 层面)的, 更多的偏向于优化数据操作。
netty实现零拷贝原理:
1.Netty的接收和发送ByteBuffer采用DIRECT BUFFERS,使用堆外直接内存进行Socket读写,不需要进行字节缓冲区的二次拷贝。如果使用传统的堆内存(HEAP BUFFERS)进行Socket读写,JVM会将堆内存Buffer拷贝一份到直接内存中,然后才写入Socket中。相比于堆外直接内存,消息在发送过程中多了一次缓冲区的内存拷贝。
2.Netty提供了组合Buffer对象,可以聚合多个ByteBuffer对象,用户可以像操作一个Buffer那样方便的对组合Buffer进行操作,避免了传统通过内存拷贝的方式将几个小Buffer合并成一个大的Buffer。
3.Netty的文件传输采用了transferTo方法,它可以直接将文件缓冲区的数据发送到目标Channel,避免了传统通过循环write方式导致的内存拷贝问题。
4. Netty实战项目学习
项目学习地址:
Netty教程:十二个实例带你轻松学习Netty
项目代码gitee地址:
nettyTeach
5. Netty实际应用场景
netty可以做什么:
有了Netty,你可以实现自己的HTTP服务器,FTP服务器,UDP服务器,RPC服务器,WebSocket服务器,Redis的Proxy服务器,MySQL的Proxy服务器等等。
传统的HTTP服务器的原理
1、创建一个ServerSocket,监听并绑定一个端口
2、一系列客户端来请求这个端口
3、服务器使用Accept,获得一个来自客户端的Socket连接对象
4、启动一个新线程处理连接
4.1、读Socket,得到字节流
4.2、解码协议,得到Http请求对象
4.3、处理Http请求,得到一个结果,封装成一个HttpResponse对象
4.4、编码协议,将结果序列化字节流 写Socket,将字节流发给客户端
5、继续循环步骤3
HTTP服务器之所以称为HTTP服务器,是因为编码解码协议是HTTP协议,如果协议是Redis协议,那它就成了Redis服务器,如果协议是WebSocket,那它就成了WebSocket服务器,等等。 使用Netty可以定制编解码协议,实现特定协议的服务器。
netty适用行业:
2.1 互联网行业
1、互联网行业:在分布式系统中,各个节点之间需要远程调用,高性能的 RPC 框架必不可少,Netty 作为异步高性能的通行框架,往往作为基础通信组件被这些 RPC 框架使用。
2、典型的应用有:阿里分布式服务框架 Dubbo 的 RPC 框架使用 Dubbo 协议进行节点间通信,Dubbo 协议默认使用 Netty 作为基础通信组件,用于实现各进程节点之间的内部通信。
2.2 游戏行业
1、无论是手游服务端还是大型的网络游戏,Java 语言得到了越来越广泛的应用。
2、Netty 作为高性能的基础通信组件,提供了 TCP/UDP 和 HTTP 协议栈,方便定制和开发私有协议栈,账号登录服务器。
3、地图服务器之间可以方便的通过 Netty 进行高性能的通信。
2.3 大数据领域
2.2 游戏行业
1、经典的 Hadoop 的高性能通信 和 序列化组件(AVRO 实现数据文件共享)的 RPC 框架,默认采 Netty 进行跨界点通信。
2、它的 Netty Service 基于 Netty 框架二次封装实现。
2.4 其它开源项目使用的Netty
网址:https://netty.io/wiki/related-projects.html
6.扩展
netty相关书籍:
Netty入门与实战:仿写微信 IM 即时通讯系统
Netty官网
Netty 4.x学习笔记 - 线程模型
Netty入门与实战
理解高性能网络模型
Netty基本原理介绍
software-architecture-patterns.pdf
Netty高性能之道 —— 李林锋
Netty In Action
Netty权威指南
netty官网:netty.io
netty优秀开源项目:
gitee:
mqtt-cluster:https://gitee.com/quickmsg/mqtt-cluster.git
netty-mqtt:https://gitee.com/lxrv587/iot_push.git
heart-netty:https://gitee.com/zjz0812/heart-netty.git
netty_redis_zookeeper:https://gitee.com/crazymaker/netty_redis_zookeeper_source_code.git
github: