Java网络编程:IO,NIO与Netty
新公司新项目,项目需要在硬件和软件平台进行信息传递,具体来说使用Netty。硬件和软件使用socket连接,硬件作为客户端,软件平台作为服务的,使用TCP/IP通信。接着这个机会把Java IO和Netty重新学习了一下,总结在这里,不准确或错误的地方,欢迎指出来。
Java I/O相关概念
对于Java IO,可以分为同步与异步,也可以分为阻塞与非阻塞,下面对这些概念进行解释:
同步与异步
- 同步与异步,描述的是用户线程与系统内核的交互方式。
- 同步:用户线程发起I/O请求后,需要等待或者轮询内核I/O操作完成后,才能继续执行
- 异步:用户线程发起I/O请求后,仍立即继续执行下去,当内核I/O操作完成后会通知用户线程,或者调用用户线程注册的回调函数。
阻塞与非阻塞
- 阻塞与非阻塞,描述的是用户线程调用内核I/O操作的方式。
- 阻塞:I/O操作彻底完成后,才能返回到用户空间。
- 非阻塞:I/O操作被调用后,立刻返回给用户线程一个状态值,无须等待I/O操作完成
- 一个IO操作分为两部分,发起I/O请求和实际I/O操作。
- 阻塞I/O和非阻塞I/O的区别在于第一步,发起I/O请求是否会被阻塞,如果阻塞直到I/O操作完成,那就是传统的阻塞I/O,否则就是非阻塞I/O
- 同步I/O和异步I/O在于第二步,如果实际I/O操作阻塞请求进程,那就是同步I/O,否则就是异步I/O。
OIO
- 早期Java提供了java.net包用于开发网络应用,这类API被称为Java OIO(Old-blocking IO),也称阻塞IO,Java IO的第一代,就是普通的IO,也称作BIO。
- 在
java.io 包下面,在我们初次学习Java语言时,也一定会去看或者使用这个包下面的类. - 它是线程同步阻塞的,每个线程只能绑定处理一个socket。当线程发起IO请求,不管内核是否准备好IO操作,从发起请求起,线程一直阻塞,直到操作完成。
- 标准IO流程是基于字节流或字符流进行操作的
NIO
- NIO,Java的New IO,非阻塞IO,也可以称为 Non-blocking IO,在java.nio包下,从Java 1.4引入。
- NIO是基于通道(Channel)和缓冲区(Buffer)进行操作的,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。
- 同步非阻塞通信,每个线程可以处理多个channel(异步)。当线程发起IO请求后,立即返回;内核在做好IO操作的准备之后,通过调用注册的回调函数通知线程做IO操作,线程开始阻塞,直到操作完成。
- 其实是逐个channel处理的,发起IO请求后立刻返回并进入排队,等待IO准备好再去调用,就是非阻塞伪异步。
- 选择器(Selector):Java NIO引入了选择器的概念,选择器用于监听多个通道的事件(例如连接打开、数据到达、数据读取)。因此单个线程可以监听多个数据通道,大大提升了单机的并发能力。
AIO
- AIO(异步I/O),彻底的异步IO通信,NIO之后的版本,也称为“NIO.2”,在Java 1.7版本引入,使用方法与最早的标准I/O有很大的差异。
- Java AIO采取“订阅-通知”的模式。应用程序向操作系统注册I/O监听,然后做自己的事情。
- 当内核做好IO操作的准备之后,做IO操作,直到操作完成或者失败之后,再通过调用注册的回调函数通知应用程序线程
- 在Windows系统,操作系统提供了IOCP(I/O CompletionPort,I/O完成端口);在Linux系统,使用epoll对异步I/O进行模拟
reactor 模型
reactor 模型:非阻塞同步网络模型,NIO使用的模型,事件分离器负责等待文件描述符或socket为读写操作准备就绪,然后将就绪事件传递给对应的处理器,最后由处理器负责完成实际的读写工作。
proactor 模型
proactor 模型:异步网络模型,AIO使用的模型。与reactor模型不同,proactor模型是起一个worker线程执行IO操作,成功后返回主线程。
Java IO应用场景
Java对IO的支持经历了这几个阶段:
- 阻塞IO:Java最早的IO,同步阻塞IO,一个线程一个连接。适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中。
- NIO:在JDK1.4中引入。NIO本身是基于事件驱动思想来完成的,其主要想解决的是BIO的大并发问题。NIO基于Reactor,当socket有流可读或可写入socket时,操作系统会相应的通知引用程序进行处理,应用再将流读取到缓冲区或写入操作系统。适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂。
- AIO:在JDK1.7中引入,这部分内容被称作NIO.2,主要在java.nio.channels包下增加了四个异步通道:
AsynchronousSocketChannel 、AsynchronousServerSocketChannel 、AsynchronousFileChannel 、AsynchronousDatagramChannel 。适用于连接数目多且连接比较长(重操作)的架构,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。
Netty简介
- Java的IO接口功能基本完备,但是使用起来很繁琐。为了简化Java IO编程,Netty出现了。
- Netty是第三方的Java IO框架,是一个提供异步事件驱动(Asynchronous Event-Driven)的网络应用框架。
- Netty集成以上的几种IO接口,包括阻塞IO与NIO都支持,但主要是基于Java NIO API实现的。Netty内部封装了 Java NIO API的复杂性,并提供了线程池处理,使得开发NIO的应用变得简单。
- Netty支持丰富的网络协议,TCP、UDP、HTTP、HTTP/2、WebSocket、SSL/TLS等
- Netty对部分功能进行了重写和优化,在更加易用的同时,性能也得到了巨大的提升。
- Netty有一个简单却不失强大的架构,主要由三部分组成:缓冲(ByteBuf),通道(Channel),事件模型(event model)。
NIO与Netty
- 作为语言基础类库,Java 自身的 NIO 设计更偏底层,这本无可厚非,但是对于一线的应用开发者,其复杂性、扩展性等方面,就存在一定的局限了
- 在基础 NIO 之上,Netty 构建了更加易用、高性能的网络框架,广泛应用于互联网、游戏、电信等各种领域。
- 单独从性能角度,Netty 在基础的 NIO 等类库之上进行了很多改进,例如:更加优雅的 Reactor模式实现、灵活的线程模型、利用 EventLoop 等创新性的机制,可以非常高效地管理成百上千的 Channel。
- 充分利用了 Java 的 Zero-Copy 机制,并且从多种角度,“斤斤计较”般的降低内存分配和回收的开销。例如,使用池化的 Direct Buffer 等技术,在提高 IO 性能的同时,减少了对象的创建和销毁;利用反射等技术直接操纵 SelectionKey,使用数组而不是 Java 容器等。
- 使用更多本地代码。例如,直接利用 JNI 调用 Open SSL 等方式,获得比 Java 内建 SSL 引擎更好的性能。
- 在通信协议、序列化等其他角度的优化。
- 总的来说,Netty 并没有 Java 核心类库那些强烈的通用性、跨平台等各种负担,针对性能等特定目标以及 Linux 等特定环境,采取了一些极致的优化手段。
生产使用
- 硬件设备作为客户端
- 软件平台作为服务端
- 使用TCP/IP通信传输数据
- 数据格式使用事先约定好的数据包格式,包括报文头、消息数据体,每个字段都要规定所占字节长度
- 使用Netty实现网络通信,使用
LengthFieldBasedFrameDecoder 基于长度字段的帧解码器 - 硬件通过网络发生数据包,软件平台接收socket消息,对数据包解码处理,得到对应数据
|