1、什么是Netty
Netty是由JBOSS提供的一个java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。
也就是说,Netty 是一个基于NIO的客户,服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户,服务端应用。Netty相当简化和流线化了网络应用的编程开发过程,例如,TCP和UDP的socket服务开发。
2、什么是NIO
Java NIO(New IO)是从Java 1.4版本开始引入的一个新的IO API,可以替代标准的Java IO API。也有人将NIO 称之为 Non-block I/O (非阻塞I/O),貌似这个称呼更能体现NIO与传统IO的区别。
Java NIO提供了与标准IO不同的IO工作方式:
① Channels and Buffers(通道和缓冲区):标准的IO基于字节流和字符流进行操作的,而NIO是基于通道(Channel)和缓冲区(Buffer)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。
② Asynchronous IO(异步IO):Java NIO可以让你异步的使用IO,例如:当线程从通道读取数据到缓冲区时,线程还是可以进行其他事情。当数据被写入到缓冲区时,线程可以继续处理它。从缓冲区写入通道也类似。
③Selectors(选择器):Java NIO引入了选择器的概念,选择器用于监听多个通道的事件(比如:连接打开,数据到达)。因此,单个的线程可以监听多个数据通道。
详细请参考《Java NIO 系列教程》
3、Netty给我们解决了什么问题
功能强大,简化了NIO开发,Netty 是一个异步的,事件驱动的网络编程框架,使用它能够快速开发出高性能的、可维护性较强的、可扩展性较强的协议服务的服务端和客户端。
4、示例 标准IO
服务端
TimeServer:
package com.techstar.io;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
* Server
* @author mrh
*
*/
public class TimeServer {
/**
* 启动服务
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
int port = 8081;
ServerSocket server = null;
try {
server = new ServerSocket(port);
System.out.println("TimeServer is started .......");
Socket socket = null;
while(true) {
socket = server.accept();
new Thread(new TimeServerHandler(socket)).start();
}
} finally {
if (server != null) {
System.out.println("The TimeServer is closed .......");
server.close();
}
}
}
}
package com.techstar.io;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Date;
public class TimeServerHandler implements Runnable{
private Socket socket;
public TimeServerHandler(Socket socket) {
super();
this.socket = socket;
}
@Override
public void run() {
BufferedReader reader = null;
PrintWriter writer = null;
try {
reader = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
writer = new PrintWriter(this.socket.getOutputStream(), true);
String currentTime = null;
String body = null;
while(true) {
body = reader.readLine();
if (body == null)
break;
System.out.println("The time server receive order:" + body);
currentTime = "QUERY TIME ORDER".equals(body)?new Date(System.currentTimeMillis()).toString() : "BAD ORDER";
writer.println(currentTime);
writer.flush();
}
} catch (Exception e) {
if (writer != null) {
writer.close();
writer = null;
}
if (reader != null) {
try {
reader.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
if (this.socket != null) {
try {
this.socket.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
this.socket = null;
}
}
}
}
客户端:
package com.techstar.io;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
public class TimeClient {
public static void main(String[] args) {
int port = 8081;
BufferedReader reader = null;
PrintWriter writer = null;
Socket socket = null;
try {
socket = new Socket("127.0.0.1", port);
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
writer = new PrintWriter(socket.getOutputStream(), true);
writer.println("QUERY TIME ORDER");
writer.flush();
System.out.println("send order to server successed!");
String response = reader.readLine();
System.out.println("Now is " + response);
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (writer != null) {
writer.close();
writer = null;
}
if (reader != null) {
try {
reader.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
if (socket != null) {
try {
socket.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
socket = null;
}
}
}
}
5、 Linux网络 I/O 模型简介
① 阻塞 I/O 模型
最常用的 I/O 模型就是阻塞 I/O 模型, 缺省情形下, 所有文件操作都是阻塞的。如下图所示
优劣:简单,效率低
② 非阻塞 I/O 模型
理解了阻塞I/O,非阻塞I/O就好理解。非阻塞I/O是程序执行过程中,I/O操作不会阻塞程序的执行,也就是在I/O操作的同时,继续执行其他代码(这得益于Node的事件循环机制)。在I/O设备效率还远远低于CPU效率的时代,这种I/O模型(非阻塞I/O)为程序带来的性能上的提高是非常可观的
优劣:效率高,复杂
③ I/O 复用模型
让应用程序可以同时对多个I/O端口进行监控以判断其上的操作是否可以进行,达到时间复用的目的。在书上看到一个例子来解释I/O的原理,我觉得很形象,如果用监控来自10根不同地方的水管(I/O端口)是否有水流到达(即是否可读),那么需要10个人(即10个线程或10处代码)来做这件事。如果利用某种技术(比如摄像头)把这10根水管的状态情况统一传达到某一点,那么就只需要1个人在那个点进行监控就行了,而类似与select或epoll这样的多路I/O复用机制就好比是摄像头的功能,它们能够把多个I/O端口的状况反馈到同一处,比如某个特定的文件描述符上,这样应用程序只需利用对应的select()或epoll_wait()系统调用阻塞关注这一处即可。
优劣:由于I/O多路复用是在单一进程的上下文中的,因此每个逻辑流程都能访问该进程的全部地址空间,所以开销比多进程低得多;缺点是编程复杂度高
④ 信号驱动 I/O 模型
让内核在描述字就绪时发送SIGIO信号通知我们。首先开启套接口的信号驱动1/O功能,sigaction系统调用安装一个信号处理函数,当内核数据包准备好时,会为该进程产生一个SIGIO信号。
⑤ 异步 I/O告知内核启动某个操作,并让内核在整个操作完成后(包括将数据从内核复制到用户自己的缓冲区)通知我们。与信号模型的区别就在于:信号驱动IO由内核通知我们何时可以开始下一个IO操作;异步I/O模型由内核通知我们I/O操作何时完成。
6、NIO的主要组件
① Buffer
缓冲区, 几乎每种Java数据类型都有一个
② Channel
数据就是一个通道, 网络数据通过Channel读取写入。与传统的IO不同, 它是双向的,允许同时进行读写操作。
③ Selector
NIO的基础,掌握Selector的熟练度,就决定了对NIO编程的熟练度。
Selector(选择器)能够检测一到多个NIO通道,并能够知晓通道是否为诸如读写事件做好准备的组件。这样,一个单独的线程可以管理多个channel,从而管理多个网络连接。
好处:
仅用单个线程来处理多个Channels的好处是,只需要更少的线程来处理通道