Java Netty
一、简介
优点:提供异步的、事件驱动的网络应用程序框架和工具。
特性:
- 设计:统一的API,适用于不同的协议(阻塞/非阻塞),基于灵活、可扩展的事件驱动模型、高度可定制的线程模型、可靠的无连接数据socket支持(UDP)
- 性能:更好的吞吐量、低延迟,更省资源、尽量减少不必要的内存拷贝。
- 安全性:完整的SSL/TLS和STARTTLS的支持,能在Applet和Android的限制环境运行良好。
- 健壮性:不再因为过快、过慢或超负载连接导致OutOfMemoryError,不再有在高速网络环境下NIO读写频率不一致的问题。
- 易用性
应用范围:
- 互联网行业:
随着网站规模的不断扩大,系统并发访问量也越来越高,传统基于tomcat等web容器的垂直架构已经无法满足需求,需要拆分应用进行服务化,以提高开发和维护效率。从组网情况来看~垂直架构拆分之后,系统采用分布式部署,每个节点之间需要远程服务调用(RPC),需要高性能的RPC框架,Netty作为异步高性能的通信框架,可以作为RPC框架的底层使用
典型的应用如:Dubbo、RocketMQ - 游戏行业:
Netty作为高性能的基础通信组件,它本身提供了TCP/UDP和HTTP协议栈,非常方便定制和开发私有协议栈。账号登录服务器、地图服务器之间可以方便的通过Netty进行高性能通信。 - 大数据领域:
经典的Hadoop读高性能通信和序列化组件Avro的RPC框架,默认采用Netty进行跨节点通信。 - 企业软件、通信软件
能做什么:
有了Netty你可以:实现自己的HTTP服务器,FTP服务器,UDP服务器,RPC服务器、redis的proxy服务器,mysql的proxy服务器等大多数服务器的高性能原理实现都是类似的。
HTTP服务器原理:
- HTTP服务器(编码解码用HTTP协议)原理
- 创建一个serverSocket,监听并绑定一个端口
- 一系列客户端来请求这个端口
- 服务器使用Accept,获得一个来自客户端的socket连接对象
- 启动一个新线程处理连接
1. 读socket,得到字节流
2. 处理Http请求,得到一个结果,封装成一个HttpResponse对象
3. 编码协议,将结果序列化字节流
4. 写socket,将字节流发给客户端 - 继续循环步骤c
二、Linux IO模型
2.1 socket
套接字,计算机网络中进程间双向通信的端点抽象。
一个socket代表网络通信的一端,是由操作系统提供的进程间通信机制。
-
IO模型
-
阻塞IO
-
非阻塞IO
-
多路复用
-
信号驱动
-
异步IO
图1 java IO的内部过程
- java本身不具备IO读写能力,在调用read方法之后,从用户态–>内核态,调用操作系统的读方法,将数据读入操作系统内核缓冲区。这期间用户线程阻塞,操作系统调用DMA(Direct Memory Access)来实现文件读,期间也不会使用CPU。
- 从内核态切回用户态,将数据从内核缓冲区读入用户缓冲区(byte数组),这期间CPU会参与拷贝,无法利用DMA。
- 调用write方法,这时将数据从用户缓冲区写入socket缓冲区,cpu会参与拷贝。
- 接下来要向网卡写入数据,这项能力java又不具备,因此又得从用户态切换回内核态,使用DMA将socket缓冲区数据写入网卡。
可以看到java的IO实际不是物理设备级别的读写,而是缓存的复制,底层的真正读写是操作系统完成的,以上过程经历了3次用户态和内核态的切换,4次数据拷贝。
使用NIO优化
通过DirectByteBuf
- ByteBuffer.allocate(10) 返回HeapByteBuffer (使用的还是JAVA内存)
- ByteBuffer.allocateDirect(10) DirectByteBuffer 使用的是操作系统的内存。
在内核缓冲区和用户缓冲区之间的数据复制做了优化。
零拷贝:零表示次数为0,拷贝表示把数据从一个存储区转移到另一个存储区
合起来就是,不需要将数据从一个存储区复制到另一个存储区。
传统的IO执行过程:
read:将数据从磁盘读取到内核缓存区中,再拷贝到用户缓冲区。
write:先将数据写入到socket缓冲区中,最后写入网卡设备。
- 应用程序调用read函数,向操作系统发起IO调用。
- DMA控制器把数据从磁盘中读取到内核缓冲区
- CPU把内核缓冲区数据拷贝到用户应用缓冲区。
- 用户应用进程通过write函数,发起IO调用。
- CPU将缓冲区的数据拷贝到socket缓冲区。
- DMA控制器将数据从socket缓冲区拷贝到网卡设备。
内核空间:主要提供进程调度,内存分配,连接硬件资源等功能。
用户空间:提供给各个程序进程的空间,它不具有访问内核空间资源的权限,如果应用程序需要使用到内核空间的资源,则需要通过系统调用来完成。
上下文(CPU寄存器&程序计数器):CPU寄存器,是CPU内置的容量小,但速度极快的内存;而程序计数器,则是用来存储CPU正在执行的指令位置、或者即将执行的下一条指令位置。它们都是CPU在运行任何任务前,必须的依赖环境,因此叫做CPU上下文。
上下文切换:它是指,先把前一个任务的CPU上下文(也就是CPU寄存器和程序计数器)保存起来,然后加载新任务的上下文到这些寄存器和程序计数器,最后再跳转到程序计数器所指的新位置,运行新任务。